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.
Files changed (57) hide show
  1. package/LICENSE +339 -0
  2. package/README.MD +110 -0
  3. package/dist/bytemanip.d.ts +12 -0
  4. package/dist/bytemanip.d.ts.map +1 -0
  5. package/dist/bytemanip.js +71 -0
  6. package/dist/database-abstraction.d.ts +39 -0
  7. package/dist/database-abstraction.d.ts.map +1 -0
  8. package/dist/database-abstraction.js +288 -0
  9. package/dist/databases.d.ts +85 -0
  10. package/dist/databases.d.ts.map +1 -0
  11. package/dist/databases.js +303 -0
  12. package/dist/devices.d.ts +6 -0
  13. package/dist/devices.d.ts.map +1 -0
  14. package/dist/devices.js +9 -0
  15. package/dist/encryption.d.ts +16 -0
  16. package/dist/encryption.d.ts.map +1 -0
  17. package/dist/encryption.js +123 -0
  18. package/dist/errors.d.ts +4 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +10 -0
  21. package/dist/filesystem/index.d.ts +2 -0
  22. package/dist/filesystem/index.d.ts.map +1 -0
  23. package/dist/filesystem/index.js +17 -0
  24. package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts +44 -0
  25. package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts.map +1 -0
  26. package/dist/filesystem/usb-mass-storage-webusb-filesystem.js +187 -0
  27. package/dist/functions.d.ts +12 -0
  28. package/dist/functions.d.ts.map +1 -0
  29. package/dist/functions.js +73 -0
  30. package/dist/helpers.d.ts +9 -0
  31. package/dist/helpers.d.ts.map +1 -0
  32. package/dist/helpers.js +47 -0
  33. package/dist/id3.d.ts +18 -0
  34. package/dist/id3.d.ts.map +1 -0
  35. package/dist/id3.js +137 -0
  36. package/dist/index.d.ts +10 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +28 -0
  39. package/dist/init-data.d.ts +16 -0
  40. package/dist/init-data.d.ts.map +1 -0
  41. package/dist/init-data.js +18 -0
  42. package/dist/initialization.d.ts +4 -0
  43. package/dist/initialization.d.ts.map +1 -0
  44. package/dist/initialization.js +32 -0
  45. package/dist/sort.d.ts +13 -0
  46. package/dist/sort.d.ts.map +1 -0
  47. package/dist/sort.js +62 -0
  48. package/dist/tables.d.ts +19 -0
  49. package/dist/tables.d.ts.map +1 -0
  50. package/dist/tables.js +101 -0
  51. package/dist/tagged-oma.d.ts +12 -0
  52. package/dist/tagged-oma.d.ts.map +1 -0
  53. package/dist/tagged-oma.js +175 -0
  54. package/dist/utils.d.ts +9 -0
  55. package/dist/utils.d.ts.map +1 -0
  56. package/dist/utils.js +90 -0
  57. package/package.json +43 -0
