diff --git a/.changeset/early-crews-study.md b/.changeset/early-crews-study.md new file mode 100644 index 00000000..866900db --- /dev/null +++ b/.changeset/early-crews-study.md @@ -0,0 +1,5 @@ +--- +"@nx.js/ncm": patch +--- + +Refactor to utilize `ArrayBufferStruct` subclasses diff --git a/packages/ncm/package.json b/packages/ncm/package.json index 30d8579c..f58e2162 100644 --- a/packages/ncm/package.json +++ b/packages/ncm/package.json @@ -20,7 +20,8 @@ "access": "public" }, "dependencies": { - "@nx.js/constants": "^0.3.0" + "@nx.js/constants": "^0.3.0", + "@nx.js/util": "^0.0.0" }, "devDependencies": { "@nx.js/runtime": "workspace:*" diff --git a/packages/ncm/src/content-meta-database.ts b/packages/ncm/src/content-meta-database.ts index ee8d6594..1a2d0c1e 100644 --- a/packages/ncm/src/content-meta-database.ts +++ b/packages/ncm/src/content-meta-database.ts @@ -1,11 +1,12 @@ import { SfBufferAttr } from '@nx.js/constants'; import { ncm } from './service'; -import type { +import { NcmContentId, NcmContentMetaKey, NcmContentType, NcmStorageId, } from './types'; +import { u8, view } from '@nx.js/util'; export class NcmContentMetaDatabase { static open(storageId: NcmStorageId) { @@ -17,7 +18,7 @@ export class NcmContentMetaDatabase { //} const out = new Switch.Service(); const inArr = new Uint8Array([storageId]); - ncm.dispatchIn(5, inArr.buffer, { + ncm.dispatchIn(5, inArr, { outObjects: [out], }); return new NcmContentMetaDatabase(out); @@ -32,7 +33,7 @@ export class NcmContentMetaDatabase { this.#srv = srv; } - set(key: NcmContentMetaKey, data: ArrayBuffer) { + set(key: NcmContentMetaKey, data: ArrayBuffer | ArrayBufferView) { //Result ncmContentMetaDatabaseSet(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, const void* data, u64 data_size) { // return serviceDispatchIn(&db->s, 0, *key, // .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, @@ -45,7 +46,7 @@ export class NcmContentMetaDatabase { }); } - get(key: NcmContentMetaKey, data: ArrayBuffer): bigint { + getInto(key: NcmContentMetaKey, data: ArrayBufferView): bigint { //Result ncmContentMetaDatabaseGet(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, u64* out_size, void* out_data, u64 out_data_size) { // return serviceDispatchInOut(&db->s, 1, *key, *out_size, // .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, @@ -60,6 +61,16 @@ export class NcmContentMetaDatabase { return out[0]; } + get(key: NcmContentMetaKey): ArrayBufferView { + const expectedSize = this.getSize(key); + const data = new Uint8Array(Number(expectedSize)); + const size = this.getInto(key, data); + if (size !== expectedSize) { + throw new Error(`Unexpected size: ${size} (expected: ${expectedSize})`); + } + return data; + } + delete(key: NcmContentMetaKey) { //Result ncmContentMetaDatabaseRemove(NcmContentMetaDatabase* db, const NcmContentMetaKey *key) { // return serviceDispatchIn(&db->s, 2, *key); @@ -79,13 +90,41 @@ export class NcmContentMetaDatabase { // } in = { type, {0}, *key }; // return serviceDispatchInOut(&db->s, 3, in, *out_content_id); //} - const inData = new ArrayBuffer(0x18); - const inArr = new Uint8Array(inData); - inArr[0] = type; - inArr.set(new Uint8Array(key), 0x8); - const out = new ArrayBuffer(0x10); + const inData = new Uint8Array(0x18); + inData[0] = type; + inData.set(u8(key), 0x8); + const out = new NcmContentId(); this.#srv.dispatchInOut(3, inData, out); - return out as NcmContentId; + return out; + } + + listContentInfo( + key: NcmContentMetaKey, + startIndex: number, + outInfo: ArrayBuffer, + ) { + //Result ncmContentMetaDatabaseListContentInfo(NcmContentMetaDatabase* db, s32* out_entries_written, NcmContentInfo* out_info, s32 count, const NcmContentMetaKey* key, s32 start_index) { + // const struct { + // s32 start_index; + // u32 pad; + // NcmContentMetaKey key; + // } in = { start_index, 0, *key }; + // return serviceDispatchInOut(&db->s, 4, in, *out_entries_written, + // .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + // .buffers = { { out_info, count*sizeof(NcmContentInfo) } }, + // ); + //} + const inData = new Uint8Array(0x18); + const inView = view(inData); + inView.setInt32(0, startIndex ?? 0, true); + inView.setUint32(4, 0, true); + inData.set(u8(key), 0x8); + const out = new Int32Array(1); + this.#srv.dispatchInOut(4, inData, out, { + bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.Out], + buffers: [outInfo], + }); + return out[0]; } has(key: NcmContentMetaKey): boolean { @@ -96,7 +135,7 @@ export class NcmContentMetaDatabase { // return rc; //} const out = new Uint8Array(1); - this.#srv.dispatchInOut(8, key, out.buffer); + this.#srv.dispatchInOut(8, key, out); return Boolean(out[0] & 1); } @@ -110,13 +149,12 @@ export class NcmContentMetaDatabase { // if (R_SUCCEEDED(rc) && out) *out = tmp & 1; // return rc; //} - const inData = new ArrayBuffer(keys.length * 0x10); - const inArr = new Uint8Array(inData); + const inData = new Uint8Array(keys.length * NcmContentMetaKey.sizeof); for (let i = 0; i < keys.length; i++) { - inArr.set(new Uint8Array(keys[i]), i * 0x10); + inData.set(u8(keys[i]), i * 0x10); } const out = new Uint8Array(1); - this.#srv.dispatchOut(9, out.buffer, { + this.#srv.dispatchOut(9, out, { bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.In], buffers: [inData], }); @@ -128,7 +166,7 @@ export class NcmContentMetaDatabase { // return serviceDispatchInOut(&db->s, 10, *key, *out_size); //} const out = new BigUint64Array(1); - this.#srv.dispatchInOut(10, key, out.buffer); + this.#srv.dispatchInOut(10, key, out); return out[0]; } diff --git a/packages/ncm/src/content-storage.ts b/packages/ncm/src/content-storage.ts index 1076a9d1..1812d074 100644 --- a/packages/ncm/src/content-storage.ts +++ b/packages/ncm/src/content-storage.ts @@ -1,6 +1,7 @@ import { SfBufferAttr } from '@nx.js/constants'; import { ncm } from './service'; -import type { NcmContentId, NcmPlaceHolderId, NcmStorageId } from './types'; +import { NcmContentId, NcmPlaceHolderId, NcmStorageId } from './types'; +import { decodeCString, u8, view } from '@nx.js/util'; export class NcmContentStorage { static open(storageId: NcmStorageId) { @@ -12,7 +13,7 @@ export class NcmContentStorage { //} const out = new Switch.Service(); const inArr = new Uint8Array([storageId]); - ncm.dispatchIn(4, inArr.buffer, { + ncm.dispatchIn(4, inArr, { outObjects: [out], }); return new NcmContentStorage(out); @@ -31,9 +32,9 @@ export class NcmContentStorage { //Result ncmContentStorageGeneratePlaceHolderId(NcmContentStorage* cs, NcmPlaceHolderId* out_id) { // return serviceDispatchOut(&cs->s, 0, *out_id); //} - const uuid = new ArrayBuffer(0x10); + const uuid = new NcmPlaceHolderId(); this.#srv.dispatchOut(0, uuid); - return uuid as NcmPlaceHolderId; + return uuid; } createPlaceHolder( @@ -58,11 +59,10 @@ export class NcmContentStorage { // return serviceDispatchIn(&cs->s, 1, in); // } //} - const inData = new ArrayBuffer(0x28); - const inDataView = new Uint8Array(inData); - inDataView.set(new Uint8Array(placeholderId), 0); - inDataView.set(new Uint8Array(contentId), 0x10); - new BigInt64Array(inData, 0x20, 1)[0] = size; + const inData = new Uint8Array(0x28); + inData.set(u8(placeholderId), 0); + inData.set(u8(contentId), NcmPlaceHolderId.sizeof); + view(inData).setBigInt64(0x20, size, true); this.#srv.dispatchIn(1, inData); } @@ -81,7 +81,7 @@ export class NcmContentStorage { // return rc; //} const out = new Uint8Array(1); - this.#srv.dispatchInOut(3, placeholderId, out.buffer); + this.#srv.dispatchInOut(3, placeholderId, out); return Boolean(out[0] & 1); } @@ -100,10 +100,9 @@ export class NcmContentStorage { // .buffers = { { data, data_size } }, // ); //} - const inData = new ArrayBuffer(0x18); - const inDataView = new Uint8Array(inData); - inDataView.set(new Uint8Array(placeholderId), 0); - new BigUint64Array(inData, 0x10, 1)[0] = offset; + const inData = new Uint8Array(0x18); + inData.set(u8(placeholderId), 0); + view(inData).setBigUint64(NcmPlaceHolderId.sizeof, offset, true); this.#srv.dispatchIn(4, inData, { bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.In], buffers: [data], @@ -126,10 +125,9 @@ export class NcmContentStorage { // return serviceDispatchIn(&cs->s, 5, in); // } //} - const inData = new ArrayBuffer(0x20); - const inDataView = new Uint8Array(inData); - inDataView.set(new Uint8Array(placeholderId), 0); - inDataView.set(new Uint8Array(contentId), 0x10); + const inData = new Uint8Array(0x20); + inData.set(u8(placeholderId), 0); + inData.set(u8(contentId), NcmPlaceHolderId.sizeof); this.#srv.dispatchIn(5, inData); } @@ -148,7 +146,7 @@ export class NcmContentStorage { // return rc; //} const out = new Uint8Array(1); - this.#srv.dispatchInOut(7, contentId, out.buffer); + this.#srv.dispatchInOut(7, contentId, out); return Boolean(out[0] & 1); } @@ -165,15 +163,14 @@ export class NcmContentStorage { // } // return rc; //} - const out = new ArrayBuffer(0x300); + const out = new Uint8Array(0x300); this.#srv.dispatchIn(8, contentId, { bufferAttrs: [ SfBufferAttr.FixedSize | SfBufferAttr.HipcPointer | SfBufferAttr.Out, ], buffers: [out], }); - const nul = new Uint8Array(out).indexOf(0); - return new TextDecoder().decode(out.slice(0, nul)); + return decodeCString(out); } getPlaceHolderPath(placeholderId: NcmPlaceHolderId): string { @@ -189,15 +186,14 @@ export class NcmContentStorage { // } // return rc; //} - const out = new ArrayBuffer(0x300); + const out = new Uint8Array(0x300); this.#srv.dispatchIn(9, placeholderId, { bufferAttrs: [ SfBufferAttr.FixedSize | SfBufferAttr.HipcPointer | SfBufferAttr.Out, ], buffers: [out], }); - const nul = new Uint8Array(out).indexOf(0); - return new TextDecoder().decode(out.slice(0, nul)); + return decodeCString(out); } cleanupAllPlaceHolder() { @@ -214,16 +210,16 @@ export class NcmContentStorage { // .buffers = { { out_ids, count*sizeof(NcmPlaceHolderId) } }, // ); //} - const outIds = new ArrayBuffer(maxCount * 0x10); + const outIds = new ArrayBuffer(maxCount * NcmPlaceHolderId.sizeof); const out = new Int32Array(1); - this.#srv.dispatchOut(11, out.buffer, { + this.#srv.dispatchOut(11, out, { bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.Out], buffers: [outIds], }); const outCount = out[0]; const rtn: NcmPlaceHolderId[] = new Array(outCount); for (let i = 0; i < outCount; i++) { - rtn[i] = outIds.slice(i * 0x10, i * 0x10 + 0x10) as NcmPlaceHolderId; + rtn[i] = new NcmPlaceHolderId(outIds, i * NcmPlaceHolderId.sizeof); } return rtn; } @@ -233,7 +229,7 @@ export class NcmContentStorage { // return serviceDispatchOut(&cs->s, 12, *out_count); //} const out = new Int32Array(1); - this.#srv.dispatchOut(12, out.buffer); + this.#srv.dispatchOut(12, out); return out[0]; } @@ -250,14 +246,14 @@ export class NcmContentStorage { const inData = new Int32Array([startOffset]); const outIds = new ArrayBuffer(maxCount * 0x10); const out = new Int32Array(1); - this.#srv.dispatchInOut(13, inData.buffer, out.buffer, { + this.#srv.dispatchInOut(13, inData, out, { bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.Out], buffers: [outIds], }); const outCount = out[0]; const rtn: NcmContentId[] = new Array(outCount); for (let i = 0; i < outCount; i++) { - rtn[i] = outIds.slice(i * 0x10, i * 0x10 + 0x10) as NcmContentId; + rtn[i] = new NcmContentId(outIds, i * 0x10); } return rtn; } @@ -267,7 +263,7 @@ export class NcmContentStorage { // return _ncmCmdInContentIdOutU64(&cs->s, content_id, (u64*)out_size, 14); //} const out = new BigInt64Array(1); - this.#srv.dispatchInOut(14, contentId, out.buffer); + this.#srv.dispatchInOut(14, contentId, out); return out[0]; } @@ -284,7 +280,7 @@ export class NcmContentStorage { // return _ncmCmdNoInOutU64(&cs->s, (u64*)out_size, 22); //} const out = new BigInt64Array(1); - this.#srv.dispatchOut(22, out.buffer); + this.#srv.dispatchOut(22, out); return out[0]; } @@ -294,7 +290,7 @@ export class NcmContentStorage { // return _ncmCmdNoInOutU64(&cs->s, (u64*)out_size, 23); //} const out = new BigInt64Array(1); - this.#srv.dispatchOut(23, out.buffer); + this.#srv.dispatchOut(23, out); return out[0]; } @@ -312,7 +308,7 @@ export class NcmContentStorage { // return _ncmCmdInPlaceHolderIdOutU64(&cs->s, placeholder_id, (u64*)out_size, 25); //} const out = new BigInt64Array(1); - this.#srv.dispatchInOut(25, placeholderId, out.buffer); + this.#srv.dispatchInOut(25, placeholderId, out); return out[0]; } diff --git a/packages/ncm/src/index.ts b/packages/ncm/src/index.ts index b9d2ac1a..7c2f0737 100644 --- a/packages/ncm/src/index.ts +++ b/packages/ncm/src/index.ts @@ -10,7 +10,7 @@ export function createContentStorage(storageId: NcmStorageId) { // return _ncmCmdInU8(&g_ncmSrv, storage_id, 0); //} const inData = new Uint8Array([storageId]); - ncm.dispatchIn(0, inData.buffer); + ncm.dispatchIn(0, inData); } export function createContentMetaDatabase(storageId: NcmStorageId) { @@ -18,7 +18,7 @@ export function createContentMetaDatabase(storageId: NcmStorageId) { // return _ncmCmdInU8(&g_ncmSrv, storage_id, 1); //} const inData = new Uint8Array([storageId]); - ncm.dispatchIn(1, inData.buffer); + ncm.dispatchIn(1, inData); } export function verifyContentStorage(storageId: NcmStorageId) { @@ -26,7 +26,7 @@ export function verifyContentStorage(storageId: NcmStorageId) { // return _ncmCmdInU8(&g_ncmSrv, storage_id, 2); //} const inData = new Uint8Array([storageId]); - ncm.dispatchIn(2, inData.buffer); + ncm.dispatchIn(2, inData); } export function verifyContentMetaDatabase(storageId: NcmStorageId) { @@ -34,5 +34,5 @@ export function verifyContentMetaDatabase(storageId: NcmStorageId) { // return _ncmCmdInU8(&g_ncmSrv, storage_id, 3); //} const inData = new Uint8Array([storageId]); - ncm.dispatchIn(3, inData.buffer); + ncm.dispatchIn(3, inData); } diff --git a/packages/ncm/src/types.ts b/packages/ncm/src/types.ts index 48d67c85..3e895a52 100644 --- a/packages/ncm/src/types.ts +++ b/packages/ncm/src/types.ts @@ -1,3 +1,8 @@ +import { ArrayBufferStruct, singleton, view } from '@nx.js/util'; + +const contentIds = new WeakMap(); +const contentMetaKeys = new WeakMap(); + /// StorageId export enum NcmStorageId { None = 0, ///< None @@ -34,7 +39,6 @@ export enum NcmContentMetaType { Delta = 0x83, ///< Delta DataPatch = 0x84, ///< DataPatch } -NcmContentMetaType; /// ContentMetaAttribute export enum NcmContentMetaAttribute { @@ -57,29 +61,124 @@ export enum NcmContentMetaPlatform { } /// ContentId -//typedef struct { -// u8 c[0x10]; ///< Id -//} NcmContentId; -declare const contentIdSymbol: unique symbol; -export type NcmContentId = ArrayBuffer & { - [contentIdSymbol]: void; -}; +export class NcmContentId extends ArrayBufferStruct { + //u8 c[0x10]; ///< Id + static sizeof = 0x10 as const; +} /// PlaceHolderId -//typedef struct { -// Uuid uuid; ///< UUID -//} NcmPlaceHolderId; -declare const placeHolderIdSymbol: unique symbol; -export type NcmPlaceHolderId = ArrayBuffer & { - [placeHolderIdSymbol]: void; -}; +export class NcmPlaceHolderId extends ArrayBufferStruct { + //Uuid uuid; ///< UUID + static sizeof = 0x10 as const; +} + +export class NcmContentStorageRecord extends ArrayBufferStruct { + //NcmContentMetaKey key; + //u8 storage_id; + //u8 padding[0x7]; + static sizeof = 0x18 as const; + + get key() { + return singleton(contentMetaKeys, this, () => new NcmContentMetaKey(this)); + } + get storageId(): NcmStorageId { + return view(this).getUint8(0x10); + } +} /// ContentMetaKey -//typedef struct { -// u64 id; ///< Id. -// u32 version; ///< Version. -// u8 type; ///< \ref NcmContentMetaType -// u8 install_type; ///< \ref NcmContentInstallType -// u8 padding[2]; ///< Padding. -//} NcmContentMetaKey; -export type NcmContentMetaKey = ArrayBuffer; +export class NcmContentMetaKey extends ArrayBufferStruct { + //u64 id; ///< Id. + //u32 version; ///< Version. + //u8 type; ///< \ref NcmContentMetaType + //u8 install_type; ///< \ref NcmContentInstallType + //u8 padding[2]; ///< Padding. + static sizeof = 0x10 as const; + + get id() { + return view(this).getBigUint64(0x0, true); + } + get version() { + return view(this).getUint32(0x8, true); + } + get type(): NcmContentMetaType { + return view(this).getUint8(0xc); + } + get installType(): NcmContentInstallType { + return view(this).getUint8(0xd); + } +} + +export class NcmContentInfo extends ArrayBufferStruct { + //NcmContentId content_id; ///< \ref NcmContentId + //u32 size_low; ///< Content size (low). + //u8 size_high; ///< Content size (high). + //u8 attr; ///< Content attributes. + //u8 content_type; ///< \ref NcmContentType. + //u8 id_offset; ///< Offset of this content. Unused by most applications. + static sizeof = 0x18 as const; + + get contentId() { + return singleton(contentIds, this, () => new NcmContentId(this)); + } + get sizeLow() { + return view(this).getUint32(0x10, true); + } + get sizeHigh() { + return view(this).getUint8(0x14); + } + get size(): bigint { + return (BigInt(this.sizeHigh) << 32n) | BigInt(this.sizeLow); + } + get attr() { + return view(this).getUint8(0x15); + } + get contentType(): NcmContentType { + return view(this).getUint8(0x16); + } + get idOffset() { + return view(this).getUint8(0x17); + } +} + +export class NcmContentMetaHeader extends ArrayBufferStruct { + // u16 extended_header_size; ///< Size of optional struct that comes after this one. + // u16 content_count; ///< Number of NcmContentInfos after the extra bytes. + // u16 content_meta_count; ///< Number of NcmContentMetaInfos that come after the NcmContentInfos. + // u8 attributes; ///< Usually None (0). + // u8 storage_id; ///< Usually None (0). + static sizeof = 0x8 as const; + + get extendedHeaderSize() { + return view(this).getUint16(0x0, true); + } + get contentCount() { + return view(this).getUint16(0x2, true); + } + get contentMetaCount() { + return view(this).getUint16(0x4, true); + } + get attributes() { + return view(this).getUint8(0x6); + } + get storageId() { + return view(this).getUint8(0x7); + } +} + +export class NcmApplicationMetaExtendedHeader extends ArrayBufferStruct { + // u64 patch_id; ///< PatchId of this application's patch. + // u32 required_system_version; ///< Firmware version required by this application. + // u32 required_application_version; ///< [9.0.0+] Owner application version required by this application. Previously padding. + static sizeof = 0x10 as const; + + get patchId() { + return view(this).getBigUint64(0x0, true); + } + get requiredSystemVersion() { + return view(this).getUint32(0x8, true); + } + get requiredApplicationVersion() { + return view(this).getUint32(0xc, true); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5746be6a..7386a58c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -575,6 +575,9 @@ importers: '@nx.js/constants': specifier: ^0.3.0 version: link:../constants + '@nx.js/util': + specifier: ^0.0.0 + version: link:../util devDependencies: '@nx.js/runtime': specifier: workspace:*