nx_mod_manifest.nx source
↩ module page · 117 lines · 7015 B
1// nx_mod_manifest.nx -- THE SOVEREIGN MOD PACKAGE COMMAND (/compare/modding MD18 mod_manifest_emit, 2026-09-06). A thin caller
2// of nx_mod_manifest_lib so the command line, the installer and the gate prove the SAME chain.
3// usage: nx_mod_manifest emit <out.nxmod> name=<n> version=<v> game=<a,b> licence=<id> [dep=<n@v>,<n@v>] [jrnl=<p>] [root=<dir>] <rel-path>...
4// nx_mod_manifest verify <manifest.nxmod> [root=<dir>]
5// exits: 0 emitted / ACCEPT | 2 usage | 3 the manifest or a member could not be read (named) | 4 REFUSED (the licence is not a
6// rights-table row, a member path is unsafe, a member is MISSING or TAMPERED, or the audit head does not match)
7// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_mod_manifest_lib.nx"
10
11const MMC_EXIT_USAGE: i64 = 2
12const MMC_EXIT_UNREADABLE: i64 = 3
13const MMC_EXIT_REFUSE: i64 = 4
14const MMC_OUTFD: i64 = 1
15const MMC_KV_CAP: i64 = 64
16
17func mmc_w(s: *u8) -> i64 { sys_write(MMC_OUTFD, s, mm_len(s)); return 0 }
18func mmc_wn(v: i64) -> i64 { let t: *u8 = sys_mmap(32); var o: i64 = 0; var x: i64 = v; if x < 0 { t[0] = 45 as u8; o = 1; x = 0 - x } o = mm_putd(t, o, x); sys_write(MMC_OUTFD, t, o); return 0 }
19func mmc_prefix(s: *u8, p: *u8) -> i64 { var i: i64 = 0; while p[i] != (0 as u8) { if s[i] != p[i] { return 0 } i = i + 1 } return 1 }
20// key=value scan over argv (positional contract untouched); returns the value or the default
21func mmc_kv(argc: i64, argv: *i64, key: *u8, dflt: *u8) -> *u8 {
22 var i: i64 = 2
23 while i < argc { let a: *u8 = argv[i] as *u8; if mmc_prefix(a, key) == 1 { return a + mm_len(key) } i = i + 1 }
24 return dflt
25}
26func mmc_is_kv(a: *u8) -> i64 {
27 if mmc_prefix(a, "name=" as *u8) == 1 { return 1 }
28 if mmc_prefix(a, "version=" as *u8) == 1 { return 1 }
29 if mmc_prefix(a, "game=" as *u8) == 1 { return 1 }
30 if mmc_prefix(a, "licence=" as *u8) == 1 { return 1 }
31 if mmc_prefix(a, "dep=" as *u8) == 1 { return 1 }
32 if mmc_prefix(a, "jrnl=" as *u8) == 1 { return 1 }
33 if mmc_prefix(a, "root=" as *u8) == 1 { return 1 }
34 return 0
35}
36func mmc_usage() -> i64 {
37 mmc_w("usage: nx_mod_manifest emit <out.nxmod> name=<n> version=<v> game=<a,b> licence=<id> [dep=<n@v>,<n@v>] [jrnl=<p>] [root=<dir>] <rel-path>... | verify <manifest.nxmod> [root=<dir>]\n" as *u8)
38 return MMC_EXIT_USAGE
39}
40func main(argc: i64, argv: *i64) -> i64 {
41 if argc < 3 { return mmc_usage() }
42 let verb: *u8 = argv[1] as *u8
43 let root: *u8 = mmc_kv(argc, argv, "root=" as *u8, "" as *u8)
44 if mm_streq(verb, "emit" as *u8) == 1 {
45 let outp: *u8 = argv[2] as *u8
46 let name: *u8 = mmc_kv(argc, argv, "name=" as *u8, "" as *u8)
47 let version: *u8 = mmc_kv(argc, argv, "version=" as *u8, "" as *u8)
48 let games: *u8 = mmc_kv(argc, argv, "game=" as *u8, "" as *u8)
49 let lic: *u8 = mmc_kv(argc, argv, "licence=" as *u8, "" as *u8)
50 let deps: *u8 = mmc_kv(argc, argv, "dep=" as *u8, "" as *u8)
51 let jrnl: *u8 = mmc_kv(argc, argv, "jrnl=" as *u8, PV_JRNL_DEFAULT)
52 if name[0] == (0 as u8) { mmc_w("MOD-REFUSE name= is required\n" as *u8); return mmc_usage() }
53 if version[0] == (0 as u8) { mmc_w("MOD-REFUSE version= is required\n" as *u8); return mmc_usage() }
54 if games[0] == (0 as u8) { mmc_w("MOD-REFUSE game= is required\n" as *u8); return mmc_usage() }
55 if lic[0] == (0 as u8) { mmc_w("MOD-REFUSE licence= is required\n" as *u8); return mmc_usage() }
56 let paths: *i64 = sys_mmap(8 * (argc + 1)) as *i64
57 var n: i64 = 0
58 var i: i64 = 3
59 while i < argc { let a: *u8 = argv[i] as *u8; if mmc_is_kv(a) == 0 { paths[n] = a as i64; n = n + 1 } i = i + 1 }
60 let head: *u8 = sys_mmap(MM_SHA_HEX + 1)
61 let info: *i64 = sys_mmap(8 * 8) as *i64
62 let rc: i64 = mod_manifest_emit(outp, name, version, games, deps, lic, jrnl, root, paths, n, head, info)
63 if rc < 0 {
64 mmc_w("MOD-REFUSE " as *u8); mmc_w(mm_err_name(rc))
65 if info[5] >= 0 { mmc_w(" member=" as *u8); mmc_w(paths[info[5]] as *u8) }
66 if rc == MM_ERR_LICENCE_UNKNOWN { mmc_w(" id=" as *u8); mmc_w(lic); mmc_w(" (a licence is a rights-table row; unknown is the absence of one)" as *u8) }
67 mmc_w("\n" as *u8)
68 if rc == MM_ERR_UNREADABLE_FILE { return MMC_EXIT_UNREADABLE }
69 if rc == MM_ERR_TABLE_UNREADABLE { return MMC_EXIT_UNREADABLE }
70 if rc == MM_ERR_OUT_UNWRITABLE { return MMC_EXIT_UNREADABLE }
71 return MMC_EXIT_REFUSE
72 }
73 mmc_w("MOD-MANIFEST name=" as *u8); mmc_w(name)
74 mmc_w(" version=" as *u8); mmc_w(version)
75 mmc_w(" games=" as *u8); mmc_wn(info[3])
76 mmc_w(" deps=" as *u8); mmc_wn(info[4])
77 mmc_w(" licence=" as *u8); mmc_w(lic)
78 mmc_w(" redist=" as *u8); mmc_wn(info[1])
79 mmc_w(" verified=" as *u8); mmc_wn(info[2])
80 mmc_w(" files=" as *u8); mmc_wn(n)
81 mmc_w(" bytes=" as *u8); mmc_wn(info[0])
82 mmc_w(" head=" as *u8); mmc_w(head)
83 mmc_w(" manifest_bytes=" as *u8); mmc_wn(rc)
84 mmc_w(" out=" as *u8); mmc_w(outp); mmc_w("\n" as *u8)
85 return 0
86 }
87 if mm_streq(verb, "verify" as *u8) == 1 {
88 let mp: *u8 = argv[2] as *u8
89 let lp: *i64 = sys_mmap(16) as *i64
90 let b: *u8 = sys_read_file(mp, lp)
91 if (b as i64) == 0 { mmc_w("MOD-REFUSE manifest-unreadable " as *u8); mmc_w(mp); mmc_w("\n" as *u8); return MMC_EXIT_UNREADABLE }
92 let m: *i64 = sys_mmap(8 * MM_M_N_SLOTS) as *i64
93 let pr: i64 = mm_parse(b, lp[0], m)
94 if pr < 0 { mmc_w("MOD-REFUSE " as *u8); mmc_w(mm_err_name(pr)); mmc_w("\n" as *u8); return MMC_EXIT_REFUSE }
95 let r: *i64 = sys_mmap(8 * MM_R_N) as *i64
96 let v: i64 = mm_verify(m, root, r)
97 let states: *i64 = r[MM_R_STATES] as *i64
98 let paths: *i64 = m[MM_M_PATHS] as *i64
99 var i: i64 = 0
100 while i < m[MM_M_NCONTENT] { mmc_w("MOD-MEMBER path=" as *u8); mmc_w(paths[i] as *u8); mmc_w(" status=" as *u8); mmc_w(mm_state_name(states[i])); mmc_w("\n" as *u8); i = i + 1 }
101 mmc_w("MOD-VERIFY name=" as *u8); mmc_w(m[MM_M_NAME] as *u8)
102 mmc_w(" version=" as *u8); mmc_w(m[MM_M_VERSION] as *u8)
103 mmc_w(" licence=" as *u8); mmc_w(m[MM_M_LIC] as *u8)
104 mmc_w(" redist=" as *u8); mmc_wn(m[MM_M_LIC_REDIST])
105 mmc_w(" files=" as *u8); mmc_wn(m[MM_M_NCONTENT])
106 mmc_w(" ok=" as *u8); mmc_wn(r[MM_R_OK])
107 mmc_w(" tampered=" as *u8); mmc_wn(r[MM_R_TAMPERED])
108 mmc_w(" missing=" as *u8); mmc_wn(r[MM_R_MISSING])
109 mmc_w(" head_declared=" as *u8); mmc_w(r[MM_R_HEAD_DECLARED] as *u8)
110 mmc_w(" head_recomputed=" as *u8); mmc_w(r[MM_R_HEAD_RECOMPUTED] as *u8)
111 mmc_w(" head_of_files=" as *u8); mmc_w(r[MM_R_HEAD_OF_FILES] as *u8)
112 mmc_w(" verdict=" as *u8); mmc_w(mm_verdict_name(v)); mmc_w("\n" as *u8)
113 if v == MM_V_ACCEPT { return 0 }
114 return MMC_EXIT_REFUSE
115 }
116 return mmc_usage()
117}