@@ -0,0 +1,6 @@
1
+ export declare const DeviceIds: {
2
+ vendorId: number;
3
+ productId: number;
4
+ name: string;
5
+ }[];
6
+ //# sourceMappingURL=devices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAK3E,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeviceIds = void 0;
4
+ exports.DeviceIds = [
5
+ { vendorId: 0x054c, productId: 0x01ad, name: 'Sony NW-HD1 / NW-HD2' },
6
+ { vendorId: 0x054c, productId: 0x0210, name: 'Sony NW-HD3' },
7
+ { vendorId: 0x054c, productId: 0x0233, name: 'Sony NW-HD5' },
8
+ { vendorId: 0x054c, productId: 0x0269, name: 'Sony NW-A3000' },
9
+ ];
@@ -0,0 +1,16 @@
1
+ import Crypto from '@originjs/crypto-js-wasm';
2
+ export declare function initCrypto(): Promise<void>;
3
+ export declare const EKBROOTS: {
4
+ [key: number]: Uint8Array;
5
+ };
6
+ export declare function importKeys(rawKeysContents: Uint8Array): void;
7
+ export declare function getMP3EncryptionKey(discId: Uint8Array, trackNumber: number): Uint8Array;
8
+ export declare function createTrackKeyForKeyring(ekbNum: number, verificationKey: Uint8Array, trackKey: Uint8Array): Uint8Array;
9
+ export declare function createMaclistValue(ekbNum: number, verificationKey: Uint8Array, contents: Uint8Array): Uint8Array;
10
+ export declare function retailMac(message: Uint8Array, key: Uint8Array): Uint8Array;
11
+ export declare function createIcvMac(icvAndHeader: Uint8Array, sessionKey: Uint8Array): Uint8Array;
12
+ export declare function encryptTrackKey(trackKey: Uint8Array): Uint8Array;
13
+ export declare function createTrackMac2(trackKeyWa: Crypto.lib.WordArray, trackEntry: Uint8Array): Uint8Array;
14
+ export declare function createTrackMac(trackKey: Uint8Array, trackEntry: Uint8Array): Uint8Array;
15
+ export declare function desDecrypt(data: Uint8Array, key: Uint8Array): Uint8Array;
16
+ //# sourceMappingURL=encryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAK9C,wBAAsB,UAAU,kBAG/B;AAGD,eAAO,MAAM,QAAQ,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAExC,CAAC;AAEX,wBAAgB,UAAU,CAAC,eAAe,EAAE,UAAU,QAQrD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAG1E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAsBzG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAenG;AAID,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAgB7D;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAM5E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,cAKnD;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,cAQvF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAG1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAE3D"}
@@ -0,0 +1,123 @@
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.desDecrypt = exports.createTrackMac = exports.createTrackMac2 = exports.encryptTrackKey = exports.createIcvMac = exports.retailMac = exports.createMaclistValue = exports.createTrackKeyForKeyring = exports.getMP3EncryptionKey = exports.importKeys = exports.EKBROOTS = exports.initCrypto = void 0;
7
+ const crypto_js_wasm_1 = __importDefault(require("@originjs/crypto-js-wasm"));
8
+ const bytemanip_1 = require("./bytemanip");
9
+ const errors_1 = require("./errors");
10
+ const utils_1 = require("./utils");
11
+ async function initCrypto() {
12
+ await crypto_js_wasm_1.default.TripleDES.loadWasm();
13
+ await crypto_js_wasm_1.default.DES.loadWasm();
14
+ }
15
+ exports.initCrypto = initCrypto;
16
+ // prettier-ignore
17
+ exports.EKBROOTS = {
18
+ // <Redacted>
19
+ };
20
+ function importKeys(rawKeysContents) {
21
+ let offset = 0;
22
+ const dataView = new DataView(rawKeysContents.buffer);
23
+ while (offset < rawKeysContents.length) {
24
+ let ekbid;
25
+ [ekbid, offset] = (0, bytemanip_1.readUint32)(dataView, offset);
26
+ exports.EKBROOTS[ekbid] = rawKeysContents.slice(offset, offset += 3 * 8);
27
+ }
28
+ }
29
+ exports.importKeys = importKeys;
30
+ function getMP3EncryptionKey(discId, trackNumber) {
31
+ const key = (trackNumber * 0x6953b2ed + 0x6baab1) ^ (0, bytemanip_1.getUint32)(discId, 12);
32
+ return (0, bytemanip_1.writeUint32)(key);
33
+ }
34
+ exports.getMP3EncryptionKey = getMP3EncryptionKey;
35
+ function createTrackKeyForKeyring(ekbNum, verificationKey, trackKey) {
36
+ if (!(ekbNum in exports.EKBROOTS)) {
37
+ throw new errors_1.NWJSError('Requested decription with an unknown EKB');
38
+ }
39
+ const rootKeyC = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[ekbNum]);
40
+ const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
41
+ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(verificationKey),
42
+ });
43
+ const trackKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
44
+ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(trackKey),
45
+ });
46
+ // Step 1: get real verification key
47
+ const decryptedVerificationKey = crypto_js_wasm_1.default.TripleDES.decrypt(verificationKeyC, rootKeyC, {
48
+ mode: crypto_js_wasm_1.default.mode.ECB,
49
+ });
50
+ // Step 2: get "encrypted" track key - so decrypt it
51
+ const decryptedTrackKey = crypto_js_wasm_1.default.TripleDES.decrypt(trackKeyC, decryptedVerificationKey, {
52
+ mode: crypto_js_wasm_1.default.mode.ECB,
53
+ });
54
+ return (0, utils_1.wordArrayToByteArray)(decryptedTrackKey, 8);
55
+ }
56
+ exports.createTrackKeyForKeyring = createTrackKeyForKeyring;
57
+ function createMaclistValue(ekbNum, verificationKey, contents) {
58
+ if (!(ekbNum in exports.EKBROOTS)) {
59
+ throw new errors_1.NWJSError('Requested decription with an unknown EKB');
60
+ }
61
+ const rootKeyC = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[ekbNum]);
62
+ const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
63
+ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(verificationKey),
64
+ });
65
+ // Step 1: get real verification key
66
+ const decryptedVerificationKey = crypto_js_wasm_1.default.TripleDES.decrypt(verificationKeyC, rootKeyC, {
67
+ mode: crypto_js_wasm_1.default.mode.ECB,
68
+ });
69
+ return createTrackMac2(decryptedVerificationKey, contents);
70
+ }
71
+ exports.createMaclistValue = createMaclistValue;
72
+ const NO_PADDING = { pad: (a) => a, unpad: (a) => a };
73
+ function retailMac(message, key) {
74
+ const keyA = key.subarray(0, 8);
75
+ const keyB = key.subarray(8, 16);
76
+ const messageWa = crypto_js_wasm_1.default.lib.WordArray.create(message);
77
+ const keyAWa = crypto_js_wasm_1.default.lib.WordArray.create(keyA);
78
+ const keyBWa = crypto_js_wasm_1.default.lib.WordArray.create(keyB);
79
+ const zeroWa = crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0));
80
+ const encA = crypto_js_wasm_1.default.DES.encrypt(messageWa, keyAWa, { padding: NO_PADDING, mode: crypto_js_wasm_1.default.mode.CBC, iv: zeroWa }).ciphertext;
81
+ const messageBFull = (0, utils_1.wordArrayToByteArray)(encA);
82
+ const messageB = messageBFull.subarray(messageBFull.length - 8);
83
+ const messageBWa = crypto_js_wasm_1.default.lib.CipherParams.create({
84
+ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(messageB),
85
+ });
86
+ const encB = crypto_js_wasm_1.default.DES.decrypt(messageBWa, keyBWa, { padding: NO_PADDING, mode: crypto_js_wasm_1.default.mode.ECB });
87
+ const final = crypto_js_wasm_1.default.DES.encrypt(encB, keyAWa, { padding: NO_PADDING, mode: crypto_js_wasm_1.default.mode.ECB }).ciphertext;
88
+ return (0, utils_1.wordArrayToByteArray)(final);
89
+ }
90
+ exports.retailMac = retailMac;
91
+ function createIcvMac(icvAndHeader, sessionKey) {
92
+ const icvWa = crypto_js_wasm_1.default.lib.WordArray.create(icvAndHeader);
93
+ const sessionKeyWa = crypto_js_wasm_1.default.lib.WordArray.create(sessionKey);
94
+ const zeroWa = crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0));
95
+ const result = crypto_js_wasm_1.default.DES.encrypt(icvWa, sessionKeyWa, { mode: crypto_js_wasm_1.default.mode.CBC, iv: zeroWa, padding: NO_PADDING });
96
+ return (0, utils_1.wordArrayToByteArray)(result.ciphertext).subarray(-8);
97
+ }
98
+ exports.createIcvMac = createIcvMac;
99
+ function encryptTrackKey(trackKey) {
100
+ const trackKeyWa = crypto_js_wasm_1.default.lib.WordArray.create(trackKey);
101
+ const keyWa = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[0x00010012]);
102
+ const encrypted = crypto_js_wasm_1.default.TripleDES.encrypt(trackKeyWa, keyWa, { mode: crypto_js_wasm_1.default.mode.ECB, padding: NO_PADDING });
103
+ return (0, utils_1.wordArrayToByteArray)(encrypted.ciphertext);
104
+ }
105
+ exports.encryptTrackKey = encryptTrackKey;
106
+ function createTrackMac2(trackKeyWa, trackEntry) {
107
+ const trackEntryWa = crypto_js_wasm_1.default.lib.WordArray.create(trackEntry);
108
+ const macKeySourceWa = crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0));
109
+ const macKey = crypto_js_wasm_1.default.DES.encrypt(macKeySourceWa, trackKeyWa, { mode: crypto_js_wasm_1.default.mode.ECB, padding: NO_PADDING }).ciphertext;
110
+ const zeroWa = crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0));
111
+ const mac = crypto_js_wasm_1.default.DES.encrypt(trackEntryWa, macKey, { mode: crypto_js_wasm_1.default.mode.CBC, iv: zeroWa, padding: NO_PADDING });
112
+ return (0, utils_1.wordArrayToByteArray)(mac.ciphertext).subarray(-8);
113
+ }
114
+ exports.createTrackMac2 = createTrackMac2;
115
+ function createTrackMac(trackKey, trackEntry) {
116
+ const trackKeyWa = crypto_js_wasm_1.default.lib.WordArray.create(trackKey);
117
+ return createTrackMac2(trackKeyWa, trackEntry);
118
+ }
119
+ exports.createTrackMac = createTrackMac;
120
+ function desDecrypt(data, key) {
121
+ return (0, utils_1.wordArrayToByteArray)(crypto_js_wasm_1.default.TripleDES.decrypt(crypto_js_wasm_1.default.lib.CipherParams.create({ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(data) }), crypto_js_wasm_1.default.lib.WordArray.create(key), { mode: crypto_js_wasm_1.default.mode.ECB, iv: crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0)) }), data.length);
122
+ }
123
+ exports.desDecrypt = desDecrypt;
@@ -0,0 +1,4 @@
1
+ export declare class NWJSError extends Error {
2
+ constructor(m: string);
3
+ }
4
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;gBACpB,CAAC,EAAE,MAAM;CAIxB"}
package/dist/errors.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NWJSError = void 0;
4
+ class NWJSError extends Error {
5
+ constructor(m) {
6
+ super(m);
7
+ Object.setPrototypeOf(this, NWJSError.prototype);
8
+ }
9
+ }
10
+ exports.NWJSError = NWJSError;
@@ -0,0 +1,2 @@
1
+ export * from './usb-mass-storage-webusb-filesystem';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filesystem/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./usb-mass-storage-webusb-filesystem"), exports);
@@ -0,0 +1,44 @@
1
+ import { HiMDFile, HiMDFilesystem, SonyVendorUSMCDriver, UMSCHiMDFilesystem } from 'himd-js';
2
+ import { WebUSBDevice } from 'usb';
3
+ export declare class SonyVendorNWJSUSMCDriver extends SonyVendorUSMCDriver {
4
+ protected drmRead(param: number, length: number): Promise<Uint8Array>;
5
+ protected drmWrite(param: number, data: Uint8Array): Promise<void>;
6
+ writeHostLeafID(leafID: Uint8Array, hostNonce: Uint8Array): Promise<void>;
7
+ getAuthenticationStage2Info(): Promise<{
8
+ discId: Uint8Array;
9
+ mac: Uint8Array;
10
+ deviceLeafId: Uint8Array;
11
+ deviceNonce: Uint8Array;
12
+ }>;
13
+ writeAuthenticationStage3Info(hostMac: Uint8Array): Promise<void>;
14
+ readMasterKey(): Promise<{
15
+ header: Uint8Array;
16
+ masterKey: Uint8Array;
17
+ }>;
18
+ writeMasterKeyAndMac(generation: number, masterKey: Uint8Array, mac: Uint8Array, sessionKey: Uint8Array): Promise<void>;
19
+ }
20
+ export declare class UMSCNWJSSession {
21
+ protected driver: SonyVendorNWJSUSMCDriver;
22
+ protected fs: HiMDFilesystem;
23
+ hostNonce: Uint8Array;
24
+ hostLeafId: Uint8Array;
25
+ deviceNonce?: Uint8Array;
26
+ discId?: Uint8Array;
27
+ deviceLeafId?: Uint8Array;
28
+ currentIcv?: Uint8Array;
29
+ currentIcvHeader?: Uint8Array;
30
+ sessionKey?: Uint8Array;
31
+ mclistHandle?: HiMDFile;
32
+ currentGeneration?: number;
33
+ allMacs?: Uint8Array;
34
+ constructor(driver: SonyVendorNWJSUSMCDriver, fs: HiMDFilesystem);
35
+ performAuthorization(): Promise<void>;
36
+ finalizeSession(): Promise<void>;
37
+ writeTrackMac(trackNumber: number, mac: Uint8Array): void;
38
+ }
39
+ export declare class UMSCNWJSFilesystem extends UMSCHiMDFilesystem {
40
+ driver: SonyVendorNWJSUSMCDriver;
41
+ constructor(webUSB: WebUSBDevice);
42
+ protected initFS(bypassCoherencyChecks?: boolean | undefined): Promise<void>;
43
+ }
44
+ //# sourceMappingURL=usb-mass-storage-webusb-filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usb-mass-storage-webusb-filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem/usb-mass-storage-webusb-filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAI7F,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAGnC,qBAAa,wBAAyB,SAAQ,oBAAoB;cAC9C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;cAuBrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IA2BlD,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU;IASzD,2BAA2B;;;;;;IAoB3B,6BAA6B,CAAC,OAAO,EAAE,UAAU;IAyBjD,aAAa;;;;IAWb,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;CAuBhH;AAED,qBAAa,eAAe;IAgBZ,SAAS,CAAC,MAAM,EAAE,wBAAwB;IAAE,SAAS,CAAC,EAAE,EAAE,cAAc;IAfpF,SAAS,aAAuB;IAChC,UAAU,aAAoE;IAE9E,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gBAAgB,CAAC,EAAE,UAAU,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,EAAE,UAAU,CAAC;gBAEC,MAAM,EAAE,wBAAwB,EAAY,EAAE,EAAE,cAAc;IAEvE,oBAAoB;IAkBpB,eAAe;IAYrB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU;CAG5D;AAED,qBAAa,kBAAmB,SAAQ,kBAAkB;IACtD,MAAM,EAAE,wBAAwB,CAAC;gBACrB,MAAM,EAAE,YAAY;cAKhB,MAAM,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYrF"}
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UMSCNWJSFilesystem = exports.UMSCNWJSSession = exports.SonyVendorNWJSUSMCDriver = void 0;
4
+ const himd_js_1 = require("himd-js");
5
+ const utils_1 = require("../utils");
6
+ const bytemanip_1 = require("../bytemanip");
7
+ const encryption_1 = require("../encryption");
8
+ const nufatfs_1 = require("nufatfs");
9
+ class SonyVendorNWJSUSMCDriver extends himd_js_1.SonyVendorUSMCDriver {
10
+ async drmRead(param, length) {
11
+ const command = new Uint8Array([
12
+ 0xa4,
13
+ 0x00,
14
+ 0x00,
15
+ 0x00,
16
+ 0x00,
17
+ 0x00,
18
+ 0x00,
19
+ 0xbc,
20
+ (length >> 8) & 0xff,
21
+ length & 0xff,
22
+ param,
23
+ 0x00,
24
+ 0x00,
25
+ 0x00,
26
+ 0x00,
27
+ 0x00,
28
+ ]);
29
+ const result = await this.sendCommandInGetResult(command, length, false, command.length);
30
+ return result.result.subarray(2);
31
+ }
32
+ async drmWrite(param, data) {
33
+ const newData = new Uint8Array(data.length + 2);
34
+ newData.set((0, bytemanip_1.writeUint16)(data.length), 0);
35
+ newData.set(data, 2);
36
+ const length = newData.length;
37
+ const command = new Uint8Array([
38
+ 0xa3,
39
+ 0x00,
40
+ 0x00,
41
+ 0x00,
42
+ 0x00,
43
+ 0x00,
44
+ 0x00,
45
+ 0xbc,
46
+ (length >> 8) & 0xff,
47
+ length & 0xff,
48
+ param,
49
+ 0x00,
50
+ 0x00,
51
+ 0x00,
52
+ 0x00,
53
+ 0x00,
54
+ ]);
55
+ await this.sendCommandOutGetResult(command, newData, command.length);
56
+ }
57
+ async writeHostLeafID(leafID, hostNonce) {
58
+ (0, utils_1.assert)(leafID.length === 8, 'Wrong length of leaf id');
59
+ const finalBuffer = new Uint8Array(2 + 8 + 8);
60
+ finalBuffer.fill(0);
61
+ finalBuffer.set(leafID, 2);
62
+ finalBuffer.set(hostNonce, 10);
63
+ await this.drmWrite(0x30, finalBuffer);
64
+ }
65
+ async getAuthenticationStage2Info() {
66
+ const data = await this.drmRead(0x31, 0x43c);
67
+ let _current = 2;
68
+ const read = (len) => data.subarray(_current, (_current += len));
69
+ const discId = read(16);
70
+ const mac = read(8);
71
+ const deviceLeafId = read(8);
72
+ const deviceNonce = read(8);
73
+ // // EKB info begin
74
+ // const keyType = read(4);
75
+ // const keyLevel = read(4);
76
+ // const ekbid = read(4);
77
+ // const zero = read(4);
78
+ // const key = read(16);
79
+ return { discId, mac, deviceLeafId, deviceNonce };
80
+ }
81
+ async writeAuthenticationStage3Info(hostMac) {
82
+ const finalBuffer = new Uint8Array(0x41a);
83
+ finalBuffer.fill(0);
84
+ finalBuffer.set(hostMac, 2);
85
+ // Send own configuration
86
+ // Key type 1
87
+ // Key level 8
88
+ // EKB 00010021
89
+ // Encrypted Node Key: 0xf0, 0x5c, 0xb7, 0xfe, 0xde, 0x3c, 0x94, 0x01, 0x78, 0x4a, 0x71, 0x8d, 0x9f, 0xf7, 0xf4, 0xb1
90
+ finalBuffer.set([
91
+ 0x00, 0x00, 0x00, 0x01,
92
+ 0x00, 0x00, 0x00, 0x08,
93
+ 0x00, 0x01, 0x00, 0x21,
94
+ 0x00, 0x00, 0x00, 0x00,
95
+ 0xf0, 0x5c, 0xb7, 0xfe, 0xde, 0x3c, 0x94, 0x01,
96
+ 0x78, 0x4a, 0x71, 0x8d, 0x9f, 0xf7, 0xf4, 0xb1,
97
+ ], 10);
98
+ await this.drmWrite(0x32, finalBuffer);
99
+ }
100
+ async readMasterKey() {
101
+ const data = await this.drmRead(0x33, 0x404);
102
+ let _current = 2;
103
+ const read = (len) => data.subarray(_current, (_current += len));
104
+ const header = read(8);
105
+ // Zeros
106
+ _current += 4;
107
+ const masterKey = read(16);
108
+ return { header, masterKey };
109
+ }
110
+ async writeMasterKeyAndMac(generation, masterKey, mac, sessionKey) {
111
+ // masterKey = 0xe0, 0xaa, 0x0b, 0x24, 0xd5, 0x1f, 0x97, 0x1d, 0x57, 0xaa, 0x7a, 0x3f, 0x8f, 0x93, 0xe7, 0xb3
112
+ // mac = 0x9e, 0x29, 0xe4, 0xa3, 0x4f, 0x8d, 0x43, 0xc7
113
+ const generationBytes = (0, bytemanip_1.writeUint32)(generation);
114
+ const theSonySoup = new Uint8Array([
115
+ 0x00, 0x20, 0x00, 0x98,
116
+ ...generationBytes,
117
+ 0x00, 0x00, 0x00, 0x00,
118
+ 0x00, 0x01, 0x00, 0x21,
119
+ ...masterKey,
120
+ ...mac,
121
+ 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00
122
+ ]);
123
+ theSonySoup.set((0, bytemanip_1.writeUint32)(generation), 4);
124
+ const newMac = (0, encryption_1.createIcvMac)(theSonySoup, sessionKey);
125
+ const finalBuffer = new Uint8Array(0x402).fill(0);
126
+ finalBuffer.set(newMac, 2 + theSonySoup.length);
127
+ finalBuffer.set(theSonySoup, 2);
128
+ await this.drmWrite(0x34, finalBuffer);
129
+ }
130
+ }
131
+ exports.SonyVendorNWJSUSMCDriver = SonyVendorNWJSUSMCDriver;
132
+ class UMSCNWJSSession {
133
+ constructor(driver, fs) {
134
+ this.driver = driver;
135
+ this.fs = fs;
136
+ this.hostNonce = (0, utils_1.createRandomBytes)();
137
+ this.hostLeafId = new Uint8Array([0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
138
+ }
139
+ async performAuthorization() {
140
+ await this.driver.writeHostLeafID(this.hostLeafId, this.hostNonce);
141
+ const resultsStage2 = await this.driver.getAuthenticationStage2Info();
142
+ this.discId = resultsStage2.discId;
143
+ this.deviceLeafId = resultsStage2.deviceLeafId;
144
+ this.deviceNonce = resultsStage2.deviceNonce;
145
+ // Disregard device mac
146
+ const ekb10021Root = encryption_1.EKBROOTS[0x00010021];
147
+ const hostMac = (0, encryption_1.retailMac)((0, utils_1.concatUint8Arrays)([resultsStage2.discId, resultsStage2.deviceNonce, this.hostNonce]), ekb10021Root);
148
+ await this.driver.writeAuthenticationStage3Info(hostMac);
149
+ const { header, masterKey: anyKey } = await this.driver.readMasterKey();
150
+ this.sessionKey = (0, encryption_1.retailMac)((0, utils_1.concatUint8Arrays)([resultsStage2.discId, resultsStage2.mac, hostMac]), ekb10021Root);
151
+ this.currentGeneration = (0, bytemanip_1.getUint32)(header, 4);
152
+ this.mclistHandle = await this.fs.open("/OMGAUDIO/MACLIST0.DAT", "rw");
153
+ this.allMacs = await this.mclistHandle.read();
154
+ }
155
+ async finalizeSession() {
156
+ const ekb10021Root = encryption_1.EKBROOTS[0x00010021];
157
+ this.currentGeneration += 1;
158
+ const masterKey = new TextEncoder().encode('.DRMisBadForYou.');
159
+ const mac = (0, encryption_1.retailMac)((0, utils_1.concatUint8Arrays)([this.discId, this.allMacs]), (0, encryption_1.desDecrypt)(masterKey, ekb10021Root));
160
+ await this.driver.writeMasterKeyAndMac(this.currentGeneration, masterKey, mac, this.sessionKey);
161
+ await this.mclistHandle.seek(0);
162
+ await this.mclistHandle.write(this.allMacs);
163
+ await this.mclistHandle.close();
164
+ }
165
+ // trackNumber starts from 0
166
+ writeTrackMac(trackNumber, mac) {
167
+ this.allMacs.set(mac, trackNumber * 8);
168
+ }
169
+ }
170
+ exports.UMSCNWJSSession = UMSCNWJSSession;
171
+ class UMSCNWJSFilesystem extends himd_js_1.UMSCHiMDFilesystem {
172
+ constructor(webUSB) {
173
+ super(webUSB);
174
+ this.driver = new SonyVendorNWJSUSMCDriver(webUSB, 0x06);
175
+ }
176
+ async initFS(bypassCoherencyChecks) {
177
+ await this.driver.inquiry();
178
+ await this.driver.testUnitReady();
179
+ const partInfo = await this.driver.getCapacity();
180
+ console.log(partInfo);
181
+ const baseDriver = await this.driver.createNUFatFSVolumeDriverFromMBRPart(0, true);
182
+ this.fsUncachedDriver = this.fsDriver = (0, nufatfs_1.createChunkingDriver)(baseDriver, 240, partInfo.blockSize);
183
+ this.fatfs = await nufatfs_1.FatFilesystem.create(this.fsDriver, bypassCoherencyChecks);
184
+ this.volumeSize = partInfo.deviceSize;
185
+ }
186
+ }
187
+ exports.UMSCNWJSFilesystem = UMSCNWJSFilesystem;
@@ -0,0 +1,12 @@
1
+ import { CodecInfo } from "himd-js";
2
+ import { WebUSBDevice } from "usb";
3
+ import { TrackMetadata } from "./databases";
4
+ import { UMSCNWJSFilesystem, UMSCNWJSSession } from "./filesystem";
5
+ import { DatabaseAbstraction } from "./database-abstraction";
6
+ export declare function uploadTrack(database: DatabaseAbstraction, session: UMSCNWJSSession, trackInfo: TrackMetadata, codec: CodecInfo, rawData: Uint8Array, callback?: (done: number, outOf: number) => void): Promise<void>;
7
+ export declare function createNWJSFS(webUsbDevice: WebUSBDevice, bypassCoherencyChecks: boolean): Promise<UMSCNWJSFilesystem>;
8
+ export declare function openNewDeviceNode(): Promise<{
9
+ dev: WebUSBDevice;
10
+ name: string;
11
+ } | null>;
12
+ //# sourceMappingURL=functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,YAAY,EAAa,MAAM,KAAK,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAInE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAO7D,wBAAsB,WAAW,CAC7B,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,UAAU,EACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,iBA2BnD;AAED,wBAAsB,YAAY,CAAC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,OAAO,+BAO5F;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA2B7F"}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openNewDeviceNode = exports.createNWJSFS = exports.uploadTrack = void 0;
4
+ const usb_1 = require("usb");
5
+ const filesystem_1 = require("./filesystem");
6
+ const tagged_oma_1 = require("./tagged-oma");
7
+ const utils_1 = require("./utils");
8
+ const devices_1 = require("./devices");
9
+ const initialization_1 = require("./initialization");
10
+ function resolvePathFromGlobalIndex(globalTrackIndex) {
11
+ return (0, utils_1.join)('OMGAUDIO', '10F00', '1000' + globalTrackIndex.toString(16).padStart(4, '0').toUpperCase() + '.OMA');
12
+ }
13
+ async function uploadTrack(database, session, trackInfo, codec, rawData, callback) {
14
+ // Step 1 - Create the encrypted OMA which will later be written to the device's storage
15
+ const encryptedOMA = (0, tagged_oma_1.createTaggedEncryptedOMA)(rawData, trackInfo, codec);
16
+ // Step 2 - write track to the database
17
+ const globalTrackIndex = database.addNewTrack({
18
+ ...trackInfo,
19
+ trackDuration: encryptedOMA.duration,
20
+ }, codec);
21
+ // Step 3 - write track to the filesystem
22
+ const fh = await database.database.filesystem.open(resolvePathFromGlobalIndex(globalTrackIndex), 'rw');
23
+ const data = encryptedOMA.data;
24
+ let remaining = data.length;
25
+ let i = 0;
26
+ callback === null || callback === void 0 ? void 0 : callback(i, data.length);
27
+ while (remaining) {
28
+ const toWrite = Math.min(2048, remaining);
29
+ await fh.write(data.slice(i, i + toWrite));
30
+ i += toWrite;
31
+ remaining -= toWrite;
32
+ callback === null || callback === void 0 ? void 0 : callback(i, data.length);
33
+ }
34
+ await fh.close();
35
+ // Step 4 - write MAC
36
+ session.writeTrackMac(globalTrackIndex - 1, encryptedOMA.maclistValue);
37
+ }
38
+ exports.uploadTrack = uploadTrack;
39
+ async function createNWJSFS(webUsbDevice, bypassCoherencyChecks) {
40
+ // Connect into the HiMD codebase
41
+ const fs = new filesystem_1.UMSCNWJSFilesystem(webUsbDevice);
42
+ await fs.init();
43
+ await (0, initialization_1.initializeIfNeeded)(fs);
44
+ return fs;
45
+ }
46
+ exports.createNWJSFS = createNWJSFS;
47
+ async function openNewDeviceNode() {
48
+ let legacyDevice, devName = null;
49
+ for (let dev of devices_1.DeviceIds) {
50
+ legacyDevice = (0, usb_1.findByIds)(dev.vendorId, dev.productId);
51
+ if (legacyDevice) {
52
+ devName = dev.name;
53
+ break;
54
+ }
55
+ }
56
+ if (!legacyDevice) {
57
+ return null;
58
+ }
59
+ legacyDevice.open();
60
+ await new Promise(res => legacyDevice.reset(res));
61
+ const iface = legacyDevice.interface(0);
62
+ try {
63
+ if (iface.isKernelDriverActive())
64
+ iface.detachKernelDriver();
65
+ }
66
+ catch (ex) {
67
+ // console.log("Couldn't detach the kernel driver. Expected on Windows.");
68
+ }
69
+ const webUsbDevice = (await usb_1.WebUSBDevice.createInstance(legacyDevice));
70
+ await webUsbDevice.open();
71
+ return { dev: webUsbDevice, name: devName };
72
+ }
73
+ exports.openNewDeviceNode = openNewDeviceNode;
@@ -0,0 +1,9 @@
1
+ import { WebUSBDevice } from "usb";
2
+ import { UMSCNWJSFilesystem } from "./filesystem";
3
+ export declare function resolvePathFromGlobalIndex(globalTrackIndex: number): string;
4
+ export declare function createNWJSFS(webUsbDevice: WebUSBDevice, bypassCoherencyChecks: boolean): Promise<UMSCNWJSFilesystem>;
5
+ export declare function openNewDeviceNode(): Promise<{
6
+ dev: WebUSBDevice;
7
+ name: string;
8
+ } | null>;
9
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkB,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKlD,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,MAAM,UAElE;AAED,wBAAsB,YAAY,CAAC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,OAAO,+BAO5F;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA2B7F"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openNewDeviceNode = exports.createNWJSFS = exports.resolvePathFromGlobalIndex = void 0;
4
+ const usb_1 = require("usb");
5
+ const filesystem_1 = require("./filesystem");
6
+ const utils_1 = require("./utils");
7
+ const devices_1 = require("./devices");
8
+ const initialization_1 = require("./initialization");
9
+ function resolvePathFromGlobalIndex(globalTrackIndex) {
10
+ return (0, utils_1.join)('OMGAUDIO', '10F00', '1000' + globalTrackIndex.toString(16).padStart(4, '0').toUpperCase() + '.OMA');
11
+ }
12
+ exports.resolvePathFromGlobalIndex = resolvePathFromGlobalIndex;
13
+ async function createNWJSFS(webUsbDevice, bypassCoherencyChecks) {
14
+ // Connect into the HiMD codebase
15
+ const fs = new filesystem_1.UMSCNWJSFilesystem(webUsbDevice);
16
+ await fs.init();
17
+ await (0, initialization_1.initializeIfNeeded)(fs);
18
+ return fs;
19
+ }
20
+ exports.createNWJSFS = createNWJSFS;
21
+ async function openNewDeviceNode() {
22
+ let legacyDevice = undefined, devName = null;
23
+ for (let dev of devices_1.DeviceIds) {
24
+ legacyDevice = (0, usb_1.findByIds)(dev.vendorId, dev.productId);
25
+ if (legacyDevice) {
26
+ devName = dev.name;
27
+ break;
28
+ }
29
+ }
30
+ if (!legacyDevice) {
31
+ return null;
32
+ }
33
+ legacyDevice.open();
34
+ await new Promise(res => legacyDevice.reset(res));
35
+ const iface = legacyDevice.interface(0);
36
+ try {
37
+ if (iface.isKernelDriverActive())
38
+ iface.detachKernelDriver();
39
+ }
40
+ catch (ex) {
41
+ // console.log("Couldn't detach the kernel driver. Expected on Windows.");
42
+ }
43
+ const webUsbDevice = (await usb_1.WebUSBDevice.createInstance(legacyDevice));
44
+ await webUsbDevice.open();
45
+ return { dev: webUsbDevice, name: devName };
46
+ }
47
+ exports.openNewDeviceNode = openNewDeviceNode;
package/dist/id3.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ export interface ID3Tag {
2
+ id: string;
3
+ flags: number;
4
+ contents: Uint8Array;
5
+ }
6
+ export interface ID3Tags {
7
+ version: {
8
+ minor: number;
9
+ major: number;
10
+ };
11
+ tags: ID3Tag[];
12
+ flags: number;
13
+ }
14
+ export declare function readSynchsafeInt32(data: DataView, offset: number): [number, number];
15
+ export declare function parse(buffer: Uint8Array): ID3Tags;
16
+ export declare function serialize(tags: ID3Tags): Uint8Array;
17
+ export declare function encodeUTF16BEStringEA3(source: string, includeType?: boolean): Uint8Array;
18
+ //# sourceMappingURL=id3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id3.d.ts","sourceRoot":"","sources":["../src/id3.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACpB,OAAO,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAYnF;AAKD,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CA+CjD;AAWD,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAuCnD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAO,cAgCxE"}