code wiki / (root) / nx_mod_manifest.nx

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}