networkwm-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +339 -0
- package/README.MD +110 -0
- package/dist/bytemanip.d.ts +12 -0
- package/dist/bytemanip.d.ts.map +1 -0
- package/dist/bytemanip.js +71 -0
- package/dist/database-abstraction.d.ts +39 -0
- package/dist/database-abstraction.d.ts.map +1 -0
- package/dist/database-abstraction.js +288 -0
- package/dist/databases.d.ts +85 -0
- package/dist/databases.d.ts.map +1 -0
- package/dist/databases.js +303 -0
- package/dist/devices.d.ts +6 -0
- package/dist/devices.d.ts.map +1 -0
- package/dist/devices.js +9 -0
- package/dist/encryption.d.ts +16 -0
- package/dist/encryption.d.ts.map +1 -0
- package/dist/encryption.js +123 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/filesystem/index.d.ts +2 -0
- package/dist/filesystem/index.d.ts.map +1 -0
- package/dist/filesystem/index.js +17 -0
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts +44 -0
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts.map +1 -0
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.js +187 -0
- package/dist/functions.d.ts +12 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +73 -0
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +47 -0
- package/dist/id3.d.ts +18 -0
- package/dist/id3.d.ts.map +1 -0
- package/dist/id3.js +137 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/init-data.d.ts +16 -0
- package/dist/init-data.d.ts.map +1 -0
- package/dist/init-data.js +18 -0
- package/dist/initialization.d.ts +4 -0
- package/dist/initialization.d.ts.map +1 -0
- package/dist/initialization.js +32 -0
- package/dist/sort.d.ts +13 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/sort.js +62 -0
- package/dist/tables.d.ts +19 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +101 -0
- package/dist/tagged-oma.d.ts +12 -0
- package/dist/tagged-oma.d.ts.map +1 -0
- package/dist/tagged-oma.js +175 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +90 -0
- package/package.json +43 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.updateMetadata = exports.createTaggedEncryptedOMA = void 0;
|
|
7
|
+
const crypto_js_wasm_1 = __importDefault(require("@originjs/crypto-js-wasm"));
|
|
8
|
+
const himd_js_1 = require("himd-js");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
const id3_1 = require("./id3");
|
|
11
|
+
const encryption_1 = require("./encryption");
|
|
12
|
+
const textEncoder = new TextEncoder();
|
|
13
|
+
const PHONY_CID = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA9, 0xC1, 0x6A, 0x81, 0x6A, 0x87, 0xDA, 0xAD, 0x4B, 0xA2, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
|
14
|
+
const EKB1001D_CONTENTS = new Uint8Array([0x45, 0x4B, 0x42, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x39, 0x47, 0xF4, 0x0A, 0x33, 0x65, 0x2F, 0x98, 0x71, 0x73, 0xC3, 0x98, 0x68, 0xC5, 0x23, 0x5B, 0x84, 0x20, 0xC8, 0xCF, 0xFB, 0x0E, 0x7F, 0x4E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x34, 0x85, 0x14, 0x51, 0x45, 0x04, 0x02, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x36, 0xD2, 0x00, 0x38, 0x74, 0x91, 0x51, 0x9D, 0xA7, 0x75, 0x94, 0x70, 0xA0, 0x17, 0x69, 0xDA, 0x69, 0x55, 0xAE, 0xA6, 0x9F, 0x6A, 0x3E, 0x69, 0x18, 0xC7, 0xC6, 0xBB, 0xD7, 0xCC, 0xFB, 0x1B, 0x81, 0x8D, 0xA9, 0x97, 0x90, 0x67, 0x29, 0x5C, 0xB7, 0x55, 0x5A, 0xEC, 0x21, 0x1B, 0x9E, 0xBD, 0xD4, 0x7E, 0xD9, 0x09, 0x79, 0xE0, 0x39, 0xA1, 0xE0, 0x76, 0x68, 0x0D, 0xB8, 0xBF, 0xED, 0xB0, 0xD3, 0x24, 0x26, 0xB8, 0xF7, 0x79, 0x22, 0xBD, 0x46, 0xC9, 0x44, 0x9F, 0xDF, 0x01, 0x74, 0xC0, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x28, 0xDB, 0x54, 0x6A, 0xC5, 0xE0, 0x4D, 0x4F, 0xCB, 0xF2, 0x46, 0x3B, 0x01, 0xDE, 0x2C, 0xD0, 0xFB, 0xAF, 0x7A, 0xA7, 0x1E, 0xEF, 0x44, 0x29, 0x05, 0x97, 0x9D, 0xBE, 0xE7, 0x28, 0x4E, 0xA4, 0x53, 0x3A, 0x2F, 0x71, 0xC7, 0xCB, 0x86, 0x58, 0x39]);
|
|
15
|
+
function encodeSonyWeirdString(type, data) {
|
|
16
|
+
return (0, utils_1.concatUint8Arrays)([
|
|
17
|
+
(0, id3_1.encodeUTF16BEStringEA3)(type),
|
|
18
|
+
new Uint8Array([0, 0]),
|
|
19
|
+
(0, id3_1.encodeUTF16BEStringEA3)(data, false),
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
function createSonyGEOB(geobName, header, kvmap) {
|
|
23
|
+
// Header:
|
|
24
|
+
const dataSlices = [
|
|
25
|
+
new Uint8Array([0x02]),
|
|
26
|
+
textEncoder.encode("binary"),
|
|
27
|
+
new Uint8Array([0, 0, 0]),
|
|
28
|
+
(0, id3_1.encodeUTF16BEStringEA3)(geobName, false),
|
|
29
|
+
new Uint8Array([0, 0]),
|
|
30
|
+
header,
|
|
31
|
+
];
|
|
32
|
+
// KVs:
|
|
33
|
+
for (let val of kvmap) {
|
|
34
|
+
const name = textEncoder.encode(val.name.padEnd(12, ' '));
|
|
35
|
+
dataSlices.push(name, new Uint8Array([0, val.chunkLen, 0, val.chunks]), val.contents);
|
|
36
|
+
}
|
|
37
|
+
return (0, utils_1.concatUint8Arrays)(dataSlices);
|
|
38
|
+
}
|
|
39
|
+
function createEncryptionHeader(titleInfo, milliseconds) {
|
|
40
|
+
const verificationKey = (0, utils_1.createRandomBytes)(8);
|
|
41
|
+
const actualTrackKey = (0, utils_1.createRandomBytes)(8);
|
|
42
|
+
// In every KEYRING section, the track key is stored as decrypted by the verification key decrypted by the ekbroot
|
|
43
|
+
const padding = (0, utils_1.createRandomBytes)(8); // What's this???
|
|
44
|
+
const keyringDataA = (0, utils_1.concatUint8Arrays)([
|
|
45
|
+
new Uint8Array([
|
|
46
|
+
0x00, 0x28, 0x00, 0x01,
|
|
47
|
+
0x00, 0x03, 0x00, 0x00,
|
|
48
|
+
0x00, 0x00, 0x00, 0x00,
|
|
49
|
+
0x00, 0x01, 0x00, 0x21,
|
|
50
|
+
]),
|
|
51
|
+
verificationKey, (0, encryption_1.createTrackKeyForKeyring)(0x00010021, verificationKey, actualTrackKey), padding,
|
|
52
|
+
new Uint8Array(8).fill(0),
|
|
53
|
+
]);
|
|
54
|
+
const firstGEOBContents = createSonyGEOB("OMG_ULINF", new Uint8Array(), [
|
|
55
|
+
{ name: 'KEYRING', chunkLen: 0x10, chunks: 0x03, contents: keyringDataA },
|
|
56
|
+
{ name: '!CID', chunkLen: 0x10, chunks: 0x02, contents: PHONY_CID },
|
|
57
|
+
{ name: '!REFID', chunkLen: 0x10, chunks: 0x01, contents: new Uint8Array([0x01, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) },
|
|
58
|
+
{ name: 'PLEASE', chunkLen: 0x10, chunks: 0x02, contents: textEncoder.encode("don't do DRM - it's bad for you.") }
|
|
59
|
+
]);
|
|
60
|
+
const verificationKeyCA = crypto_js_wasm_1.default.lib.CipherParams.create({
|
|
61
|
+
ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(verificationKey),
|
|
62
|
+
});
|
|
63
|
+
const decryptedVerificationKeyWaA = crypto_js_wasm_1.default.TripleDES.decrypt(verificationKeyCA, crypto_js_wasm_1.default.lib.WordArray.create(encryption_1.EKBROOTS[0x00010021]), { mode: crypto_js_wasm_1.default.mode.ECB });
|
|
64
|
+
const maclistVerifiedData = firstGEOBContents.slice(94); // Offset to '!CID'
|
|
65
|
+
const maclistValue = (0, encryption_1.createTrackMac2)(decryptedVerificationKeyWaA, maclistVerifiedData);
|
|
66
|
+
const keyringDataB = (0, utils_1.concatUint8Arrays)([
|
|
67
|
+
new Uint8Array([0x00, 0x28, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1D]),
|
|
68
|
+
verificationKey, (0, encryption_1.createTrackKeyForKeyring)(0x0001001D, verificationKey, actualTrackKey), padding,
|
|
69
|
+
new Uint8Array(8).fill(0),
|
|
70
|
+
EKB1001D_CONTENTS,
|
|
71
|
+
]);
|
|
72
|
+
let secondGEOBContents = createSonyGEOB("OMG_BKLSI", new Uint8Array([0x00, 0x01, 0x00, 0x40, 0x00, 0xDC, 0x00, 0x70, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), [
|
|
73
|
+
{ name: 'KEYRING', chunkLen: 0x10, chunks: 0x03, contents: keyringDataB },
|
|
74
|
+
{ name: '!CID', chunkLen: 0x10, chunks: 0x02, contents: PHONY_CID },
|
|
75
|
+
{ name: 'SHARE_P_SID', chunkLen: 0x10, chunks: 0x01, contents: new Uint8Array([0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xE3, 0x22, 0xB5, 0x46, 0x89, 0xED, 0x10]) },
|
|
76
|
+
{ name: '!REFID', chunkLen: 0x10, chunks: 0x01, contents: new Uint8Array([0x01, 0x01, 0x00, 0x00, 0x03, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) },
|
|
77
|
+
]);
|
|
78
|
+
const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
|
|
79
|
+
ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(verificationKey),
|
|
80
|
+
});
|
|
81
|
+
const decryptedVerificationKeyWa = crypto_js_wasm_1.default.TripleDES.decrypt(verificationKeyC, crypto_js_wasm_1.default.lib.WordArray.create(encryption_1.EKBROOTS[0x0001001D]), { mode: crypto_js_wasm_1.default.mode.ECB });
|
|
82
|
+
const cbcMac = (0, encryption_1.createTrackMac2)(decryptedVerificationKeyWa, secondGEOBContents.subarray(0x14a));
|
|
83
|
+
// Merge the CBC-MAC into the GEOB
|
|
84
|
+
secondGEOBContents = (0, utils_1.concatUint8Arrays)([secondGEOBContents, cbcMac, new Uint8Array(8).fill(0)]);
|
|
85
|
+
const id3Info = {
|
|
86
|
+
flags: 0,
|
|
87
|
+
version: { major: 3, minor: 0 },
|
|
88
|
+
tags: [
|
|
89
|
+
{ id: "GEOB", contents: firstGEOBContents, flags: 0 },
|
|
90
|
+
{ id: "TIT2", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.title), flags: 0 },
|
|
91
|
+
{ id: "TPE1", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.artist), flags: 0 },
|
|
92
|
+
{ id: "TALB", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.album), flags: 0 },
|
|
93
|
+
{ id: "TALB", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.album), flags: 0 },
|
|
94
|
+
{ id: "TCON", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.genre), flags: 0 },
|
|
95
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TPE1S", titleInfo.artist), flags: 0 },
|
|
96
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TRACK", '0'), flags: 0 },
|
|
97
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_ALBMS", titleInfo.album), flags: 0 },
|
|
98
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TIT2S", titleInfo.title), flags: 0 },
|
|
99
|
+
{ id: "TLEN", contents: (0, id3_1.encodeUTF16BEStringEA3)(milliseconds.toString()), flags: 0 },
|
|
100
|
+
{ id: "GEOB", contents: secondGEOBContents, flags: 0 }, // OMG_BKLSI
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
return { contents: (0, id3_1.serialize)(id3Info), trackEncryptionKey: actualTrackKey, maclistValue };
|
|
104
|
+
}
|
|
105
|
+
function createTaggedEncryptedOMA(rawData, titleInfo, codec) {
|
|
106
|
+
const formatHeader = (0, utils_1.createEA3Header)(codec, true);
|
|
107
|
+
const milliseconds = Math.floor(1000 * (0, himd_js_1.getSeconds)(codec, Math.ceil(rawData.length / (0, himd_js_1.getBytesPerFrame)(codec))));
|
|
108
|
+
const { contents: encHeader, trackEncryptionKey, maclistValue } = createEncryptionHeader(titleInfo, milliseconds);
|
|
109
|
+
let iv = (0, utils_1.createRandomBytes)(8);
|
|
110
|
+
formatHeader.set(iv, formatHeader.length - 8);
|
|
111
|
+
// Encrypt the whole file
|
|
112
|
+
const keyWa = crypto_js_wasm_1.default.lib.WordArray.create(trackEncryptionKey);
|
|
113
|
+
const ivWa = crypto_js_wasm_1.default.lib.WordArray.create(iv);
|
|
114
|
+
const blockWa = crypto_js_wasm_1.default.lib.WordArray.create(rawData);
|
|
115
|
+
const allData = crypto_js_wasm_1.default.DES.encrypt(blockWa, keyWa, { mode: crypto_js_wasm_1.default.mode.CBC, iv: ivWa });
|
|
116
|
+
rawData = (0, utils_1.wordArrayToByteArray)(allData.ciphertext, rawData.length);
|
|
117
|
+
return { data: (0, utils_1.concatUint8Arrays)([encHeader, formatHeader, rawData]), maclistValue, duration: milliseconds };
|
|
118
|
+
}
|
|
119
|
+
exports.createTaggedEncryptedOMA = createTaggedEncryptedOMA;
|
|
120
|
+
function findInMetadata(metadata, id, asGeob) {
|
|
121
|
+
if (!asGeob)
|
|
122
|
+
return metadata.tags.find(e => e.id === id);
|
|
123
|
+
return metadata.tags.filter(e => e.id === "GEOB")
|
|
124
|
+
.find(e => {
|
|
125
|
+
if (e.contents[0] !== 0x02)
|
|
126
|
+
return false;
|
|
127
|
+
// This is a valid Sony crypto block
|
|
128
|
+
for (let i = 0; i < id.length * 2; i += 2) {
|
|
129
|
+
if (e.contents[i + 11] !== id.charCodeAt(i / 2))
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
async function updateMetadata(file, titleInfo) {
|
|
136
|
+
// Read the first 10 bytes to get the encryption header's size.
|
|
137
|
+
const ea3Header = await file.read(10);
|
|
138
|
+
if (ea3Header.length !== 10)
|
|
139
|
+
throw new Error("Invalid header");
|
|
140
|
+
const headerSizeRemaining = (0, id3_1.readSynchsafeInt32)(new DataView(ea3Header.buffer), 6)[0];
|
|
141
|
+
const fullEncryptionHeader = (0, utils_1.concatUint8Arrays)([ea3Header, await file.read(headerSizeRemaining)]);
|
|
142
|
+
const subsequentData = await file.read();
|
|
143
|
+
const metadata = (0, id3_1.parse)(fullEncryptionHeader);
|
|
144
|
+
const tlen = findInMetadata(metadata, "TLEN", false);
|
|
145
|
+
const ulinf = findInMetadata(metadata, "OMG_ULINF", true);
|
|
146
|
+
const bklsi = findInMetadata(metadata, "OMG_BKLSI", true);
|
|
147
|
+
if (!ulinf || !bklsi || !tlen)
|
|
148
|
+
throw new Error("Not a valid encrypted OMA");
|
|
149
|
+
const newMetadataBlock = {
|
|
150
|
+
flags: metadata.flags,
|
|
151
|
+
version: metadata.version,
|
|
152
|
+
tags: [
|
|
153
|
+
ulinf,
|
|
154
|
+
{ id: "TIT2", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.title), flags: 0 },
|
|
155
|
+
{ id: "TPE1", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.artist), flags: 0 },
|
|
156
|
+
{ id: "TALB", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.album), flags: 0 },
|
|
157
|
+
{ id: "TALB", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.album), flags: 0 },
|
|
158
|
+
{ id: "TCON", contents: (0, id3_1.encodeUTF16BEStringEA3)(titleInfo.genre), flags: 0 },
|
|
159
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TPE1S", titleInfo.artist), flags: 0 },
|
|
160
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TRACK", '0'), flags: 0 },
|
|
161
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_ALBMS", titleInfo.album), flags: 0 },
|
|
162
|
+
{ id: "TXXX", contents: encodeSonyWeirdString("OMG_TIT2S", titleInfo.title), flags: 0 },
|
|
163
|
+
tlen,
|
|
164
|
+
bklsi,
|
|
165
|
+
]
|
|
166
|
+
};
|
|
167
|
+
const serialized = (0, id3_1.serialize)(newMetadataBlock);
|
|
168
|
+
// Rewrite the file
|
|
169
|
+
const zeroOutDifference = Math.max(0, fullEncryptionHeader.length - serialized.length);
|
|
170
|
+
await file.seek(0);
|
|
171
|
+
await file.write(serialized);
|
|
172
|
+
await file.write(subsequentData);
|
|
173
|
+
await file.write(new Uint8Array(zeroOutDifference).fill(0));
|
|
174
|
+
}
|
|
175
|
+
exports.updateMetadata = updateMetadata;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CodecInfo } from 'himd-js';
|
|
2
|
+
export declare function assert(condition: boolean, message?: string): void;
|
|
3
|
+
export declare function join(...paths: string[]): string;
|
|
4
|
+
export declare function concatUint8Arrays(args: Uint8Array[]): Uint8Array;
|
|
5
|
+
export declare function createEA3Header({ codecId, codecInfo }: CodecInfo, encrypted?: boolean): Uint8Array;
|
|
6
|
+
export declare function wordArrayToByteArray(wordArray: any, length?: number): Uint8Array;
|
|
7
|
+
export declare function createRandomBytes(length?: number): Uint8Array;
|
|
8
|
+
export declare function arrayEq<T>(a: ArrayLike<T>, b: ArrayLike<T>): boolean;
|
|
9
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,QAM1D;AAED,wBAAgB,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,UAEtC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,cAcnD;AAED,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,SAAS,UAAQ,cASnF;AAmBD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,GAAE,MAA2B,cAavF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,SAAI,cAM3C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,WAM1D"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.arrayEq = exports.createRandomBytes = exports.wordArrayToByteArray = exports.createEA3Header = exports.concatUint8Arrays = exports.join = exports.assert = void 0;
|
|
4
|
+
function assert(condition, message) {
|
|
5
|
+
if (condition) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
message = message || 'no message provided';
|
|
9
|
+
throw new Error(`Assertion failed: ${message}`);
|
|
10
|
+
}
|
|
11
|
+
exports.assert = assert;
|
|
12
|
+
function join(...paths) {
|
|
13
|
+
return paths.join("/").replace(/\/*/, '/');
|
|
14
|
+
}
|
|
15
|
+
exports.join = join;
|
|
16
|
+
function concatUint8Arrays(args) {
|
|
17
|
+
let totalLength = 0;
|
|
18
|
+
for (let a of args) {
|
|
19
|
+
totalLength += a.length;
|
|
20
|
+
}
|
|
21
|
+
let res = new Uint8Array(totalLength);
|
|
22
|
+
let offset = 0;
|
|
23
|
+
for (let a of args) {
|
|
24
|
+
res.set(a, offset);
|
|
25
|
+
offset += a.length;
|
|
26
|
+
}
|
|
27
|
+
return res;
|
|
28
|
+
}
|
|
29
|
+
exports.concatUint8Arrays = concatUint8Arrays;
|
|
30
|
+
function createEA3Header({ codecId, codecInfo }, encrypted = false) {
|
|
31
|
+
const headerSize = 96;
|
|
32
|
+
const header = new Uint8Array(headerSize);
|
|
33
|
+
header.set(new Uint8Array([0x45, 0x41, 0x33, 0x01, 0x00, 0x60, encrypted ? 0x00 : 0xff, encrypted ? 0x01 : 0xff, 0x00, 0x00, 0x00, 0x00]));
|
|
34
|
+
header[32] = codecId;
|
|
35
|
+
header[33] = codecInfo[0];
|
|
36
|
+
header[34] = codecInfo[1];
|
|
37
|
+
header[35] = codecInfo[2];
|
|
38
|
+
return header;
|
|
39
|
+
}
|
|
40
|
+
exports.createEA3Header = createEA3Header;
|
|
41
|
+
function wordToByteArray(word, length, littleEndian = false) {
|
|
42
|
+
let ba = [], xFF = 0xff;
|
|
43
|
+
let actualLength = length;
|
|
44
|
+
if (littleEndian) {
|
|
45
|
+
length = 4;
|
|
46
|
+
}
|
|
47
|
+
if (length > 0)
|
|
48
|
+
ba.push(word >>> 24);
|
|
49
|
+
if (length > 1)
|
|
50
|
+
ba.push((word >>> 16) & xFF);
|
|
51
|
+
if (length > 2)
|
|
52
|
+
ba.push((word >>> 8) & xFF);
|
|
53
|
+
if (length > 3)
|
|
54
|
+
ba.push(word & xFF);
|
|
55
|
+
if (littleEndian) {
|
|
56
|
+
ba = ba.splice(4 - actualLength).reverse();
|
|
57
|
+
}
|
|
58
|
+
return ba;
|
|
59
|
+
}
|
|
60
|
+
function wordArrayToByteArray(wordArray, length = wordArray.sigBytes) {
|
|
61
|
+
let res = new Uint8Array(length);
|
|
62
|
+
let bytes;
|
|
63
|
+
let i = 0;
|
|
64
|
+
let offset = 0;
|
|
65
|
+
while (length > 0) {
|
|
66
|
+
bytes = wordToByteArray(wordArray.words[i], Math.min(4, length));
|
|
67
|
+
res.set(bytes, offset);
|
|
68
|
+
length -= bytes.length;
|
|
69
|
+
offset += bytes.length;
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
return res;
|
|
73
|
+
}
|
|
74
|
+
exports.wordArrayToByteArray = wordArrayToByteArray;
|
|
75
|
+
function createRandomBytes(length = 8) {
|
|
76
|
+
return new Uint8Array(Array(length)
|
|
77
|
+
.fill(0)
|
|
78
|
+
.map(() => Math.floor(Math.random() * 256)));
|
|
79
|
+
}
|
|
80
|
+
exports.createRandomBytes = createRandomBytes;
|
|
81
|
+
function arrayEq(a, b) {
|
|
82
|
+
if (a.length !== b.length)
|
|
83
|
+
return false;
|
|
84
|
+
for (let i = 0; i < a.length; i++) {
|
|
85
|
+
if (a[i] !== b[i])
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
exports.arrayEq = arrayEq;
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "networkwm-js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A library which lets you manage tracks and metadata on Sony Network Walkmen.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/asivery/networkwm-js.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"sony",
|
|
15
|
+
"walkman",
|
|
16
|
+
"mp3",
|
|
17
|
+
"atrac"
|
|
18
|
+
],
|
|
19
|
+
"author": "asivery",
|
|
20
|
+
"license": "GPL-2.0",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/asivery/networkwm-js/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/asivery/networkwm-js#readme",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^18.11.9",
|
|
30
|
+
"prettier": "^2.8.8",
|
|
31
|
+
"typescript": "^4.8.4"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@originjs/crypto-js-wasm": "github:asivery/crypto-js-wasm",
|
|
35
|
+
"async-mutex": "^0.4.0",
|
|
36
|
+
"himd-js": "^0.2.0-alpha.5",
|
|
37
|
+
"mp3-parser": "^0.3.0",
|
|
38
|
+
"node-id3": "^0.2.5",
|
|
39
|
+
"node-mass-storage": "^0.2.0",
|
|
40
|
+
"nufatfs": "^0.1.5",
|
|
41
|
+
"usb": "^2.4.1"
|
|
42
|
+
}
|
|
43
|
+
}
|