@tootallnate/cnmt 0.0.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.
@@ -0,0 +1,4 @@
1
+
2
+ > @tootallnate/cnmt@0.0.0 build /Users/nrajlich/Code/TooTallNate/switch-tools/packages/cnmt
3
+ > tsc
4
+
@@ -0,0 +1,60 @@
1
+ /**
2
+ * CNMT (Content Meta) builder for Nintendo Switch.
3
+ *
4
+ * Builds CNMT binary data used inside Meta NCAs to describe
5
+ * the contents (NCAs) of an application package.
6
+ *
7
+ * Reference: hacbrewpack/cnmt.c, hacbrewpack/cnmt.h
8
+ */
9
+ /** CNMT content types */
10
+ export declare enum ContentType {
11
+ Meta = 0,
12
+ Program = 1,
13
+ Data = 2,
14
+ Control = 3,
15
+ HtmlDocument = 4,
16
+ LegalInformation = 5,
17
+ DeltaFragment = 6
18
+ }
19
+ /** CNMT meta type */
20
+ export declare enum MetaType {
21
+ SystemProgram = 1,
22
+ SystemData = 2,
23
+ SystemUpdate = 3,
24
+ BootImagePackage = 4,
25
+ BootImagePackageSafe = 5,
26
+ Application = 128,
27
+ Patch = 129,
28
+ AddOnContent = 130,
29
+ Delta = 131
30
+ }
31
+ /**
32
+ * A content record describing one NCA.
33
+ */
34
+ export interface ContentRecord {
35
+ /** SHA-256 hash of the entire NCA file (32 bytes) */
36
+ hash: Uint8Array;
37
+ /** NCA ID: first 16 bytes of the hash */
38
+ ncaId: Uint8Array;
39
+ /** NCA file size (up to 6 bytes / 48 bits) */
40
+ size: number;
41
+ /** Content type */
42
+ type: ContentType;
43
+ /** ID offset (usually 0) */
44
+ idOffset?: number;
45
+ }
46
+ export interface CnmtOptions {
47
+ /** Application title ID */
48
+ titleId: bigint;
49
+ /** Title version (default: 0) */
50
+ titleVersion?: number;
51
+ /** Content records for the NCAs in this package */
52
+ contentRecords: ContentRecord[];
53
+ }
54
+ /**
55
+ * Build a CNMT binary blob.
56
+ *
57
+ * @param options - CNMT configuration
58
+ * @returns CNMT binary data as ArrayBuffer
59
+ */
60
+ export declare function build(options: CnmtOptions): ArrayBuffer;
package/dist/index.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * CNMT (Content Meta) builder for Nintendo Switch.
3
+ *
4
+ * Builds CNMT binary data used inside Meta NCAs to describe
5
+ * the contents (NCAs) of an application package.
6
+ *
7
+ * Reference: hacbrewpack/cnmt.c, hacbrewpack/cnmt.h
8
+ */
9
+ /** CNMT content types */
10
+ export var ContentType;
11
+ (function (ContentType) {
12
+ ContentType[ContentType["Meta"] = 0] = "Meta";
13
+ ContentType[ContentType["Program"] = 1] = "Program";
14
+ ContentType[ContentType["Data"] = 2] = "Data";
15
+ ContentType[ContentType["Control"] = 3] = "Control";
16
+ ContentType[ContentType["HtmlDocument"] = 4] = "HtmlDocument";
17
+ ContentType[ContentType["LegalInformation"] = 5] = "LegalInformation";
18
+ ContentType[ContentType["DeltaFragment"] = 6] = "DeltaFragment";
19
+ })(ContentType || (ContentType = {}));
20
+ /** CNMT meta type */
21
+ export var MetaType;
22
+ (function (MetaType) {
23
+ MetaType[MetaType["SystemProgram"] = 1] = "SystemProgram";
24
+ MetaType[MetaType["SystemData"] = 2] = "SystemData";
25
+ MetaType[MetaType["SystemUpdate"] = 3] = "SystemUpdate";
26
+ MetaType[MetaType["BootImagePackage"] = 4] = "BootImagePackage";
27
+ MetaType[MetaType["BootImagePackageSafe"] = 5] = "BootImagePackageSafe";
28
+ MetaType[MetaType["Application"] = 128] = "Application";
29
+ MetaType[MetaType["Patch"] = 129] = "Patch";
30
+ MetaType[MetaType["AddOnContent"] = 130] = "AddOnContent";
31
+ MetaType[MetaType["Delta"] = 131] = "Delta";
32
+ })(MetaType || (MetaType = {}));
33
+ /** CNMT header size: 0x20 bytes */
34
+ const HEADER_SIZE = 0x20;
35
+ /** Extended application header size: 0x10 bytes */
36
+ const EXTENDED_APP_HEADER_SIZE = 0x10;
37
+ /** Content record size: 0x38 bytes */
38
+ const CONTENT_RECORD_SIZE = 0x38;
39
+ /** Digest size: 0x20 bytes */
40
+ const DIGEST_SIZE = 0x20;
41
+ /**
42
+ * Build a CNMT binary blob.
43
+ *
44
+ * @param options - CNMT configuration
45
+ * @returns CNMT binary data as ArrayBuffer
46
+ */
47
+ export function build(options) {
48
+ const { titleId, titleVersion = 0, contentRecords } = options;
49
+ // Total size = header + extended header + content records + digest
50
+ const totalSize = HEADER_SIZE +
51
+ EXTENDED_APP_HEADER_SIZE +
52
+ contentRecords.length * CONTENT_RECORD_SIZE +
53
+ DIGEST_SIZE;
54
+ const buffer = new ArrayBuffer(totalSize);
55
+ const view = new DataView(buffer);
56
+ const bytes = new Uint8Array(buffer);
57
+ let offset = 0;
58
+ // --- CNMT Header (0x20 bytes) ---
59
+ // title_id (8 bytes)
60
+ view.setBigUint64(offset + 0x00, titleId, true);
61
+ // title_version (4 bytes)
62
+ view.setUint32(offset + 0x08, titleVersion, true);
63
+ // type (1 byte)
64
+ view.setUint8(offset + 0x0c, MetaType.Application);
65
+ // padding (1 byte) — already zero
66
+ // extended_header_size (2 bytes)
67
+ view.setUint16(offset + 0x0e, EXTENDED_APP_HEADER_SIZE, true);
68
+ // content_entry_count (2 bytes)
69
+ view.setUint16(offset + 0x10, contentRecords.length, true);
70
+ // meta_entry_count (2 bytes) — 0
71
+ // padding (remaining bytes of header) — already zero
72
+ offset += HEADER_SIZE;
73
+ // --- Extended Application Header (0x10 bytes) ---
74
+ // patch_title_id = title_id + 0x800
75
+ view.setBigUint64(offset + 0x00, titleId + 0x800n, true);
76
+ // required_system_version (4 bytes) — 0
77
+ // padding (4 bytes) — already zero
78
+ offset += EXTENDED_APP_HEADER_SIZE;
79
+ // --- Content Records (0x38 bytes each) ---
80
+ for (const record of contentRecords) {
81
+ // hash (0x20 bytes)
82
+ bytes.set(record.hash.subarray(0, 0x20), offset + 0x00);
83
+ // ncaid (0x10 bytes)
84
+ bytes.set(record.ncaId.subarray(0, 0x10), offset + 0x20);
85
+ // size (6 bytes, little-endian)
86
+ const size = record.size;
87
+ view.setUint32(offset + 0x30, size & 0xffffffff, true);
88
+ view.setUint16(offset + 0x34, Math.floor(size / 0x100000000) & 0xffff, true);
89
+ // type (1 byte)
90
+ view.setUint8(offset + 0x36, record.type);
91
+ // id_offset (1 byte)
92
+ view.setUint8(offset + 0x37, record.idOffset ?? 0);
93
+ offset += CONTENT_RECORD_SIZE;
94
+ }
95
+ // --- Digest (0x20 bytes) ---
96
+ // All zeros (already zeroed from ArrayBuffer allocation)
97
+ return buffer;
98
+ }
99
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@tootallnate/cnmt",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "CNMT (Content Meta) builder for Nintendo Switch NCAs",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "vitest run"
10
+ },
11
+ "keywords": [],
12
+ "author": "Nathan Rajlich <n@n8.io>",
13
+ "license": "MIT",
14
+ "devDependencies": {
15
+ "typescript": "^5.3.3"
16
+ }
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * CNMT (Content Meta) builder for Nintendo Switch.
3
+ *
4
+ * Builds CNMT binary data used inside Meta NCAs to describe
5
+ * the contents (NCAs) of an application package.
6
+ *
7
+ * Reference: hacbrewpack/cnmt.c, hacbrewpack/cnmt.h
8
+ */
9
+
10
+ /** CNMT content types */
11
+ export enum ContentType {
12
+ Meta = 0,
13
+ Program = 1,
14
+ Data = 2,
15
+ Control = 3,
16
+ HtmlDocument = 4,
17
+ LegalInformation = 5,
18
+ DeltaFragment = 6,
19
+ }
20
+
21
+ /** CNMT meta type */
22
+ export enum MetaType {
23
+ SystemProgram = 0x01,
24
+ SystemData = 0x02,
25
+ SystemUpdate = 0x03,
26
+ BootImagePackage = 0x04,
27
+ BootImagePackageSafe = 0x05,
28
+ Application = 0x80,
29
+ Patch = 0x81,
30
+ AddOnContent = 0x82,
31
+ Delta = 0x83,
32
+ }
33
+
34
+ /** CNMT header size: 0x20 bytes */
35
+ const HEADER_SIZE = 0x20;
36
+
37
+ /** Extended application header size: 0x10 bytes */
38
+ const EXTENDED_APP_HEADER_SIZE = 0x10;
39
+
40
+ /** Content record size: 0x38 bytes */
41
+ const CONTENT_RECORD_SIZE = 0x38;
42
+
43
+ /** Digest size: 0x20 bytes */
44
+ const DIGEST_SIZE = 0x20;
45
+
46
+ /**
47
+ * A content record describing one NCA.
48
+ */
49
+ export interface ContentRecord {
50
+ /** SHA-256 hash of the entire NCA file (32 bytes) */
51
+ hash: Uint8Array;
52
+ /** NCA ID: first 16 bytes of the hash */
53
+ ncaId: Uint8Array;
54
+ /** NCA file size (up to 6 bytes / 48 bits) */
55
+ size: number;
56
+ /** Content type */
57
+ type: ContentType;
58
+ /** ID offset (usually 0) */
59
+ idOffset?: number;
60
+ }
61
+
62
+ export interface CnmtOptions {
63
+ /** Application title ID */
64
+ titleId: bigint;
65
+ /** Title version (default: 0) */
66
+ titleVersion?: number;
67
+ /** Content records for the NCAs in this package */
68
+ contentRecords: ContentRecord[];
69
+ }
70
+
71
+ /**
72
+ * Build a CNMT binary blob.
73
+ *
74
+ * @param options - CNMT configuration
75
+ * @returns CNMT binary data as ArrayBuffer
76
+ */
77
+ export function build(options: CnmtOptions): ArrayBuffer {
78
+ const { titleId, titleVersion = 0, contentRecords } = options;
79
+
80
+ // Total size = header + extended header + content records + digest
81
+ const totalSize =
82
+ HEADER_SIZE +
83
+ EXTENDED_APP_HEADER_SIZE +
84
+ contentRecords.length * CONTENT_RECORD_SIZE +
85
+ DIGEST_SIZE;
86
+
87
+ const buffer = new ArrayBuffer(totalSize);
88
+ const view = new DataView(buffer);
89
+ const bytes = new Uint8Array(buffer);
90
+
91
+ let offset = 0;
92
+
93
+ // --- CNMT Header (0x20 bytes) ---
94
+ // title_id (8 bytes)
95
+ view.setBigUint64(offset + 0x00, titleId, true);
96
+ // title_version (4 bytes)
97
+ view.setUint32(offset + 0x08, titleVersion, true);
98
+ // type (1 byte)
99
+ view.setUint8(offset + 0x0c, MetaType.Application);
100
+ // padding (1 byte) — already zero
101
+ // extended_header_size (2 bytes)
102
+ view.setUint16(offset + 0x0e, EXTENDED_APP_HEADER_SIZE, true);
103
+ // content_entry_count (2 bytes)
104
+ view.setUint16(offset + 0x10, contentRecords.length, true);
105
+ // meta_entry_count (2 bytes) — 0
106
+ // padding (remaining bytes of header) — already zero
107
+
108
+ offset += HEADER_SIZE;
109
+
110
+ // --- Extended Application Header (0x10 bytes) ---
111
+ // patch_title_id = title_id + 0x800
112
+ view.setBigUint64(offset + 0x00, titleId + 0x800n, true);
113
+ // required_system_version (4 bytes) — 0
114
+ // padding (4 bytes) — already zero
115
+
116
+ offset += EXTENDED_APP_HEADER_SIZE;
117
+
118
+ // --- Content Records (0x38 bytes each) ---
119
+ for (const record of contentRecords) {
120
+ // hash (0x20 bytes)
121
+ bytes.set(record.hash.subarray(0, 0x20), offset + 0x00);
122
+ // ncaid (0x10 bytes)
123
+ bytes.set(record.ncaId.subarray(0, 0x10), offset + 0x20);
124
+ // size (6 bytes, little-endian)
125
+ const size = record.size;
126
+ view.setUint32(offset + 0x30, size & 0xffffffff, true);
127
+ view.setUint16(
128
+ offset + 0x34,
129
+ Math.floor(size / 0x100000000) & 0xffff,
130
+ true
131
+ );
132
+ // type (1 byte)
133
+ view.setUint8(offset + 0x36, record.type);
134
+ // id_offset (1 byte)
135
+ view.setUint8(offset + 0x37, record.idOffset ?? 0);
136
+
137
+ offset += CONTENT_RECORD_SIZE;
138
+ }
139
+
140
+ // --- Digest (0x20 bytes) ---
141
+ // All zeros (already zeroed from ArrayBuffer allocation)
142
+
143
+ return buffer;
144
+ }
@@ -0,0 +1,105 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { build, ContentType, MetaType } from '../src/index.js';
3
+
4
+ function bytesToHex(bytes: Uint8Array): string {
5
+ return Array.from(bytes)
6
+ .map((b) => b.toString(16).padStart(2, '0'))
7
+ .join('');
8
+ }
9
+
10
+ describe('CNMT builder', () => {
11
+ it('should build a CNMT with correct header', () => {
12
+ const titleId = 0x0100000000001000n;
13
+ const fakeHash = new Uint8Array(32);
14
+ for (let i = 0; i < 32; i++) fakeHash[i] = i;
15
+ const fakeNcaId = fakeHash.subarray(0, 16);
16
+
17
+ const cnmt = build({
18
+ titleId,
19
+ contentRecords: [
20
+ {
21
+ hash: fakeHash,
22
+ ncaId: fakeNcaId,
23
+ size: 0x100000,
24
+ type: ContentType.Program,
25
+ },
26
+ {
27
+ hash: fakeHash,
28
+ ncaId: fakeNcaId,
29
+ size: 0x80000,
30
+ type: ContentType.Control,
31
+ },
32
+ ],
33
+ });
34
+
35
+ const view = new DataView(cnmt);
36
+ const bytes = new Uint8Array(cnmt);
37
+
38
+ // Header (0x20 bytes)
39
+ expect(view.getBigUint64(0x00, true)).toBe(titleId);
40
+ expect(view.getUint32(0x08, true)).toBe(0); // title_version
41
+ expect(view.getUint8(0x0c)).toBe(MetaType.Application);
42
+ expect(view.getUint16(0x0e, true)).toBe(0x10); // extended_header_size
43
+ expect(view.getUint16(0x10, true)).toBe(2); // content_entry_count
44
+
45
+ // Extended header (0x10 bytes starting at 0x20)
46
+ expect(view.getBigUint64(0x20, true)).toBe(titleId + 0x800n); // patch_title_id
47
+
48
+ // Content record 0 (at offset 0x30, 0x38 bytes)
49
+ expect(view.getUint8(0x30 + 0x36)).toBe(ContentType.Program);
50
+
51
+ // Content record 1 (at offset 0x30 + 0x38 = 0x68)
52
+ expect(view.getUint8(0x68 + 0x36)).toBe(ContentType.Control);
53
+
54
+ // Total size = 0x20 (header) + 0x10 (ext header) + 2 * 0x38 (records) + 0x20 (digest)
55
+ expect(cnmt.byteLength).toBe(0x20 + 0x10 + 2 * 0x38 + 0x20);
56
+ });
57
+
58
+ it('should store NCA size as 6-byte little-endian', () => {
59
+ const fakeHash = new Uint8Array(32).fill(0xab);
60
+ const fakeNcaId = fakeHash.subarray(0, 16);
61
+ const size = 0x0102030405; // 5-byte value
62
+
63
+ const cnmt = build({
64
+ titleId: 0x0100000000001000n,
65
+ contentRecords: [
66
+ {
67
+ hash: fakeHash,
68
+ ncaId: fakeNcaId,
69
+ size,
70
+ type: ContentType.Program,
71
+ },
72
+ ],
73
+ });
74
+
75
+ const view = new DataView(cnmt);
76
+ const recordOffset = 0x30; // header + ext header
77
+
78
+ // Read the 6-byte size at record + 0x30
79
+ const low = view.getUint32(recordOffset + 0x30, true);
80
+ const high = view.getUint16(recordOffset + 0x34, true);
81
+ const reconstructed = low + high * 0x100000000;
82
+ expect(reconstructed).toBe(size);
83
+ });
84
+
85
+ it('should end with a 0x20-byte zero digest', () => {
86
+ const fakeHash = new Uint8Array(32).fill(0xff);
87
+ const fakeNcaId = fakeHash.subarray(0, 16);
88
+
89
+ const cnmt = build({
90
+ titleId: 0x0100000000001000n,
91
+ contentRecords: [
92
+ {
93
+ hash: fakeHash,
94
+ ncaId: fakeNcaId,
95
+ size: 100,
96
+ type: ContentType.Program,
97
+ },
98
+ ],
99
+ });
100
+
101
+ const bytes = new Uint8Array(cnmt);
102
+ const digest = bytes.subarray(bytes.length - 0x20);
103
+ expect(bytesToHex(digest)).toBe('00'.repeat(0x20));
104
+ });
105
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "outDir": "./dist",
7
+ "declaration": true,
8
+ "sourceMap": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "strict": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }