code wiki / (root) / nx_nif_gate.nx

nx_nif_gate.nx source

↩ module page · 349 lines · 17835 B

1// nx_nif_gate.nx -- BITE-PROOF for the NIF structural probe + geometry leg, both directions. 2// 3// IT DRIVES THE SHIPPING CODE: every tooth calls nx_nif_lib directly -- the same nif_probe, 4// nif_types and nif_geom the CLI calls. FIXTURES ARE ASSEMBLED AT RUNTIME under /tmp/<gate>/ by a 5// byte builder (binary format; no literal fixture bytes exist in this source). Idempotent. 6// 7// FIXTURE HONESTY, DECLARED: the good fixtures are SYNTHETIC minimal modern-layout NIFs derived 8// from the mirrored nif.xml and the hex-measured real SE + FO76 files; the REAL corpus (nifly test 9// nifs, hex-verified 2026-08-30) rides the CLI smoke and the banked-mirror watch row. 10// 11// license_tier: ORIGINAL expect_exit: 0 12import "nx_syscalls.nx" 13import "nx_gate_verdict.nx" 14import "nx_nif_lib.nx" 15 16const NG_DIR: *u8 = "/tmp/nx_nif_gate" as *u8 17const NG_F_GOOD: *u8 = "/tmp/nx_nif_gate/good.nif" as *u8 18const NG_F_BS155: *u8 = "/tmp/nx_nif_gate/bs155.nif" as *u8 19const NG_F_OOB: *u8 = "/tmp/nx_nif_gate/oob.nif" as *u8 20const NG_F_JUNK: *u8 = "/tmp/nx_nif_gate/junk.bin" as *u8 21const NG_F_OLDVER: *u8 = "/tmp/nx_nif_gate/oldver.nif" as *u8 22const NG_F_ABSURD: *u8 = "/tmp/nx_nif_gate/absurd.nif" as *u8 23const NG_F_EMPTY: *u8 = "/tmp/nx_nif_gate/empty.nif" as *u8 24const NG_BUFCAP: i64 = 8192 25const NG_VER_OLD: i64 = 335544323 // 0x14000003 = 20.0.0.3 -- recognized magic, unsupported layout 26// the geometry block: desc low nibble 5 dwords/vert, 2 verts, 1 tri -> datasize 5*2*4 + 1*6 = 46 27const NG_GEO_DESCLO: i64 = 5 28const NG_GEO_VERTS: i64 = 2 29const NG_GEO_TRIS: i64 = 1 30const NG_GEO_DSIZE: i64 = 46 31const NG_GEO_BLOCK: i64 = 162 // 100 prefix (0 extras) + 8 desc + 2 tri + 2 vert + 4 dsize + 46 data 32const NG_F32_ONE: i64 = 1065353216 // 1.0f -- vertex 0 x 33const NG_F32_ONEP5: i64 = 1069547520 // 1.5f -- vertex 1 y 34const NG_F32_TWO: i64 = 1073741824 // 2.0f -- the good NiBound radius (covers both verts) 35const NG_F32_HALF: i64 = 1056964608 // 0.5f -- the out-of-bound radius (covers neither) 36const NG_BLOCKBYTES: i64 = 212 // 20 (NiNode) + 162 (real BSTriShape) + 30 (undersized BSTriShape) 37 38func ng_u8(dst: *u8, at: i64, v: i64) -> i64 { dst[at] = v as u8; return at + 1 } 39func ng_u16(dst: *u8, at: i64, v: i64) -> i64 { dst[at] = (v & 0xff) as u8; dst[at + 1] = ((v >> 8) & 0xff) as u8; return at + 2 } 40func ng_u32(dst: *u8, at: i64, v: i64) -> i64 { 41 dst[at] = (v & 0xff) as u8 42 dst[at + 1] = ((v >> 8) & 0xff) as u8 43 dst[at + 2] = ((v >> 16) & 0xff) as u8 44 dst[at + 3] = ((v >> 24) & 0xff) as u8 45 return at + 4 46} 47func ng_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 48func ng_raw(dst: *u8, at: i64, s: *u8) -> i64 { 49 var i: i64 = 0 50 var o: i64 = at 51 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } 52 return o 53} 54func ng_sized(dst: *u8, at: i64, s: *u8) -> i64 { 55 var o: i64 = ng_u32(dst, at, ng_slen(s)) 56 return ng_raw(dst, o, s) 57} 58// export string as observed on the real SE file: length byte counts the terminating nul 59func ng_export_empty(dst: *u8, at: i64) -> i64 { 60 var o: i64 = ng_u8(dst, at, 1) 61 return ng_u8(dst, o, 0) 62} 63func ng_zeros(dst: *u8, at: i64, count: i64) -> i64 { 64 var i: i64 = 0 65 var o: i64 = at 66 while i < count { dst[o] = 0 as u8; o = o + 1; i = i + 1 } 67 return o 68} 69func ng_write(path: *u8, body: *u8, n: i64) -> i64 { 70 sys_unlinkat(path) 71 let fd: i64 = sys_openat_wr(path, MODE_0644) 72 if fd < 0 { return 0 - 1 } 73 if n > 0 { sys_write(fd, body, n) } 74 sys_close(fd) 75 return n 76} 77 78// minimal modern NIF: 3 blocks over 2 types (NiNode, BSTriShape x2), 1 string, 0 groups. 79// Block 1 carries a REAL BSTriShape tuple satisfying the nif.xml datasize formula; block 2 is an 80// undersized BSTriShape that the geometry leg must count UNVERIFIED. 81func ng_build_good(dst: *u8, ver: i64, rbits: i64) -> i64 { 82 var o: i64 = ng_raw(dst, 0, "Gamebryo File Format, Version 20.2.0.7" as *u8) 83 o = ng_u8(dst, o, NF_CH_NL) 84 o = ng_u32(dst, o, ver) 85 o = ng_u8(dst, o, 1) // little endian 86 o = ng_u32(dst, o, 12) // user version 87 o = ng_u32(dst, o, 3) // num blocks 88 o = ng_u32(dst, o, 100) // BS version (SSE): Author + ProcessScript + ExportScript, no MaxFilepath 89 o = ng_export_empty(dst, o) 90 o = ng_export_empty(dst, o) 91 o = ng_export_empty(dst, o) 92 o = ng_u16(dst, o, 2) // num block types 93 o = ng_sized(dst, o, "NiNode" as *u8) 94 o = ng_sized(dst, o, "BSTriShape" as *u8) 95 o = ng_u16(dst, o, 0) // block 0 -> NiNode 96 o = ng_u16(dst, o, 1) // block 1 -> BSTriShape (real tuple) 97 o = ng_u16(dst, o, 1) // block 2 -> BSTriShape (undersized) 98 o = ng_u32(dst, o, 20) 99 o = ng_u32(dst, o, NG_GEO_BLOCK) 100 o = ng_u32(dst, o, 30) 101 o = ng_u32(dst, o, 1) // num strings 102 o = ng_u32(dst, o, 5) // max string length 103 o = ng_sized(dst, o, "hello" as *u8) 104 o = ng_u32(dst, o, 0) // num groups 105 // block 0: NiNode, 20 opaque bytes 106 o = ng_zeros(dst, o, 20) 107 // block 1: BSTriShape -- 72-byte NiAVObject fixed part (num extras at +4 stays 0), then the 108 // block's OWN NiBound oracle (center 0,0,0 + the caller's radius), refs, tuple, and REAL 109 // vertex records: v0 at (1, 0, 0), v1 at (0, 1.5, 0), stride 5 dwords, then the triangle. 110 o = ng_zeros(dst, o, NF_BOUND_OFF) 111 o = ng_u32(dst, o, 0) // bound center x 112 o = ng_u32(dst, o, 0) // bound center y 113 o = ng_u32(dst, o, 0) // bound center z 114 o = ng_u32(dst, o, rbits) // bound radius 115 o = ng_zeros(dst, o, 12) // skin + shader + alpha refs 116 o = ng_u32(dst, o, NG_GEO_DESCLO) // desc low dword (low nibble = dwords per vertex) 117 o = ng_u32(dst, o, 0) // desc high dword 118 o = ng_u16(dst, o, NG_GEO_TRIS) 119 o = ng_u16(dst, o, NG_GEO_VERTS) 120 o = ng_u32(dst, o, NG_GEO_DSIZE) 121 o = ng_u32(dst, o, NG_F32_ONE) // v0 x = 1.0 122 o = ng_u32(dst, o, 0) // v0 y 123 o = ng_u32(dst, o, 0) // v0 z 124 o = ng_zeros(dst, o, 8) // v0 tail of the 5-dword record 125 o = ng_u32(dst, o, 0) // v1 x 126 o = ng_u32(dst, o, NG_F32_ONEP5) // v1 y = 1.5 127 o = ng_u32(dst, o, 0) // v1 z 128 o = ng_zeros(dst, o, 8) // v1 tail 129 o = ng_zeros(dst, o, 6) // the triangle 130 // block 2: BSTriShape but only 30 bytes -- the tuple cannot fit past the prefix 131 o = ng_zeros(dst, o, 30) 132 return o 133} 134// bsver-155 minimal header (FO76 layout: Author + extra u32 + ExportScript + MaxFilepath), 1 block 135func ng_build_bs155(dst: *u8) -> i64 { 136 var o: i64 = ng_raw(dst, 0, "Gamebryo File Format, Version 20.2.0.7" as *u8) 137 o = ng_u8(dst, o, NF_CH_NL) 138 o = ng_u32(dst, o, NF_VER_SSE) 139 o = ng_u8(dst, o, 1) 140 o = ng_u32(dst, o, 12) 141 o = ng_u32(dst, o, 1) // num blocks 142 o = ng_u32(dst, o, 155) // BS version (FO76) 143 o = ng_export_empty(dst, o) // Author 144 o = ng_u32(dst, o, 0) // the extra u32 (BS > 130) 145 o = ng_export_empty(dst, o) // Export Script (no Process Script at BS >= 131) 146 o = ng_export_empty(dst, o) // Max Filepath (BS >= 103) 147 o = ng_u16(dst, o, 1) 148 o = ng_sized(dst, o, "NiNode" as *u8) 149 o = ng_u16(dst, o, 0) 150 o = ng_u32(dst, o, 20) 151 o = ng_u32(dst, o, 0) // num strings 152 o = ng_u32(dst, o, 0) // max string length 153 o = ng_u32(dst, o, 0) // num groups 154 o = ng_zeros(dst, o, 20) 155 return o 156} 157// absurd type-table count spliced in: bounds must refuse, never misparse 158func ng_build_absurd(dst: *u8) -> i64 { 159 var o: i64 = ng_raw(dst, 0, "Gamebryo File Format, Version 20.2.0.7" as *u8) 160 o = ng_u8(dst, o, NF_CH_NL) 161 o = ng_u32(dst, o, NF_VER_SSE) 162 o = ng_u8(dst, o, 1) 163 o = ng_u32(dst, o, 12) 164 o = ng_u32(dst, o, 3) 165 o = ng_u32(dst, o, 100) 166 o = ng_export_empty(dst, o) 167 o = ng_export_empty(dst, o) 168 o = ng_export_empty(dst, o) 169 o = ng_u16(dst, o, 65535) // absurd num block types 170 return o 171} 172 173func main(argc: i64, argv: *i64) -> i64 { 174 let ctr: *i64 = gv_ctr() 175 gv_head("=== NX-NIF GATE -- the Bethesda mesh container must census exactly, and abstain by name on everything else ===" as *u8) 176 177 sys_mkdir(NG_DIR, MODE_0755) 178 let asmbuf: *u8 = sys_mmap(NG_BUFCAP) 179 let sc: *i64 = sys_mmap(64) as *i64 180 181 let n_good: i64 = ng_build_good(asmbuf, NF_VER_SSE, NG_F32_TWO) 182 let w_good: i64 = ng_write(NG_F_GOOD, asmbuf, n_good) 183 let n_oob: i64 = ng_build_good(asmbuf, NF_VER_SSE, NG_F32_HALF) 184 let w_oob: i64 = ng_write(NG_F_OOB, asmbuf, n_oob) 185 let n_155: i64 = ng_build_bs155(asmbuf) 186 let w_155: i64 = ng_write(NG_F_BS155, asmbuf, n_155) 187 let n_old: i64 = ng_build_good(asmbuf, NG_VER_OLD, NG_F32_TWO) 188 let w_old: i64 = ng_write(NG_F_OLDVER, asmbuf, n_old) 189 let n_abs: i64 = ng_build_absurd(asmbuf) 190 let w_abs: i64 = ng_write(NG_F_ABSURD, asmbuf, n_abs) 191 let n_junk: i64 = ng_raw(asmbuf, 0, "these bytes are neither gamebryo nor netimmerse and never will be, padded to pass the length floor" as *u8) 192 let w_junk: i64 = ng_write(NG_F_JUNK, asmbuf, n_junk) 193 let w_empty: i64 = ng_write(NG_F_EMPTY, asmbuf, 0) 194 195 gv_puts(" planted: good=" as *u8); gv_num(w_good) 196 gv_puts(" bs155=" as *u8); gv_num(w_155) 197 gv_puts(" oob=" as *u8); gv_num(w_oob) 198 gv_puts(" oldver=" as *u8); gv_num(w_old) 199 gv_puts(" absurd=" as *u8); gv_num(w_abs) 200 gv_puts(" junk=" as *u8); gv_num(w_junk) 201 gv_puts(" empty=" as *u8); gv_num(w_empty); gv_puts("\n" as *u8) 202 203 var built: i64 = 0 204 if w_good == n_good { if w_good > 300 { if w_155 == n_155 { if w_oob == n_oob { if w_old == n_old { if w_abs == n_abs { 205 if w_junk == n_junk { if w_junk >= 40 { if w_empty == 0 { built = 1 } } } } } } } } } 206 gv_need("every fixture reached its planted condition (written == built, good past 300 B with its geometry block, empty empty)" as *u8, built, ctr) 207 208 let good: *u8 = sys_read_file(NG_F_GOOD, sc) 209 let good_n: i64 = sc[0] 210 let facts: *i64 = sys_mmap(NF_N_SLOTS * 8) as *i64 211 212 // ---- T1 THE BITE 213 nif_probe(good, good_n, good_n, facts) 214 var fired_good: i64 = 0 215 if facts[0] == 1 { fired_good = 1 } 216 let f_ver: i64 = facts[1] 217 let f_bsv: i64 = facts[2] 218 let f_blk: i64 = facts[3] 219 let f_typ: i64 = facts[4] 220 let f_tri: i64 = facts[5] 221 let f_tot: i64 = facts[6] 222 let f_str: i64 = facts[7] 223 let f_hdr: i64 = facts[8] 224 let junk: *u8 = sys_read_file(NG_F_JUNK, sc) 225 let junk_n: i64 = sc[0] 226 nif_probe(junk, junk_n, junk_n, facts) 227 var fired_junk: i64 = 0 228 if facts[0] != 0 { fired_junk = 1 } 229 gv_bite("T1 nif_probe parses the modern container and reads prose as NOT A NIF" as *u8, fired_good, fired_junk, ctr) 230 231 // ---- T2 census exact against the built values 232 gv_puts(" census: ver=" as *u8); gv_num(f_ver) 233 gv_puts(" bsver=" as *u8); gv_num(f_bsv) 234 gv_puts(" blocks=" as *u8); gv_num(f_blk) 235 gv_puts(" types=" as *u8); gv_num(f_typ) 236 gv_puts(" trishapes=" as *u8); gv_num(f_tri) 237 gv_puts(" blockbytes=" as *u8); gv_num(f_tot) 238 gv_puts(" strings=" as *u8); gv_num(f_str) 239 gv_puts(" headerbytes=" as *u8); gv_num(f_hdr); gv_puts("\n" as *u8) 240 var t2: i64 = 0 241 if f_ver == NF_VER_SSE { if f_bsv == 100 { if f_blk == 3 { if f_typ == 2 { if f_str == 1 { t2 = 1 } } } } } 242 gv_check("T2 header census exact: version 20.2.0.7, BS 100, 3 blocks, 2 types, 1 string" as *u8, t2, ctr) 243 244 // ---- T3 the TriShape count discriminates by TYPE, not by block count 245 var t3: i64 = 0 246 if f_tri == 2 { if f_blk == 3 { t3 = 1 } } 247 gv_check("T3 trishape census counts the two BSTriShape blocks and not the NiNode (2 of 3)" as *u8, t3, ctr) 248 249 // ---- T4 the size table sums and the header ends where the payload begins 250 var t4: i64 = 0 251 if f_tot == NG_BLOCKBYTES { if f_hdr > 0 { if f_hdr + f_tot == good_n { t4 = 1 } } } 252 gv_check("T4 block bytes sum to the declared 212 and headerbytes + blockbytes == the file (the partition SUMS)" as *u8, t4, ctr) 253 254 // ---- T5 the type-table verb agrees with the probe 255 let toffs: *i64 = sys_mmap(NF_MAX_TYPES * 8) as *i64 256 let tlens: *i64 = sys_mmap(NF_MAX_TYPES * 8) as *i64 257 let tcounts: *i64 = sys_mmap(NF_MAX_TYPES * 8) as *i64 258 let meta: *i64 = sys_mmap(16) as *i64 259 let st: i64 = nif_types(good, good_n, good_n, toffs, tlens, tcounts, NF_MAX_TYPES, meta) 260 var t5: i64 = 0 261 if st == 1 { if meta[0] == 2 { if tcounts[0] == 1 { if tcounts[1] == 2 { t5 = 1 } } } } 262 gv_check("T5 nif_types agrees: 2 types, NiNode carries 1 block and BSTriShape carries 2" as *u8, t5, ctr) 263 264 // ---- T5g THE GEOMETRY LEG: the schema's own datasize arithmetic separates a true tuple from 265 // an undersized block in the SAME file -- both signals present at once. 266 let vouts: *i64 = sys_mmap(64 * 8) as *i64 267 let touts: *i64 = sys_mmap(64 * 8) as *i64 268 let vflags: *i64 = sys_mmap(64 * 8) as *i64 269 let gmeta: *i64 = sys_mmap(32) as *i64 270 let shapes: i64 = nif_geom(good, good_n, good_n, vouts, touts, vflags, 64, gmeta) 271 gv_puts(" geom: shapes=" as *u8); gv_num(shapes) 272 gv_puts(" verified=" as *u8); gv_num(gmeta[0]) 273 gv_puts(" verts=" as *u8); gv_num(gmeta[1]) 274 gv_puts(" tris=" as *u8); gv_num(gmeta[2]) 275 gv_puts(" flags=" as *u8); gv_num(vflags[0]); gv_puts("," as *u8); gv_num(vflags[1]); gv_puts("\n" as *u8) 276 var t5g: i64 = 0 277 if shapes == 2 { if gmeta[0] == 1 { if gmeta[1] == NG_GEO_VERTS { if gmeta[2] == NG_GEO_TRIS { 278 if vflags[0] == 1 { if vflags[1] == 0 { t5g = 1 } } } } } } 279 gv_check("T5g nif_geom verifies the true tuple by the nif.xml formula (2 verts, 1 tri) and counts the undersized sibling UNVERIFIED" as *u8, t5g, ctr) 280 281 // ---- T5v THE VERTEX LEG: positions decode to exact permil and land inside the block's OWN bound 282 let pxs: *i64 = sys_mmap(64 * 8) as *i64 283 let pys: *i64 = sys_mmap(64 * 8) as *i64 284 let pzs: *i64 = sys_mmap(64 * 8) as *i64 285 let vmeta: *i64 = sys_mmap(32) as *i64 286 let nv: i64 = nif_verts(good, good_n, good_n, 0, pxs, pys, pzs, 64, vmeta) 287 gv_puts(" verts: n=" as *u8); gv_num(nv) 288 gv_puts(" inside=" as *u8); gv_num(vmeta[1]) 289 gv_puts(" radius=" as *u8); gv_num(vmeta[2]) 290 gv_puts(" v0=" as *u8); gv_num(pxs[0]); gv_puts("," as *u8); gv_num(pys[0]); gv_puts("," as *u8); gv_num(pzs[0]) 291 gv_puts(" v1=" as *u8); gv_num(pxs[1]); gv_puts("," as *u8); gv_num(pys[1]); gv_puts("," as *u8); gv_num(pzs[1]); gv_puts("\n" as *u8) 292 var t5v: i64 = 0 293 if nv == 2 { if pxs[0] == 1000 { if pys[0] == 0 { if pys[1] == 1500 { if pxs[1] == 0 { 294 if vmeta[1] == 2 { if vmeta[2] == 2000 { t5v = 1 } } } } } } } 295 gv_check("T5v vertex positions decode exactly (1.0 -> 1000, 1.5 -> 1500 permil) and both land inside the declared radius-2000 bound" as *u8, t5v, ctr) 296 297 // ---- T5w CONTAINMENT BITES: the SAME geometry under a shrunk bound must read OUTSIDE 298 let oobf: *u8 = sys_read_file(NG_F_OOB, sc) 299 let oobf_n: i64 = sc[0] 300 let nv2: i64 = nif_verts(oobf, oobf_n, oobf_n, 0, pxs, pys, pzs, 64, vmeta) 301 var contain_good: i64 = 0 302 if nv == 2 { contain_good = 1 } 303 var contain_oob_fires: i64 = 0 304 if nv2 == 2 { if vmeta[1] > 0 { contain_oob_fires = 1 } } 305 gv_puts(" oob: n=" as *u8); gv_num(nv2) 306 gv_puts(" inside=" as *u8); gv_num(vmeta[1]); gv_puts("\n" as *u8) 307 gv_bite("T5w the bound containment discriminates: identical verts read inside at radius 2.0 and OUTSIDE at radius 0.5" as *u8, contain_good, contain_oob_fires, ctr) 308 309 // ---- T5h the FO76-layout header (bsver 155: extra u32, no Process Script, Max Filepath) parses 310 let b155: *u8 = sys_read_file(NG_F_BS155, sc) 311 let b155_n: i64 = sc[0] 312 nif_probe(b155, b155_n, b155_n, facts) 313 var t5h: i64 = 0 314 if facts[0] == 1 { if facts[2] == 155 { if facts[3] == 1 { if facts[8] + facts[6] == b155_n { t5h = 1 } } } } 315 gv_check("T5h the bsver-155 header variant parses: state 1, 1 block, and its partition sums too" as *u8, t5h, ctr) 316 317 // ---- T6 an unsupported version is RECOGNIZED-UNSUPPORTED (2), never misparsed into counts 318 let old: *u8 = sys_read_file(NG_F_OLDVER, sc) 319 let old_n: i64 = sc[0] 320 nif_probe(old, old_n, old_n, facts) 321 var t6: i64 = 0 322 if facts[0] == 2 { if facts[3] == (0 - 1) { if facts[1] == NG_VER_OLD { t6 = 1 } } } 323 gv_check("T6 neg-control-an-unsupported-version-reads-state-2-with-counts-abstained-and-the-version-still-reported" as *u8, t6, ctr) 324 325 // ---- T7 an absurd bound is a named abstention, not a crash or a confident garbage census 326 let absf: *u8 = sys_read_file(NG_F_ABSURD, sc) 327 let absf_n: i64 = sc[0] 328 nif_probe(absf, absf_n, absf_n, facts) 329 var t7: i64 = 0 330 if facts[0] == 2 { if facts[3] == (0 - 1) { t7 = 1 } } 331 gv_check("T7 neg-control-a-65535-entry-type-table-refuses-into-state-2-abstention (bounds, not guesses)" as *u8, t7, ctr) 332 333 // ---- T8 truncation honesty: a partial read of a real container abstains 334 nif_probe(good, 60, good_n, facts) 335 var t8: i64 = 0 336 if facts[0] == 2 { if facts[3] == (0 - 1) { t8 = 1 } } 337 gv_check("T8 a truncated read is RECOGNIZED-UNSUPPORTED with every count abstained, never a small number" as *u8, t8, ctr) 338 339 // ---- T9 empty refused 340 let emp: *u8 = sys_read_file(NG_F_EMPTY, sc) 341 let emp_n: i64 = sc[0] 342 nif_probe(emp, emp_n, emp_n, facts) 343 var t9: i64 = 0 344 if facts[0] == 0 { t9 = 1 } 345 gv_check("T9 neg-control-a-zero-byte-file-is-not-a-nif" as *u8, t9, ctr) 346 347 return gv_verdict("NIFFACTS" as *u8, ctr, 348 "the Bethesda mesh container censuses exactly on the modern layout incl. the bsver-155 header, self-verifies TriShape geometry AND vertex positions by the file's own declared oracles, and abstains BY NAME on anything it cannot parse" as *u8) 349}