Skip to content

Commit

Permalink
Add @nx.js/ncm package (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate authored Dec 19, 2024
1 parent 4dc91ce commit 01a13f0
Show file tree
Hide file tree
Showing 14 changed files with 741 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/tender-crews-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nx.js/ncm": patch
---

Add `@nx.js/ncm` package
7 changes: 7 additions & 0 deletions docs/app/toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
TbClockBolt,
TbDatabaseEdit,
TbGlobe,
TbBrowser,
TbBraces,
Expand Down Expand Up @@ -42,6 +43,12 @@ export function Toggle() {
url: '/inspect',
icon: <TbZoomQuestion size={24} />,
},
{
title: '@nx.js/ncm',
description: 'Content manager API',
url: '/ncm',
icon: <TbDatabaseEdit size={24} />,
},
{
title: '@nx.js/repl',
description: 'Read-Eval-Print Loop for nx.js',
Expand Down
43 changes: 43 additions & 0 deletions docs/content/ncm/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Usage
description: "Content Manager (ncm) service IPC wrapper for nx.js applications"
---

The `@nx.js/ncm` package provides a wrapper for the `ncm` service IPC, which
allows for management of the Nintendo Content storages and meta databases.

In short, use this package for installing titles to your Switch.

## Installation

<InstallTabs items={['npm', 'pnpm', 'yarn', 'bun']}>
<Tab value='npm'>

```bash
npm install @nx.js/ncm
```

</Tab>
<Tab value="pnpm">

```bash
pnpm add @nx.js/ncm
```

</Tab>
<Tab value='yarn'>

```bash
yarn add @nx.js/ncm
```

</Tab>
<Tab value='bun'>

```bash
bun add @nx.js/ncm
```

</Tab>

</InstallTabs>
10 changes: 10 additions & 0 deletions docs/content/ncm/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"root": true,
"pages": [
"--- Documentation ---",
"index",
"...",
"--- API Reference ---",
"...api"
]
}
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@nx.js/constants": "workspace:*",
"@nx.js/clkrst": "workspace:*",
"@nx.js/http": "workspace:*",
"@nx.js/ncm": "workspace:*",
"@nx.js/repl": "workspace:*",
"@types/mdx": "^2.0.13",
"@types/node": "20",
Expand Down
40 changes: 40 additions & 0 deletions packages/ncm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@nx.js/ncm",
"version": "0.0.0",
"description": "Content Manager (ncm) service IPC wrapper for nx.js",
"repository": {
"type": "git",
"url": "https://github.com/TooTallNate/nx.js.git",
"directory": "packages/ncm"
},
"homepage": "https://nxjs.n8.io/ncm",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "vitest",
"docs": "typedoc && ../../type-aliases-meta.sh"
},
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"dependencies": {
"@nx.js/constants": "^0.3.0"
},
"devDependencies": {
"@nx.js/runtime": "workspace:*",
"vite": "^5.4.2",
"vitest": "^2.0.5"
},
"keywords": [
"nx.js",
"switch",
"ncm",
"content",
"manager"
],
"author": "Nathan Rajlich <[email protected]>",
"license": "MIT"
}
141 changes: 141 additions & 0 deletions packages/ncm/src/content-meta-database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { SfBufferAttr } from '@nx.js/constants';
import { ncm } from './service';
import type {
NcmContentId,
NcmContentMetaKey,
NcmContentType,
NcmStorageId,
} from './types';

export class NcmContentMetaDatabase {
static open(storageId: NcmStorageId) {
//Result ncmOpenContentMetaDatabase(NcmContentMetaDatabase* out_content_meta_database, NcmStorageId storage_id) {
// return serviceDispatchIn(&g_ncmSrv, 5, storage_id,
// .out_num_objects = 1,
// .out_objects = out_content_meta_database,
// );
//}
const out = new Switch.Service();
const inArr = new Uint8Array([storageId]);
ncm.dispatchIn(5, inArr.buffer, {
outObjects: [out],
});
return new NcmContentMetaDatabase(out);
}

#srv: Switch.Service;

/**
* @private
*/
constructor(srv: Switch.Service) {
this.#srv = srv;
}

set(key: NcmContentMetaKey, data: ArrayBuffer) {
//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 },
// .buffers = { { data, data_size } },
// );
//}
this.#srv.dispatchIn(0, key, {
bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.In],
buffers: [data],
});
}

get(key: NcmContentMetaKey, data: ArrayBuffer): 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 },
// .buffers = { { out_data, out_data_size } },
// );
//}
const out = new BigUint64Array(1);
this.#srv.dispatchInOut(1, key, out.buffer, {
bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.Out],
buffers: [data],
});
return out[0];
}

delete(key: NcmContentMetaKey) {
//Result ncmContentMetaDatabaseRemove(NcmContentMetaDatabase* db, const NcmContentMetaKey *key) {
// return serviceDispatchIn(&db->s, 2, *key);
//}
this.#srv.dispatchIn(2, key);
}

getContentIdByType(
key: NcmContentMetaKey,
type: NcmContentType,
): NcmContentId {
//Result ncmContentMetaDatabaseGetContentIdByType(NcmContentMetaDatabase* db, NcmContentId* out_content_id, const NcmContentMetaKey* key, NcmContentType type) {
// const struct {
// u8 type;
// u8 padding[7];
// NcmContentMetaKey key;
// } 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);
this.#srv.dispatchInOut(3, inData, out);
return out as NcmContentId;
}

has(key: NcmContentMetaKey): boolean {
//Result ncmContentMetaDatabaseHas(NcmContentMetaDatabase* db, bool* out, const NcmContentMetaKey* key) {
// u8 tmp=0;
// Result rc = serviceDispatchInOut(&db->s, 8, *key, tmp);
// if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
// return rc;
//}
const out = new Uint8Array(1);
this.#srv.dispatchInOut(8, key, out.buffer);
return Boolean(out[0] & 1);
}

hasAll(keys: NcmContentMetaKey[]): boolean {
//Result ncmContentMetaDatabaseHasAll(NcmContentMetaDatabase* db, bool* out, const NcmContentMetaKey* keys, s32 count) {
// u8 tmp=0;
// Result rc = serviceDispatchOut(&db->s, 9, *out,
// .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
// .buffers = { { keys, count*sizeof(NcmContentMetaKey) } },
// );
// if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
// return rc;
//}
const inData = new ArrayBuffer(keys.length * 0x10);
const inArr = new Uint8Array(inData);
for (let i = 0; i < keys.length; i++) {
inArr.set(new Uint8Array(keys[i]), i * 0x10);
}
const out = new Uint8Array(1);
this.#srv.dispatchOut(9, out.buffer, {
bufferAttrs: [SfBufferAttr.HipcMapAlias | SfBufferAttr.In],
buffers: [inData],
});
return Boolean(out[0] & 1);
}

getSize(key: NcmContentMetaKey): bigint {
//Result ncmContentMetaDatabaseGetSize(NcmContentMetaDatabase* db, u64* out_size, const NcmContentMetaKey* key) {
// return serviceDispatchInOut(&db->s, 10, *key, *out_size);
//}
const out = new BigUint64Array(1);
this.#srv.dispatchInOut(10, key, out.buffer);
return out[0];
}

commit() {
//Result ncmContentMetaDatabaseCommit(NcmContentMetaDatabase* db) {
// return _ncmCmdNoIO(&db->s, 15);
//}
this.#srv.dispatch(15);
}
}
Loading

0 comments on commit 01a13f0

Please sign in to comment.