nx_game_page_artifact_lib.nx source
↩ module page · 810 lines · 35372 B
1// Native packaging by artifact reference; no publication or engine-acceptance claim.
2import "nx_fsops_lib.nx"
3import "nx_fs_create_lib.nx"
4import "nx_base64.nx"
5import "nx_sha256.nx"
6
7const GPA_HEADER_BYTES: i64 = 8
8
9struct GpaWasmCursor {
10 data: *u8,
11 size: i64,
12 pos: i64,
13}
14struct GpaMemory {
15 minimum: i64,
16 maximum: i64,
17 count: i64,
18}
19// WebAssembly u32 LEB128: at most five bytes, with only four payload bits in the last.
20func gpa_u32(c: *GpaWasmCursor) -> i64 {
21 var value: i64 = 0
22 var shift: i64 = 0
23 while shift < 35 {
24 if c.pos >= c.size { return -1 }
25 let b: i64 = c.data[c.pos] as i64
26 c.pos = c.pos+1
27 if shift == 28 { if b > 15 { return -1 } }
28 value = value | ((b & 127) << shift)
29 if (b & 128) == 0 { return value }
30 shift = shift+7
31 }
32 return -1
33}
34func gpa_wasm_header(data: *u8,n: i64) -> i64 {
35 if n < GPA_HEADER_BYTES { return 0 }
36 let header: *u8 = "\x00asm\x01\x00\x00\x00" as *u8
37 return gpa_same(data,GPA_HEADER_BYTES,header,GPA_HEADER_BYTES)
38}
39func gpa_wasm_name(c: *GpaWasmCursor,name: *u8) -> i64 {
40 let n: i64 = gpa_u32(c)
41 if n < 0 || n > c.size-c.pos { return 0 }
42 let ok: i64 = gpa_same((c.data as i64+c.pos) as *u8,n,name,vw_slen(name))
43 c.pos = c.pos+n
44 return ok
45}
46// This packager supports the compiler's one-memory, no-other-import twin contract.
47// Other valid Wasm layouts require a separate supported contract, never silent guessing.
48func gpa_memory_section(data: *u8,n: i64,shared: i64,m: *GpaMemory) -> i64 {
49 let c: *GpaWasmCursor = sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
50 c.data=data; c.size=n; c.pos=0
51 var ok: i64 = 1
52 if gpa_u32(c) != 1 { ok=0 }
53 if shared == 1 {
54 if gpa_wasm_name(c,"env") != 1 { ok=0 }
55 if gpa_wasm_name(c,"memory") != 1 { ok=0 }
56 if gpa_u32(c) != 2 { ok=0 }
57 }
58 let flags: i64 = gpa_u32(c)
59 if shared == 1 { if flags != 3 { ok=0 } } else { if flags != 0 && flags != 1 { ok=0 } }
60 let minimum: i64 = gpa_u32(c)
61 var maximum: i64 = minimum
62 if (flags & 1) == 1 { maximum=gpa_u32(c) }
63 // 2^16 pages is the wasm32 address-space ceiling, not a workload budget.
64 if minimum <= 0 || maximum < minimum || maximum > 65536 { ok=0 }
65 if c.pos != n { ok=0 }
66 if m.count != 0 { ok=0 }
67 if ok == 1 { m.minimum=minimum; m.maximum=maximum; m.count=1 }
68 sys_munmap(c as *u8,__size_of(GpaWasmCursor))
69 return ok
70}
71func gpa_body_section(c: *GpaWasmCursor,shared: i64,m: *GpaMemory,span: *GpaWasmCursor) -> i64 {
72 while c.pos < c.size {
73 let start: i64 = c.pos
74 let id: i64 = c.data[c.pos] as i64
75 c.pos=c.pos+1
76 let n: i64 = gpa_u32(c)
77 if n < 0 || n > c.size-c.pos { return -1 }
78 let payload: *u8 = (c.data as i64+c.pos) as *u8
79 c.pos=c.pos+n
80 if id == 2 || id == 5 {
81 if (shared == 1 && id != 2) || (shared == 0 && id != 5) { return -1 }
82 if gpa_memory_section(payload,n,shared,m) != 1 { return -1 }
83 } else {
84 span.data=(c.data as i64+start) as *u8; span.size=c.pos-start
85 return 1
86 }
87 }
88 return 0
89}
90// Equal non-memory sections bind code, exports, types and data to the same engine.
91// This is pair identity checking, not complete Wasm validation or runtime qualification.
92func gpa_pair(plain: *u8,pn: i64,twin: *u8,tn: i64,memory: *GpaMemory) -> i64 {
93 if gpa_wasm_header(plain,pn) != 1 || gpa_wasm_header(twin,tn) != 1 { return 0 }
94 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
95 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
96 let sa: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
97 let sb: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
98 let ma: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory
99 let mb: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory
100 a.data=plain; a.size=pn; a.pos=GPA_HEADER_BYTES
101 b.data=twin; b.size=tn; b.pos=GPA_HEADER_BYTES
102 var ok: i64=1
103 while ok == 1 {
104 let ra: i64=gpa_body_section(a,0,ma,sa)
105 let rb: i64=gpa_body_section(b,1,mb,sb)
106 if ra < 0 || rb < 0 || ra != rb { ok=0; break }
107 if ra == 0 { break }
108 if gpa_same(sa.data,sa.size,sb.data,sb.size) != 1 { ok=0 }
109 }
110 if ma.count != 1 || mb.count != 1 || ma.minimum != mb.minimum { ok=0 }
111 if ok == 1 { memory.minimum=mb.minimum; memory.maximum=mb.maximum; memory.count=1 }
112 sys_munmap(a as *u8,__size_of(GpaWasmCursor)); sys_munmap(b as *u8,__size_of(GpaWasmCursor))
113 sys_munmap(sa as *u8,__size_of(GpaWasmCursor)); sys_munmap(sb as *u8,__size_of(GpaWasmCursor))
114 sys_munmap(ma as *u8,__size_of(GpaMemory)); sys_munmap(mb as *u8,__size_of(GpaMemory))
115 return ok
116}
117
118const GPA_MARK: *u8 = "const B=\"" as *u8
119const GPA_SHA_BYTES: i64 = 32
120const GPA_SHA_HEX: i64 = 64
121const GPA_RC_USAGE: i64 = 2
122const GPA_RC_INPUT: i64 = 3
123const GPA_RC_SHAPE: i64 = 4
124const GPA_RC_CONFLICT: i64 = 8
125
126func gpa_same(a: *u8, an: i64, b: *u8, bn: i64) -> i64 {
127 if an != bn { return 0 }
128 var i: i64 = 0
129 while i < an { if a[i] != b[i] { return 0 }; i = i + 1 }
130 return 1
131}
132func gpa_sha(b: *u8, n: i64, hex: *u8) -> i64 {
133 let raw: *u8 = sys_mmap(GPA_SHA_BYTES)
134 sha256_digest(b, n, raw)
135 let digits: *u8 = "0123456789abcdef" as *u8
136 var i: i64 = 0
137 while i < GPA_SHA_BYTES {
138 let c: i64 = raw[i] as i64
139 hex[i*2] = digits[c / 16]; hex[i*2+1] = digits[c % 16]
140 i = i + 1
141 }
142 hex[GPA_SHA_HEX] = 0 as u8
143 sys_munmap(raw, GPA_SHA_BYTES)
144 return GPA_SHA_HEX
145}
146// Returns output length or a negative refusal. Nothing is written before every bound is established.
147func gpa_compose(page: *u8, pn: i64, wasm: *u8, wn: i64, out: *u8, cap: i64) -> i64 {
148 if pn <= 0 { return 0 - GPA_RC_INPUT }
149 if wn < GPA_HEADER_BYTES { return 0 - GPA_RC_INPUT }
150 if wasm[0] != (0 as u8) { return 0 - GPA_RC_INPUT }
151 if wasm[1] != (97 as u8) { return 0 - GPA_RC_INPUT }
152 if wasm[2] != (115 as u8) { return 0 - GPA_RC_INPUT }
153 if wasm[3] != (109 as u8) { return 0 - GPA_RC_INPUT }
154 if wasm[4] != (1 as u8) { return 0 - GPA_RC_INPUT }
155 var h: i64 = 5
156 while h < GPA_HEADER_BYTES { if wasm[h] != (0 as u8) { return 0 - GPA_RC_INPUT }; h = h + 1 }
157 let ml: i64 = vw_slen(GPA_MARK)
158 var hit: i64 = 0 - 1
159 var i: i64 = 0
160 while i + ml <= pn {
161 if gpa_same(((page as i64)+i) as *u8, ml, GPA_MARK, ml) == 1 {
162 if hit >= 0 { return 0 - GPA_RC_SHAPE }
163 hit = i + ml
164 }
165 i = i + 1
166 }
167 if hit < 0 { return 0 - GPA_RC_SHAPE }
168 var end: i64 = hit
169 while end < pn {
170 if page[end] == (34 as u8) { break }
171 let c: i64 = page[end] as i64
172 if b64_dec_char(c) < 0 { if c != B64_PAD { return 0 - GPA_RC_SHAPE } }
173 end = end + 1
174 }
175 if end + 1 >= pn { return 0 - GPA_RC_SHAPE }
176 if page[end+1] != (59 as u8) { return 0 - GPA_RC_SHAPE }
177 let encn: i64 = ((wn + 2) / 3) * 4
178 let total: i64 = pn - (end-hit) + encn
179 if total > cap { return 0 - GPA_RC_SHAPE }
180 if gpa_release_slot(page,pn)<0 { return 0 - GPA_RC_SHAPE }
181 i = 0
182 while i < hit { out[i] = page[i]; i = i + 1 }
183 let wrote: i64 = b64_encode(wasm, wn, ((out as i64)+hit) as *u8)
184 var dest: i64 = hit + wrote
185 i = end
186 while i < pn { out[dest] = page[i]; dest = dest + 1; i = i + 1 }
187 if gpa_seal(out,dest)<0 { return 0 - GPA_RC_SHAPE }
188 return dest
189}
190func gpa_slot_replace(src: *u8,n: i64,mark: *u8,value: *u8,quoted: i64,out: *u8,cap: i64) -> i64 {
191 let ml: i64=vw_slen(mark); let vl: i64=vw_slen(value)
192 var start: i64=0-1; var i: i64=0
193 while i+ml<=n {
194 if gpa_same((src as i64+i) as *u8,ml,mark,ml)==1 {
195 if start>=0 { return 0-GPA_RC_SHAPE }; start=i+ml
196 }
197 i=i+1
198 }
199 if start<0 { return 0-GPA_RC_SHAPE }
200 var end: i64=start
201 while end<n {
202 let c: i64=src[end] as i64
203 if quoted==1 { if c==34 { break }; if c<32 || c==92 { return 0-GPA_RC_SHAPE } }
204 else { if c==59 { break }; if c<48 || c>57 { return 0-GPA_RC_SHAPE } }
205 end=end+1
206 }
207 if end==start || end>=n { return 0-GPA_RC_SHAPE }
208 if quoted==1 { if end+1>=n { return 0-GPA_RC_SHAPE }; if src[end+1]!=(59 as u8) { return 0-GPA_RC_SHAPE } }
209 let total: i64=n-(end-start)+vl
210 if total>cap || total<0 { return 0-GPA_RC_SHAPE }
211 i=0; while i<start { out[i]=src[i]; i=i+1 }
212 var j: i64=0; while j<vl { out[i]=value[j]; i=i+1; j=j+1 }
213 j=end; while j<n { out[i]=src[j]; i=i+1; j=j+1 }
214 return i
215}
216func gpa_decimal(v: i64,out: *u8) -> i64 {
217 var x: i64=v; var n: i64=1
218 while x>=10 { n=n+1; x=x/10 }
219 x=v; var i: i64=n
220 while i>0 { i=i-1; out[i]=(48+x%10) as u8; x=x/10 }
221 out[n]=0 as u8
222 return n
223}
224// The native HTML envelope owns this slot. Script-only packaging stays supported.
225// The declaration is updated together with NXMT_URL, so rebind cannot leave stale metadata.
226func gpa_declare_shared(src: *u8,n: i64,url: *u8,out: *u8,cap: i64) -> i64 {
227 let head: *u8="<head>" as *u8
228 let close: *u8="</head>" as *u8
229 let mark: *u8="<meta name=\"nishi-wasm\" data-nishi-role=\"shared-runtime\" content=\"" as *u8
230 let suffix: *u8="\">" as *u8
231 let hl: i64=vw_slen(head); let cl: i64=vw_slen(close)
232 let ml: i64=vw_slen(mark); let sl: i64=vw_slen(suffix)
233 var hs: i64=0-1; var he: i64=0-1; var hit: i64=0-1
234 var i: i64=0
235 while i<n {
236 if i+hl<=n { if gpa_same((src as i64+i) as *u8,hl,head,hl)==1 {
237 if hs>=0 { return 0-GPA_RC_SHAPE }; hs=i+hl
238 } }
239 if i+cl<=n { if gpa_same((src as i64+i) as *u8,cl,close,cl)==1 {
240 if he>=0 { return 0-GPA_RC_SHAPE }; he=i
241 } }
242 if i+ml<=n { if gpa_same((src as i64+i) as *u8,ml,mark,ml)==1 {
243 if hit>=0 { return 0-GPA_RC_SHAPE }; hit=i
244 } }
245 i=i+1
246 }
247 if hs<0 && he<0 && hit<0 {
248 if n>cap { return 0-GPA_RC_SHAPE }
249 i=0;while i<n { out[i]=src[i];i=i+1 };return n
250 }
251 if hs<0 || he<hs { return 0-GPA_RC_SHAPE }
252 var start: i64=he;var end: i64=he
253 if hit>=0 {
254 if hit<hs || hit>=he { return 0-GPA_RC_SHAPE }
255 start=hit;end=hit+ml
256 while end<he { if src[end]==(34 as u8) { break };end=end+1 }
257 if end+sl>he { return 0-GPA_RC_SHAPE }
258 if gpa_same((src as i64+end) as *u8,sl,suffix,sl)!=1 { return 0-GPA_RC_SHAPE }
259 end=end+sl
260 }
261 let amp: *u8="&" as *u8;let al: i64=vw_slen(amp)
262 let un: i64=vw_slen(url);var escaped: i64=un
263 i=0;while i<un { if url[i]==(38 as u8) { escaped=escaped+al-1 };i=i+1 }
264 let total: i64=n-(end-start)+ml+escaped+sl
265 if total>cap || total<n-(end-start) { return 0-GPA_RC_SHAPE }
266 var p: i64=0;i=0;while i<start { out[p]=src[i];p=p+1;i=i+1 }
267 i=0;while i<ml { out[p]=mark[i];p=p+1;i=i+1 }
268 i=0;while i<un {
269 if url[i]==(38 as u8) { var j: i64=0;while j<al { out[p]=amp[j];p=p+1;j=j+1 } }
270 else { out[p]=url[i];p=p+1 }
271 i=i+1
272 }
273 i=0;while i<sl { out[p]=suffix[i];p=p+1;i=i+1 }
274 i=end;while i<n { out[p]=src[i];p=p+1;i=i+1 }
275 return p
276}
277// Legacy pages have neither identity field. Modern pages require exactly one complete tuple.
278func gpa_shared_identity(src:*u8,n:i64,twin:*u8,tn:i64,out:*u8,cap:i64)->i64 {
279 let hm:*u8="const NXMT_SHA256=\"" as *u8
280 let bm:*u8="const NXMT_BYTES=" as *u8
281 let hc:i64=fsx_count_occ(src,n,hm,vw_slen(hm))
282 let bc:i64=fsx_count_occ(src,n,bm,vw_slen(bm))
283 if hc==0&&bc==0 {
284 if n>cap {return 0-GPA_RC_SHAPE}
285 var i:i64=0;while i<n {out[i]=src[i];i=i+1};return n
286 }
287 if hc!=1||bc!=1||tn<=0 {return 0-GPA_RC_SHAPE}
288 let hash:*u8=sys_mmap(GPA_SHA_HEX+1)
289 let size:*u8=sys_mmap(GPA_SHA_HEX+1)
290 let mid:*u8=sys_mmap(cap)
291 if (hash as i64)<=0||(size as i64)<=0||(mid as i64)<=0 {return 0-GPA_RC_SHAPE}
292 gpa_sha(twin,tn,hash);gpa_decimal(tn,size)
293 var length:i64=gpa_slot_replace(src,n,hm,hash,1,mid,cap)
294 if length>=0 {length=gpa_slot_replace(mid,length,bm,size,0,out,cap)}
295 sys_munmap(hash,GPA_SHA_HEX+1);sys_munmap(size,GPA_SHA_HEX+1);sys_munmap(mid,cap)
296 return length
297}
298
299// A paired package is built privately; any refusal leaves the caller's output untouched.
300func gpa_compose_pair(page: *u8,pn: i64,plain: *u8,wn: i64,twin: *u8,tn: i64,url: *u8,out: *u8,cap: i64) -> i64 {
301 if cap<=0 { return 0-GPA_RC_SHAPE }
302 let un: i64=vw_slen(url)
303 if un==0 { return 0-GPA_RC_SHAPE }
304 var i: i64=0
305 while i<un {
306 let c: i64=url[i] as i64
307 if c<33 || c>126 || c==34 || c==39 || c==92 || c==60 || c==62 || c==96 { return 0-GPA_RC_SHAPE }
308 i=i+1
309 }
310 let memory: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory
311 let paired: i64=gpa_pair(plain,wn,twin,tn,memory)
312 if paired!=1 { sys_munmap(memory as *u8,__size_of(GpaMemory)); return 0-GPA_RC_SHAPE }
313 // Wasm32 page counts are at most 65536: decimal storage derives from that bound.
314 let digits: i64=vw_slen("65536" as *u8)+1
315 // Intermediate replacements can be longer than the final page. Bound temporary storage
316 // by original bytes plus every inserted value; final output capacity is checked separately.
317 let declaration: i64=vw_slen("<meta name=\"nishi-wasm\" data-nishi-role=\"shared-runtime\" content=\"\">" as *u8)+un*vw_slen("&" as *u8)
318 let scratch: i64=pn+((wn+2)/3)*4+un+2*(digits-1)+declaration+2*GPA_SHA_HEX
319 let a: *u8=sys_mmap(scratch); let b: *u8=sys_mmap(scratch)
320 let lo: *u8=sys_mmap(digits); let hi: *u8=sys_mmap(digits)
321 gpa_decimal(memory.minimum,lo); gpa_decimal(memory.maximum,hi)
322 var n: i64=gpa_compose(page,pn,plain,wn,a,scratch)
323 if n>=0 { n=gpa_slot_replace(a,n,"const NXMT_URL=\"" as *u8,url,1,b,scratch) }
324 if n>=0 { n=gpa_slot_replace(b,n,"const NXMT_PAGES=" as *u8,lo,0,a,scratch) }
325 if n>=0 { n=gpa_slot_replace(a,n,"const NXMT_MAX=" as *u8,hi,0,b,scratch) }
326 if n>=0 { n=gpa_shared_identity(b,n,twin,tn,a,scratch) }
327 if n>=0 { n=gpa_declare_shared(a,n,url,b,scratch) }
328 if n>cap { n=0-GPA_RC_SHAPE }
329 if n>=0 { if gpa_seal(b,n)<0 { n=0-GPA_RC_SHAPE } }
330 if n>=0 { i=0; while i<n { out[i]=b[i]; i=i+1 } }
331 sys_munmap(a,scratch);sys_munmap(b,scratch);sys_munmap(lo,digits);sys_munmap(hi,digits)
332 sys_munmap(memory as *u8,__size_of(GpaMemory))
333 return n
334}
335// Compare every original function body before considering a compiler-owned appended initializer.
336// A positive result proves application-code preservation only, never whole-module equivalence.
337func gpa_section(data: *u8,n: i64,wanted: i64,out: *GpaWasmCursor) -> i64 {
338 if gpa_wasm_header(data,n)!=1 { return 0 }
339 let c: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
340 c.data=data;c.size=n;c.pos=GPA_HEADER_BYTES
341 var found: i64=0
342 var ok: i64=1
343 while c.pos<c.size {
344 let id: i64=c.data[c.pos] as i64;c.pos=c.pos+1
345 let count: i64=gpa_u32(c)
346 if count<0 || count>c.size-c.pos { ok=0;break }
347 if id==wanted {
348 if found!=0 { ok=0;break }
349 out.data=(data as i64+c.pos) as *u8;out.size=count;out.pos=0
350 found=1
351 }
352 c.pos=c.pos+count
353 }
354 sys_munmap(c as *u8,__size_of(GpaWasmCursor))
355 if ok!=1 { return 0 }
356 return found
357}
358func gpa_code_prefix(a: *GpaWasmCursor,b: *GpaWasmCursor,extra: *GpaWasmCursor) -> i64 {
359 let original: i64=gpa_u32(a)
360 let candidate: i64=gpa_u32(b)
361 if original<0 || candidate!=original+1 { return 0 }
362 var i: i64=0
363 while i<original {
364 let an: i64=gpa_u32(a)
365 let bn: i64=gpa_u32(b)
366 if an<0 || bn<0 || an>a.size-a.pos || bn>b.size-b.pos { return 0 }
367 if gpa_same((a.data as i64+a.pos) as *u8,an,(b.data as i64+b.pos) as *u8,bn)!=1 { return 0 }
368 a.pos=a.pos+an;b.pos=b.pos+bn;i=i+1
369 }
370 if a.pos!=a.size { return 0 }
371 let extra_size: i64=gpa_u32(b)
372 if extra_size<=0 || extra_size!=b.size-b.pos { return 0 }
373 extra.data=(b.data as i64+b.pos) as *u8;extra.size=extra_size;extra.pos=0
374 return 1
375}
376func gpa_application_code(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
377 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
378 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
379 let extra: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
380 var ok: i64=0
381 // Section 10 is the WebAssembly code section, independent of emitted function count.
382 if gpa_section(plain,pn,10,a)==1 && gpa_section(twin,tn,10,b)==1 {
383 ok=gpa_code_prefix(a,b,extra)
384 }
385 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor))
386 sys_munmap(extra as *u8,__size_of(GpaWasmCursor))
387 return ok
388}
389// Hex describes WebAssembly opcodes, not configurable execution limits.
390func gpa_hex_digit(c: i64) -> i64 {
391 if c>=48 && c<=57 { return c-48 }
392 if c>=97 && c<=102 { return c-97+10 }
393 return -1
394}
395func gpa_bytes(c: *GpaWasmCursor,hex: *u8) -> i64 {
396 let n: i64=vw_slen(hex)
397 if n%2!=0 || c.pos<0 || n/2>c.size-c.pos { return 0 }
398 var i: i64=0
399 while i<n {
400 let hi: i64=gpa_hex_digit(hex[i] as i64)
401 let lo: i64=gpa_hex_digit(hex[i+1] as i64)
402 if hi<0 || lo<0 { return 0 }
403 if (c.data[c.pos] as i64)!=hi*16+lo { return 0 }
404 c.pos=c.pos+1;i=i+2
405 }
406 return 1
407}
408// Decode an i32.const as its unsigned wasm32 address/length value; -1 means malformed.
409func gpa_i32_constant(c: *GpaWasmCursor) -> i64 {
410 if gpa_bytes(c,"41")!=1 { return -1 }
411 var value: i64=0
412 var shift: i64=0
413 while shift<35 {
414 if c.pos>=c.size { return -1 }
415 let b: i64=c.data[c.pos] as i64;c.pos=c.pos+1
416 if shift==28 { if b>7 && b<120 { return -1 } }
417 value=value|((b&127)<<shift)
418 shift=shift+7
419 if (b&128)==0 {
420 if shift<32 && (b&64)!=0 { value=value|((0-1)<<shift) }
421 return value&4294967295
422 }
423 }
424 return -1
425}
426func gpa_initial_data(a: *GpaWasmCursor,b: *GpaWasmCursor,body: *GpaWasmCursor) -> i64 {
427 let count: i64=gpa_u32(a)
428 if count<0 || gpa_u32(b)!=count { return 0 }
429 if gpa_bytes(body,"01017e")!=1 { return 0 }
430 let state: i64=gpa_i32_constant(body)
431 if state<0 || state%4!=0 { return 0 }
432 if gpa_bytes(body,"41004101fe480200ad210020004200510440")!=1 { return 0 }
433 var i: i64=0
434 while i<count {
435 if gpa_u32(a)!=0 || gpa_u32(b)!=1 { return 0 }
436 let destination: i64=gpa_i32_constant(a)
437 if destination<0 || gpa_bytes(a,"0b")!=1 { return 0 }
438 let n: i64=gpa_u32(a)
439 let bn: i64=gpa_u32(b)
440 if n<0 || bn!=n || n>a.size-a.pos || n>b.size-b.pos { return 0 }
441 if destination>4294967296-n { return 0 }
442 if state<destination+n && destination<state+4 { return 0 }
443 if gpa_same((a.data as i64+a.pos) as *u8,n,(b.data as i64+b.pos) as *u8,n)!=1 { return 0 }
444 if gpa_i32_constant(body)!=destination { return 0 }
445 if gpa_i32_constant(body)!=0 || gpa_i32_constant(body)!=n { return 0 }
446 if gpa_bytes(body,"fc08")!=1 || gpa_u32(body)!=i || gpa_u32(body)!=0 { return 0 }
447 a.pos=a.pos+n;b.pos=b.pos+n;i=i+1
448 }
449 if a.pos!=a.size || b.pos!=b.size { return 0 }
450 if gpa_i32_constant(body)!=state { return 0 }
451 if gpa_bytes(body,"4102fe170200420221000b20000b")!=1 { return 0 }
452 if body.pos!=body.size { return 0 }
453 return 1
454}
455func gpa_data_initialization(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
456 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
457 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
458 let body: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
459 var ok: i64=0
460 if gpa_section(plain,pn,10,a)==1 && gpa_section(twin,tn,10,b)==1 {
461 if gpa_code_prefix(a,b,body)==1 {
462 if gpa_section(plain,pn,11,a)==1 && gpa_section(twin,tn,11,b)==1 {
463 ok=gpa_initial_data(a,b,body)
464 }
465 }
466 }
467 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor))
468 sys_munmap(body as *u8,__size_of(GpaWasmCursor))
469 return ok
470}
471func gpa_type_item(c: *GpaWasmCursor) -> i64 {
472 if gpa_bytes(c,"60")!=1 { return 0 }
473 var vector: i64=0
474 while vector<2 {
475 let n: i64=gpa_u32(c)
476 if n<0 || n>c.size-c.pos { return 0 }
477 var i: i64=0
478 while i<n {
479 let t: i64=c.data[c.pos] as i64
480 // Numeric, SIMD and shorthand reference types in the native compiler dialect.
481 if t!=127 && t!=126 && t!=125 && t!=124 && t!=123 && t!=112 && t!=111 { return 0 }
482 c.pos=c.pos+1;i=i+1
483 }
484 vector=vector+1
485 }
486 return 1
487}
488func gpa_type_extension(a: *GpaWasmCursor,b: *GpaWasmCursor,initializer_type: i64) -> i64 {
489 let original: i64=gpa_u32(a)
490 let candidate: i64=gpa_u32(b)
491 if original<0 || candidate<original || candidate>original+1 { return 0 }
492 if initializer_type<0 || initializer_type>=candidate { return 0 }
493 var i: i64=0
494 var initializer_ok: i64=0
495 while i<candidate {
496 let bs: i64=b.pos
497 if gpa_type_item(b)!=1 { return 0 }
498 if i<original {
499 let start: i64=a.pos
500 if gpa_type_item(a)!=1 { return 0 }
501 if gpa_same((a.data as i64+start) as *u8,a.pos-start,(b.data as i64+bs) as *u8,b.pos-bs)!=1 { return 0 }
502 }
503 if i==initializer_type {
504 let end: i64=b.pos
505 b.pos=bs
506 if gpa_bytes(b,"6000017e")==1 && b.pos==end { initializer_ok=1 }
507 b.pos=end
508 }
509 if i==original && i!=initializer_type { return 0 }
510 i=i+1
511 }
512 if a.pos!=a.size || b.pos!=b.size { return 0 }
513 return initializer_ok
514}
515func gpa_function_extension(a: *GpaWasmCursor,b: *GpaWasmCursor) -> i64 {
516 let original: i64=gpa_u32(a)
517 let candidate: i64=gpa_u32(b)
518 if original<0 || candidate!=original+1 { return -1 }
519 var i: i64=0
520 while i<original {
521 let t: i64=gpa_u32(a)
522 if t<0 || gpa_u32(b)!=t { return -1 }
523 i=i+1
524 }
525 let initializer_type: i64=gpa_u32(b)
526 if a.pos!=a.size || b.pos!=b.size { return -1 }
527 return initializer_type
528}
529func gpa_export_extension(a: *GpaWasmCursor,b: *GpaWasmCursor,initializer_index: i64) -> i64 {
530 let original: i64=gpa_u32(a)
531 if original<0 || gpa_u32(b)!=original+1 { return 0 }
532 let bytes: i64=a.size-a.pos
533 if bytes>b.size-b.pos { return 0 }
534 if gpa_same((a.data as i64+a.pos) as *u8,bytes,(b.data as i64+b.pos) as *u8,bytes)!=1 { return 0 }
535 b.pos=b.pos+bytes
536 if gpa_wasm_name(b,"_nx_data_init")!=1 { return 0 }
537 if gpa_bytes(b,"00")!=1 || gpa_u32(b)!=initializer_index { return 0 }
538 if b.pos!=b.size { return 0 }
539 return 1
540}
541func gpa_interface_extension(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
542 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
543 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor
544 var ok: i64=0
545 if gpa_section(plain,pn,3,a)==1 && gpa_section(twin,tn,3,b)==1 {
546 let initializer_index: i64=gpa_u32(a);a.pos=0
547 let initializer_type: i64=gpa_function_extension(a,b)
548 if initializer_type>=0 {
549 if gpa_section(plain,pn,1,a)==1 && gpa_section(twin,tn,1,b)==1 {
550 if gpa_type_extension(a,b,initializer_type)==1 {
551 if gpa_section(plain,pn,7,a)==1 && gpa_section(twin,tn,7,b)==1 {
552 ok=gpa_export_extension(a,b,initializer_index)
553 }
554 }
555 }
556 }
557 }
558 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor))
559 return ok
560}
561// Section ordering is the core Wasm binary order; data-count precedes code.
562func gpa_section_order(id: i64) -> i64 {
563 if id==12 { return 10 }
564 if id==10 { return 11 }
565 if id==11 { return 12 }
566 if id>=1 && id<=9 { return id }
567 return -1
568}
569func gpa_ordered_module(data: *u8,n: i64) -> i64 {
570 if gpa_wasm_header(data,n)!=1 { return 0 }
571 var c: GpaWasmCursor
572 c.data=data;c.size=n;c.pos=GPA_HEADER_BYTES
573 var previous: i64=0
574 while c.pos<n {
575 let id: i64=data[c.pos] as i64;c.pos=c.pos+1
576 let size: i64=gpa_u32(&c)
577 if size<0 || size>n-c.pos { return 0 }
578 if id!=0 {
579 let rank: i64=gpa_section_order(id)
580 if rank<=previous { return 0 }
581 previous=rank
582 }
583 c.pos=c.pos+size
584 }
585 return 1
586}
587// Return unchanged-section spans in source order, including every custom section.
588func gpa_unchanged_span(c: *GpaWasmCursor,out: *GpaWasmCursor) -> i64 {
589 while c.pos<c.size {
590 let start: i64=c.pos
591 let id: i64=c.data[c.pos] as i64;c.pos=c.pos+1
592 let size: i64=gpa_u32(c)
593 if size<0 || size>c.size-c.pos { return -1 }
594 c.pos=c.pos+size
595 // These sections have separate exact transformation checks.
596 if id!=1 && id!=2 && id!=3 && id!=5 && id!=7 && id!=10 && id!=11 && id!=12 {
597 // A start function can access data before explicit shared initialization.
598 if id==8 { return -1 }
599 out.data=(c.data as i64+start) as *u8;out.size=c.pos-start;out.pos=0
600 return 1
601 }
602 }
603 return 0
604}
605func gpa_unchanged_sections(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
606 var a: GpaWasmCursor;var b: GpaWasmCursor
607 var sa: GpaWasmCursor;var sb: GpaWasmCursor
608 a.data=plain;a.size=pn;a.pos=GPA_HEADER_BYTES
609 b.data=twin;b.size=tn;b.pos=GPA_HEADER_BYTES
610 while a.pos<=pn && b.pos<=tn {
611 let ra: i64=gpa_unchanged_span(&a,&sa)
612 let rb: i64=gpa_unchanged_span(&b,&sb)
613 if ra<0 || rb<0 || ra!=rb { return 0 }
614 if ra==0 { return 1 }
615 if gpa_same(sa.data,sa.size,sb.data,sb.size)!=1 { return 0 }
616 }
617 return 0
618}
619func gpa_extension_memory(plain: *u8,pn: i64,twin: *u8,tn: i64,memory: *GpaMemory) -> i64 {
620 var a: GpaWasmCursor;var b: GpaWasmCursor
621 var ma: GpaMemory;var mb: GpaMemory
622 ma.count=0;mb.count=0
623 if gpa_section(plain,pn,2,&a)!=0 || gpa_section(twin,tn,5,&b)!=0 { return 0 }
624 if gpa_section(plain,pn,5,&a)!=1 || gpa_section(twin,tn,2,&b)!=1 { return 0 }
625 if gpa_memory_section(a.data,a.size,0,&ma)!=1 { return 0 }
626 if gpa_memory_section(b.data,b.size,1,&mb)!=1 { return 0 }
627 // The compiler appends one aligned scalar cell; it can cross at most one page.
628 if mb.minimum<ma.minimum || mb.minimum>ma.minimum+1 { return 0 }
629 memory.minimum=mb.minimum;memory.maximum=mb.maximum;memory.count=1
630 return 1
631}
632func gpa_extension_counts(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
633 var a: GpaWasmCursor;var b: GpaWasmCursor
634 if gpa_section(twin,tn,11,&b)!=1 { return 0 }
635 let count: i64=gpa_u32(&b)
636 if count<0 || gpa_section(twin,tn,12,&b)!=1 { return 0 }
637 if gpa_u32(&b)!=count || b.pos!=b.size { return 0 }
638 if gpa_section(plain,pn,12,&a)==1 {
639 if gpa_u32(&a)!=count || a.pos!=a.size { return 0 }
640 }
641 return 1
642}
643// Structural evidence only: zero-init reservation provenance remains separately required.
644func gpa_extension_envelope(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 {
645 if gpa_ordered_module(plain,pn)!=1 || gpa_ordered_module(twin,tn)!=1 { return 0 }
646 if gpa_unchanged_sections(plain,pn,twin,tn)!=1 { return 0 }
647 var memory: GpaMemory
648 if gpa_extension_memory(plain,pn,twin,tn,&memory)!=1 { return 0 }
649 return gpa_extension_counts(plain,pn,twin,tn)
650}
651func gpa_pair_report(plain: *u8,pn: i64,twin: *u8,tn: i64,expected_plain: *u8,expected_twin: *u8,ph: *u8,th: *u8,memory: *GpaMemory) -> i64 {
652 if pn<=0 || tn<=0 || pn>=FSX_READ_CAP || tn>=FSX_READ_CAP {
653 fsx_puts("PAGE-PAIR REFUSED unreadable-or-capacity-bound-artifact; no pair verdict\n")
654 return GPA_RC_INPUT
655 }
656 gpa_sha(plain,pn,ph); gpa_sha(twin,tn,th)
657 if fsx_seq(ph,expected_plain)!=1 || fsx_seq(th,expected_twin)!=1 {
658 fsx_puts("PAGE-PAIR REFUSED artifact-digest-conflict; read the intended build receipts\n")
659 return GPA_RC_CONFLICT
660 }
661 if gpa_pair(plain,pn,twin,tn,memory)!=1 {
662 if gpa_application_code(plain,pn,twin,tn)==1 {
663 fsx_puts("PAGE-PAIR EVIDENCE original-function-bodies=byte-identical appended-functions=1 unresolved=initializer-types-exports-data-layout action=verify-compiler-extension publication=refused\n")
664 }
665 if gpa_data_initialization(plain,pn,twin,tn)==1 {
666 fsx_puts("PAGE-PAIR EVIDENCE static-payloads=byte-identical destinations=preserved initializer=exact-atomic-copy-program unresolved=types-exports-state-reservation publication=refused\n")
667 }
668 if gpa_interface_extension(plain,pn,twin,tn)==1 {
669 fsx_puts("PAGE-PAIR EVIDENCE application-types-exports=preserved initializer-signature=verified unresolved=state-reservation publication=refused\n")
670 }
671 fsx_puts("PAGE-PAIR REFUSED differing-body-or-unsupported-memory-contract; inspect both build inputs\n")
672 return GPA_RC_SHAPE
673 }
674 fsx_puts("PAGE-PAIR VERIFIED plain_sha256="); fsx_puts(ph)
675 fsx_puts(" shared_sha256="); fsx_puts(th)
676 fsx_puts(" minimum_pages="); fsx_putn(memory.minimum)
677 fsx_puts(" maximum_pages="); fsx_putn(memory.maximum)
678 fsx_puts(" body=byte-identical runtime-validation=separate publication=not-attempted\n")
679 return 0
680}
681func gpa_pair_main(argc: i64, argv: *i64) -> i64 {
682 if argc != 6 {
683 fsx_puts("usage: nx_game_page_emit pair <plain.wasm> <shared.wasm> <plain-sha256> <shared-sha256>\n")
684 return GPA_RC_USAGE
685 }
686 if fsx_denied(argv[2] as *u8)==1 || fsx_denied(argv[3] as *u8)==1 { return FSX_RC_DENIED }
687 let plain: *u8=sys_mmap(FSX_READ_CAP+1)
688 let twin: *u8=sys_mmap(FSX_READ_CAP+1)
689 let pn: i64=vw_read(argv[2] as *u8,plain,FSX_READ_CAP)
690 let tn: i64=vw_read(argv[3] as *u8,twin,FSX_READ_CAP)
691 let ph: *u8=sys_mmap(GPA_SHA_HEX+1)
692 let th: *u8=sys_mmap(GPA_SHA_HEX+1)
693 let memory: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory
694 let rc: i64=gpa_pair_report(plain,pn,twin,tn,argv[4] as *u8,argv[5] as *u8,ph,th,memory)
695 sys_munmap(plain,FSX_READ_CAP+1); sys_munmap(twin,FSX_READ_CAP+1)
696 sys_munmap(ph,GPA_SHA_HEX+1); sys_munmap(th,GPA_SHA_HEX+1)
697 sys_munmap(memory as *u8,__size_of(GpaMemory))
698 return rc
699}
700func gpa_rebind_compose(argc: i64,argv: *i64,page: *u8,pn: i64,wasm: *u8,wn: i64,out: *u8,cap: i64) -> i64 {
701 if argc==7 {
702 let n: i64=gpa_compose(page,pn,wasm,wn,out,cap)
703 if n<0 { return 0-GPA_RC_SHAPE }; return n
704 }
705 let path: *u8=argv[7] as *u8
706 if fsx_denied(path)==1 { return 0-FSX_RC_DENIED }
707 let twin: *u8=sys_mmap(FSX_READ_CAP)
708 let tn: i64=vw_read(path,twin,FSX_READ_CAP)
709 if tn<GPA_HEADER_BYTES || tn>=FSX_READ_CAP {
710 sys_munmap(twin,FSX_READ_CAP)
711 fsx_puts("PAGE-REBIND REFUSED shared-artifact-unreadable-or-capacity-bound\n" as *u8)
712 return 0-GPA_RC_INPUT
713 }
714 let hash: *u8=sys_mmap(GPA_SHA_HEX+1)
715 gpa_sha(twin,tn,hash)
716 var n: i64=0-GPA_RC_CONFLICT
717 if fsx_seq(hash,argv[8] as *u8)==1 {
718 n=gpa_compose_pair(page,pn,wasm,wn,twin,tn,argv[9] as *u8,out,cap)
719 } else { fsx_puts("PAGE-REBIND REFUSED shared-sha256-mismatch\n" as *u8) }
720 sys_munmap(hash,GPA_SHA_HEX+1);sys_munmap(twin,FSX_READ_CAP)
721 return n
722}
723func gpa_main(argc: i64, argv: *i64) -> i64 {
724 if argc != 7 && argc != 10 {
725 fsx_puts("usage: nx_game_page_emit rebind <base.html> <wasm> <out.html> <base-sha256> <wasm-sha256> [<shared.wasm> <shared-sha256> <shared-url>]\n" as *u8)
726 return GPA_RC_USAGE
727 }
728 let base: *u8 = argv[2] as *u8
729 let module: *u8 = argv[3] as *u8
730 let target: *u8 = argv[4] as *u8
731 if fsx_denied(base) == 1 { return FSX_RC_DENIED }
732 if fsx_denied(module) == 1 { return FSX_RC_DENIED }
733 if fsx_write_denied(target) == 1 { return FSX_RC_DENIED }
734 let page: *u8 = sys_mmap(FSX_READ_CAP+1)
735 let wasm: *u8 = sys_mmap(FSX_READ_CAP+1)
736 let pn: i64 = vw_read(base, page, FSX_READ_CAP)
737 let wn: i64 = vw_read(module, wasm, FSX_READ_CAP)
738 if pn <= 0 { fsx_puts("PAGE-REBIND REFUSED base-unreadable\n" as *u8); return GPA_RC_INPUT }
739 if wn < GPA_HEADER_BYTES { fsx_puts("PAGE-REBIND REFUSED wasm-unreadable\n" as *u8); return GPA_RC_INPUT }
740 if pn >= FSX_READ_CAP { fsx_puts("PAGE-REBIND REFUSED base-at-read-cap\n" as *u8); return GPA_RC_INPUT }
741 if wn >= FSX_READ_CAP { fsx_puts("PAGE-REBIND REFUSED wasm-at-read-cap\n" as *u8); return GPA_RC_INPUT }
742 let ph: *u8 = sys_mmap(GPA_SHA_HEX+1)
743 let wh: *u8 = sys_mmap(GPA_SHA_HEX+1)
744 gpa_sha(page, pn, ph); gpa_sha(wasm, wn, wh)
745 if fsx_seq(ph, argv[5] as *u8) == 0 { fsx_puts("PAGE-REBIND REFUSED base-sha256-mismatch\n" as *u8); return GPA_RC_CONFLICT }
746 if fsx_seq(wh, argv[6] as *u8) == 0 { fsx_puts("PAGE-REBIND REFUSED wasm-sha256-mismatch\n" as *u8); return GPA_RC_CONFLICT }
747 let out: *u8 = sys_mmap(FSX_READ_CAP+1)
748 let n: i64 = gpa_rebind_compose(argc, argv, page, pn, wasm, wn, out, FSX_READ_CAP)
749 if n < 0 { fsx_puts("PAGE-REBIND REFUSED composition; source and existing output retained\n" as *u8); return 0-n }
750 let prior: *u8 = sys_mmap(FSX_READ_CAP+1)
751 let oldn: i64 = vw_read(target, prior, FSX_READ_CAP+1)
752 if oldn >= 0 {
753 if gpa_same(prior, oldn, out, n) == 1 {
754 fsx_puts("PAGE-REBIND UNCHANGED writes=0\n" as *u8)
755 return 0
756 }
757 fsx_puts("PAGE-REBIND REFUSED output-exists-with-different-content; choose a new staging path\n" as *u8)
758 return GPA_RC_CONFLICT
759 }
760 let wrote: i64 = fxc_create(target, out, n)
761 if wrote == FXC_EXISTS {
762 let racedn: i64 = vw_read(target, prior, FSX_READ_CAP+1)
763 if gpa_same(out, n, prior, racedn) == 1 {
764 fsx_puts("PAGE-REBIND UNCHANGED concurrent-identical-writer\n" as *u8)
765 return 0
766 }
767 fsx_puts("PAGE-REBIND REFUSED concurrent-output-conflict; existing bytes retained\n" as *u8)
768 return GPA_RC_CONFLICT
769 }
770 if wrote != n {
771 fsx_puts("PAGE-REBIND FAILED exclusive-stage-write rc=" as *u8); fsx_putn(wrote)
772 fsx_puts("; no replacing-rename fallback; use a filesystem supporting exclusive installation\n" as *u8)
773 return FSX_RC_IO
774 }
775 let checkn: i64 = vw_read(target, prior, FSX_READ_CAP+1)
776 if gpa_same(out, n, prior, checkn) != 1 { fsx_puts("PAGE-REBIND FAILED readback-mismatch\n" as *u8); return FSX_RC_IO }
777 fsx_puts("PAGE-REBIND STAGED bytes=" as *u8); fsx_putn(n)
778 fsx_puts(" verified=byte-exact publication=not-attempted runtime-validation=required\n" as *u8)
779 return 0
780}
781
782const GPA_RELEASE_MARK: *u8 = "<meta name=\"nishi-release-sha256\" content=\"" as *u8
783// 0: legacy page without a field; negative: ambiguous/malformed field; positive: value offset.
784func gpa_release_slot(page: *u8,n: i64) -> i64 {
785 let ml: i64=vw_slen(GPA_RELEASE_MARK)
786 var found: i64=0; var i: i64=0
787 while i+ml<=n {
788 if gpa_same((page as i64+i) as *u8,ml,GPA_RELEASE_MARK,ml)==1 {
789 if found!=0 { return -1 }; found=i+ml
790 }
791 i=i+1
792 }
793 if found==0 { return 0 }
794 if found+GPA_SHA_HEX+2>n { return -1 }
795 if page[found+GPA_SHA_HEX]!=34 as u8 || page[found+GPA_SHA_HEX+1]!=62 as u8 { return -1 }
796 i=0; while i<GPA_SHA_HEX {
797 let c: i64=page[found+i] as i64
798 if (c<48 || c>57) && (c<97 || c>102) { return -1 }; i=i+1
799 }
800 return found
801}
802// Digest covers exact emitted HTML, with only this digest field normalized to ASCII zero.
803func gpa_seal(page: *u8,n: i64) -> i64 {
804 let slot: i64=gpa_release_slot(page,n); if slot<=0 { return slot }
805 var i: i64=0; while i<GPA_SHA_HEX { page[slot+i]=48 as u8; i=i+1 }
806 let hash: *u8=sys_mmap(GPA_SHA_HEX+1)
807 gpa_sha(page,n,hash)
808 i=0; while i<GPA_SHA_HEX { page[slot+i]=hash[i]; i=i+1 }
809 sys_munmap(hash,GPA_SHA_HEX+1); return 1
810}