code wiki / _hdl_build / nx_gltf_load_gate.nx
nx_gltf_load_gate.nx source
↩ module page · 267 lines · 14006 B
1// nx_gltf_load_gate.nx -- ★NATIVE sovereign glTF viewer gate: load our exported nishi_being.glb back into the Nishi
2// renderer and render it (round-trip on OUR OWN stack), AND ★D1 FAIL-CLOSED teeth (debt 1785902685, 2026-08-12):
3// T5-T10 feed malformed/unsupported glbs assembled AT RUNTIME in /tmp -> the loader must REFUSE with the exact NAMED
4// reason, never segfault. T7 is the POSITIVE CONTROL (a valid minimal Khronos-shaped glb with u16 indices MUST load)
5// so a refuse-everything guard cannot pass. gv-migrated (D001): verdict comes from nx_gate_verdict, exit carries it.
6// license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_png.nx"
9import "nx_trimesh.nx"
10import "nx_gltf_load.nx"
11import "nx_gate_verdict.nx"
12
13func clearfb(fb: *i64, n: i64, c: i64) -> i64 { var i: i64=0; while i<n { fb[i]=c; i=i+1 } return 0 }
14func filled(fb: *i64, n: i64, bg: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { if fb[i]!=bg { c=c+1 } i=i+1 } return c }
15
16// exact string equality (asserts WHICH refusal rule fired -- a suite that only asks "was it refused?" is blind)
17func gstreq(a: *u8, b: *u8) -> i64 {
18 var i: i64 = 0
19 var r: i64 = 0-1
20 while r < 0 {
21 let ca: i64 = a[i] as i64
22 let cb: i64 = b[i] as i64
23 if ca != cb { r = 0 } else { if ca == 0 { r = 1 } else { i = i + 1 } }
24 }
25 return r
26}
27// fixture emitter: write a buffer to a file
28func gwfile(path: *u8, b: *u8, n: i64) -> i64 {
29 let fd: i64 = sys_openat_wr(path, 0x1a4)
30 if fd < 0 { return 0 }
31 sys_write(fd, b, n)
32 sys_close(fd)
33 return 1
34}
35func gw32(b: *u8, at: i64, v: i64) -> i64 {
36 b[at] = (v & 0xff) as u8
37 b[at+1] = ((v>>8) & 0xff) as u8
38 b[at+2] = ((v>>16) & 0xff) as u8
39 b[at+3] = ((v>>24) & 0xff) as u8
40 return 0
41}
42func gw16(b: *u8, at: i64, v: i64) -> i64 {
43 b[at] = (v & 0xff) as u8
44 b[at+1] = ((v>>8) & 0xff) as u8
45 return 0
46}
47func gputs(b: *u8, at: i64, s: *u8) -> i64 {
48 var i: i64 = 0
49 while s[i] != (0 as u8) { b[at+i] = s[i]; i = i + 1 }
50 return at + i
51}
52func gputn(b: *u8, at: i64, v: i64) -> i64 {
53 if v == 0 { b[at] = 48 as u8; return at + 1 }
54 let tmp: *u8 = sys_mmap(32) as *u8
55 var x: i64 = v
56 var n: i64 = 0
57 while x > 0 { tmp[n] = (48 + x%10) as u8; x = x/10; n = n + 1 }
58 var i: i64 = 0
59 while i < n { b[at+i] = tmp[n-1-i]; i = i + 1 }
60 return at + n
61}
62// assemble a minimal Khronos-shaped .glb (3 verts / 1 tri) with parameterized index componentType,
63// index bufferView byteOffset, and index values -- the refusal fixtures AND the positive control.
64func mkglb(path: *u8, ct: i64, io: i64, ia: i64, ibb: i64, ic: i64) -> i64 {
65 let js: *u8 = sys_mmap(2048) as *u8
66 var p: i64 = 0
67 p = gputs(js, p, "{\"meshes\":[{\"primitives\":[{\"attributes\":{\"POSITION\":0},\"indices\":1}]}],\"accessors\":[{\"bufferView\":0,\"componentType\":5126,\"count\":3},{\"bufferView\":1,\"componentType\":" as *u8)
68 p = gputn(js, p, ct)
69 p = gputs(js, p, ",\"count\":3}],\"bufferViews\":[{\"byteOffset\":0,\"byteLength\":36},{\"byteOffset\":" as *u8)
70 p = gputn(js, p, io)
71 p = gputs(js, p, ",\"byteLength\":6}]}" as *u8)
72 while p % 4 != 0 { js[p] = 32 as u8; p = p + 1 }
73 let jlen: i64 = p
74 let binlen: i64 = 44
75 let total: i64 = 28 + jlen + binlen
76 let b: *u8 = sys_mmap(total + 64) as *u8
77 b[0] = 103 as u8; b[1] = 108 as u8; b[2] = 84 as u8; b[3] = 70 as u8
78 gw32(b, 4, 2)
79 gw32(b, 8, total)
80 gw32(b, 12, jlen)
81 b[16] = 74 as u8; b[17] = 83 as u8; b[18] = 79 as u8; b[19] = 78 as u8
82 var i: i64 = 0
83 while i < jlen { b[20+i] = js[i]; i = i + 1 }
84 gw32(b, 20+jlen, binlen)
85 b[24+jlen] = 66 as u8; b[25+jlen] = 73 as u8; b[26+jlen] = 78 as u8; b[27+jlen] = 0 as u8
86 let bo: i64 = 28 + jlen
87 var k: i64 = 0
88 while k < 44 { b[bo+k] = 0 as u8; k = k + 1 }
89 gw32(b, bo+12, 1065353216)
90 gw32(b, bo+28, 1065353216)
91 gw16(b, bo+36, ia)
92 gw16(b, bo+38, ibb)
93 gw16(b, bo+40, ic)
94 return gwfile(path, b, total)
95}
96// one refusal tooth: load `path` via the std loader; pass iff refused(0) with the EXACT reason `want`.
97// prints the observed values on mismatch (a boolean cannot say why).
98func refuse_tooth(path: *u8, want: *u8) -> i64 {
99 let ok: i64 = gltf_load_std(path, 900)
100 let why: *u8 = gltf_load_why()
101 var pass: i64 = 0
102 if ok == 0 { if gstreq(why, want) == 1 { pass = 1 } }
103 if pass == 0 {
104 gv_puts(" detail: ok=" as *u8); gv_num(ok)
105 gv_puts(" why=" as *u8); gv_puts(why)
106 gv_puts(" want=" as *u8); gv_puts(want)
107 gv_puts("\n" as *u8)
108 }
109 return pass
110}
111
112// ★D2 fixture: 2 meshes / 3 primitives (9 verts, 3 tris), third primitive uses u32 indices --
113// proves the multi-mesh walk, the per-mesh primitives array, the concat base offsets, and both index widths
114func mk2glb(path: *u8) -> i64 {
115 let jb: *u8 = sys_mmap(2048) as *u8
116 var p: i64 = gputs(jb, 0, "{\"meshes\":[{\"primitives\":[{\"attributes\":{\"POSITION\":0},\"indices\":1},{\"attributes\":{\"POSITION\":2},\"indices\":3}]},{\"primitives\":[{\"attributes\":{\"POSITION\":4},\"indices\":5}]}],\"accessors\":[{\"bufferView\":0,\"componentType\":5126,\"count\":3},{\"bufferView\":1,\"componentType\":5123,\"count\":3},{\"bufferView\":2,\"componentType\":5126,\"count\":3},{\"bufferView\":3,\"componentType\":5123,\"count\":3},{\"bufferView\":4,\"componentType\":5126,\"count\":3},{\"bufferView\":5,\"componentType\":5125,\"count\":3}],\"bufferViews\":[{\"byteOffset\":0,\"byteLength\":36},{\"byteOffset\":108,\"byteLength\":6},{\"byteOffset\":36,\"byteLength\":36},{\"byteOffset\":116,\"byteLength\":6},{\"byteOffset\":72,\"byteLength\":36},{\"byteOffset\":124,\"byteLength\":12}]}" as *u8)
117 while p % 4 != 0 { jb[p] = 32 as u8; p = p + 1 }
118 let jlen: i64 = p
119 let binlen: i64 = 136
120 let total: i64 = 28 + jlen + binlen
121 let b: *u8 = sys_mmap(total + 64) as *u8
122 b[0] = 103 as u8; b[1] = 108 as u8; b[2] = 84 as u8; b[3] = 70 as u8
123 gw32(b, 4, 2)
124 gw32(b, 8, total)
125 gw32(b, 12, jlen)
126 b[16] = 74 as u8; b[17] = 83 as u8; b[18] = 79 as u8; b[19] = 78 as u8
127 var i: i64 = 0
128 while i < jlen { b[20+i] = jb[i]; i = i + 1 }
129 gw32(b, 20+jlen, binlen)
130 b[24+jlen] = 66 as u8; b[25+jlen] = 73 as u8; b[26+jlen] = 78 as u8; b[27+jlen] = 0 as u8
131 let bo: i64 = 28 + jlen
132 var k: i64 = 0
133 while k < binlen { b[bo+k] = 0 as u8; k = k + 1 }
134 // prim0 verts @0: (0,0,0)(1,0,0)(0,1,0) 1.0f=1065353216 2.0f=1073741824 3.0f=1077936128
135 gw32(b, bo+12, 1065353216)
136 gw32(b, bo+28, 1065353216)
137 // prim1 verts @36: (2,0,0)(3,0,0)(2,1,0)
138 gw32(b, bo+36, 1073741824)
139 gw32(b, bo+48, 1077936128)
140 gw32(b, bo+60, 1073741824)
141 gw32(b, bo+64, 1065353216)
142 // prim2 verts @72: (0,2,0)(1,2,0)(0,3,0)
143 gw32(b, bo+76, 1073741824)
144 gw32(b, bo+84, 1065353216)
145 gw32(b, bo+88, 1073741824)
146 gw32(b, bo+100, 1077936128)
147 // indices: u16 @108 (0,1,2) + u16 @116 (0,1,2) + u32 @124 (0,1,2)
148 gw16(b, bo+110, 1)
149 gw16(b, bo+112, 2)
150 gw16(b, bo+118, 1)
151 gw16(b, bo+120, 2)
152 gw32(b, bo+128, 1)
153 gw32(b, bo+132, 2)
154 return gwfile(path, b, total)
155}
156
157const BG: i64 = 24 + 26*256 + 34*65536
158const W: i64 = 300
159const H: i64 = 460
160
161func main() -> i64 {
162 let ctr: *i64 = gv_ctr()
163 gv_head("nx_gltf_load_gate -- sovereign .glb loader: round-trip render + D1 fail-closed named refusals (debt 1785902685)" as *u8)
164
165 // precondition: the exported being fixture (round-trip half). Refusal teeth need no fixture.
166 var havebeing: i64 = 0
167 let bfd: i64 = sys_openat_rd("knowledge/nishi_being.glb" as *u8)
168 if bfd >= 0 { havebeing = 1; sys_close(bfd) }
169 if gv_need("knowledge/nishi_being.glb" as *u8, havebeing, ctr) == 1 {
170 let ok: i64 = gltf_load("knowledge/nishi_being.glb" as *u8)
171 let nv: i64 = tm_nv()
172 let nt: i64 = tm_nt()
173 gv_puts(" loaded: ok=" as *u8); gv_num(ok); gv_puts(" verts=" as *u8); gv_num(nv); gv_puts(" tris=" as *u8); gv_num(nt); gv_puts(" overflow=" as *u8); gv_num(tm_ovf()); gv_puts("\n" as *u8)
174 var t1: i64 = 0
175 if ok == 1 { if nv > 1000 { if tm_ovf() == 0 { t1 = 1 } } }
176 if t1 == 0 { gv_puts(" detail: why=" as *u8); gv_puts(gltf_load_why()); gv_puts("\n" as *u8) }
177 gv_check("T1 parses our exported .glb into the native renderer" as *u8, t1, ctr)
178 // T2: loaded counts equal the file's OWN declared accessor counts (two readers of one
179 // declaration -- catches silent truncation, and never goes stale when the export evolves;
180 // the old tooth hardcoded 7954/15912 and went RED when the being export grew to 8180/16360)
181 let szp2: *i64 = sys_mmap(16) as *i64
182 let fbuf: *u8 = sys_read_file("knowledge/nishi_being.glb" as *u8, szp2)
183 let jl2: i64 = gll_ru32(fbuf, 12)
184 let dNV: i64 = gll_nth_num(fbuf, 20, 20+jl2, "\"count\":" as *u8, 1)
185 let dNI: i64 = gll_nth_num(fbuf, 20, 20+jl2, "\"count\":" as *u8, 4)
186 gv_puts(" declared: NV=" as *u8); gv_num(dNV); gv_puts(" NI=" as *u8); gv_num(dNI); gv_puts("\n" as *u8)
187 var t2: i64 = 0
188 if nv == dNV { if nt*3 == dNI { t2 = 1 } }
189 gv_check("T2 loaded counts equal the file's declared accessor counts" as *u8, t2, ctr)
190 let npx: i64 = W*H
191 let fb: *i64 = sys_mmap(npx*8) as *i64
192 let zb: *i64 = sys_mmap(npx*8) as *i64
193 clearfb(fb, npx, BG); trimesh_zclear(zb, npx); trimesh_render(fb, zb, W, H, 2400, 0-200, 2600, 520, 2)
194 let fil: i64 = filled(fb, npx, BG)
195 gv_puts(" render: filled=" as *u8); gv_num(fil); gv_puts("\n" as *u8)
196 var t3: i64 = 0
197 if fil > npx/12 { t3 = 1 }
198 gv_check("T3 loaded .glb renders on our stack (filled > 1/12 of frame)" as *u8, t3, ctr)
199 // turntable artifact: 3 views of the loaded model
200 let yaws: *i64 = sys_mmap(3*8) as *i64
201 yaws[0]=1400; yaws[1]=3200; yaws[2]=5600
202 let GWD: i64 = W*3
203 let gal: *i64 = sys_mmap(GWD*H*8) as *i64
204 clearfb(gal, GWD*H, BG)
205 let cell: *i64 = sys_mmap(npx*8) as *i64
206 var v: i64 = 0
207 while v < 3 {
208 clearfb(cell, npx, BG); trimesh_zclear(zb, npx); trimesh_render(cell, zb, W, H, yaws[v], 0-200, 2600, 520, 2)
209 var y: i64 = 0
210 while y < H { var x: i64=0; while x<W { gal[y*GWD+v*W+x]=cell[y*W+x]; x=x+1 } y=y+1 }
211 v = v + 1
212 }
213 write_png(gal, GWD, H, "knowledge/nx_gltf_view.png" as *u8)
214 gv_puts(" turntable artifact -> knowledge/nx_gltf_view.png (loaded .glb, 3 views)\n" as *u8)
215 }
216
217 // ===== ★D1 refusal teeth: fixtures assembled at runtime, reasons asserted EXACTLY =====
218 let p5: *u8 = "/tmp/gllg_trunc.bin" as *u8
219 let tb: *u8 = sys_mmap(16) as *u8
220 var q: i64 = 0
221 while q < 10 { tb[q] = 88 as u8; q = q + 1 }
222 gwfile(p5, tb, 10)
223 gv_check("T5 neg-control-truncated-file refused named file-too-short" as *u8, refuse_tooth(p5, "file-too-short" as *u8), ctr)
224 var t5b: i64 = 0
225 if gltf_load(p5) == 0 { if gstreq(gltf_load_why(), "file-too-short" as *u8) == 1 { t5b = 1 } }
226 if t5b == 0 { gv_puts(" detail: legacy why=" as *u8); gv_puts(gltf_load_why()); gv_puts("\n" as *u8) }
227 gv_check("T5b neg-control-legacy-entry refused named file-too-short" as *u8, t5b, ctr)
228 let p6: *u8 = "/tmp/gllg_jsonoob.glb" as *u8
229 let hb: *u8 = sys_mmap(32) as *u8
230 hb[0] = 103 as u8; hb[1] = 108 as u8; hb[2] = 84 as u8; hb[3] = 70 as u8
231 gw32(hb, 4, 2)
232 gw32(hb, 8, 24)
233 gw32(hb, 12, 999999)
234 hb[16] = 74 as u8; hb[17] = 83 as u8; hb[18] = 79 as u8; hb[19] = 78 as u8
235 gw32(hb, 20, 0)
236 gwfile(p6, hb, 24)
237 gv_check("T6 neg-control-json-oob refused named json-chunk-oob" as *u8, refuse_tooth(p6, "json-chunk-oob" as *u8), ctr)
238 // T7 POSITIVE CONTROL: a valid minimal Khronos-shaped glb (u16 indices) MUST load -- refuse-everything dies here
239 let p7: *u8 = "/tmp/gllg_ok.glb" as *u8
240 mkglb(p7, 5123, 36, 0, 1, 2)
241 var t7: i64 = 0
242 if gltf_load_std(p7, 900) == 1 { if tm_nv() == 3 { if tm_nt() == 1 { t7 = 1 } } }
243 if t7 == 0 { gv_puts(" detail: why=" as *u8); gv_puts(gltf_load_why()); gv_puts(" nv=" as *u8); gv_num(tm_nv()); gv_puts(" nt=" as *u8); gv_num(tm_nt()); gv_puts("\n" as *u8) }
244 gv_check("T7 positive-control minimal std glb loads (u16 indices, 3v/1t)" as *u8, t7, ctr)
245 let p8: *u8 = "/tmp/gllg_idxoob.glb" as *u8
246 mkglb(p8, 5123, 900000, 0, 1, 2)
247 gv_check("T8 neg-control-index-oob refused named index-accessor-oob" as *u8, refuse_tooth(p8, "index-accessor-oob" as *u8), ctr)
248 let p9: *u8 = "/tmp/gllg_u8idx.glb" as *u8
249 mkglb(p9, 5121, 36, 0, 1, 2)
250 gv_check("T9 neg-control-index-ctype refused named index-componentType-unsupported" as *u8, refuse_tooth(p9, "index-componentType-unsupported" as *u8), ctr)
251 let p10: *u8 = "/tmp/gllg_idxrange.glb" as *u8
252 mkglb(p10, 5123, 36, 0, 1, 7)
253 gv_check("T10 neg-control-index-range refused named index-out-of-range" as *u8, refuse_tooth(p10, "index-out-of-range" as *u8), ctr)
254
255 // ===== ★D2 (debt 1786545740): multi-mesh/multi-primitive concat, mechanism proven exactly =====
256 let p12: *u8 = "/tmp/gllg_multi.glb" as *u8
257 mk2glb(p12)
258 var t12: i64 = 0
259 if gltf_load_all(p12, 900) == 1 { if tm_nv() == 9 { if tm_nt() == 3 { t12 = 1 } } }
260 if t12 == 0 { gv_puts(" detail: why=" as *u8); gv_puts(gltf_load_why()); gv_puts(" nv=" as *u8); gv_num(tm_nv()); gv_puts(" nt=" as *u8); gv_num(tm_nt()); gv_puts("\n" as *u8) }
261 gv_check("T12 D2 concat: 2 meshes / 3 primitives load as 9 verts / 3 tris (u16 AND u32 indices)" as *u8, t12, ctr)
262 gv_puts(" NOTE donor seed_san.vrm ABSENT estate-wide (measured 2026-08-12, corpus_complete=1) -- D2 donor acceptance awaits re-acquisition; mechanism proven on the synthetic multi-prim fixture above\n" as *u8)
263
264 let rc: i64 = gv_verdict("GLTF-LOAD-GATE" as *u8, ctr, "loader is fail-closed with named reasons; round-trip + positive control + D2 concat intact" as *u8)
265 sys_exit(rc)
266 return rc
267}