nx_varfacts_candidate_t139.nx source
↩ module page · 729 lines · 30183 B
1// nx_varfacts_lib.nx -- VaM .var PACKAGE FACT EXTRACTION CORE (conversion tier, VaM lane).
2//
3// A .var is a ZIP archive. THE WALL THIS LANE LIVES BEHIND (standing policy 1785794796 +
4// measure-dont-ship): VaM is a CAPABILITY TARGET -- asset BYTES (meshes/textures/morph deltas)
5// never enter our products or our published plane. What this core reads is the CENTRAL DIRECTORY
6// (file NAMES + sizes = the package's own manifest) and meta.json (the package's own declared
7// license) -- the package describing itself, never its content. Extracted MEASUREMENTS flow to
8// refcorpus behind the k>=8 admission wall like every other reference corpus.
9//
10// COMPOSES INCUMBENTS: EOCD/central-directory walk = the nx_zip_read shape (that organ is a
11// hardwired PyTorch-.bin test, not a library -- the walk is factored HERE as callables); ZIP
12// method-8 members inflate via nx_deflate_inflate, the SAME raw-DEFLATE core under nx_gzip_wrap
13// and nx_zlib_wrap (capacity-bounded; NOT the retired duplicate nx_inflate, debt 1785854636).
14//
15// ★TAIL-WINDOW CONTRACT: the EOCD + central directory live at the END of a ZIP, so a 519MB .var
16// measures from its last few MB. The caller hands the TAIL bytes + the true file size; if the
17// central directory starts before the window, the probe REFUSES (ok=0) -- a partial census is a
18// plausible undercount, which is worse than absence.
19//
20// Fact slots (flat i64, VF_N_SLOTS):
21// [0] ok [1] entries [2] morphs(.vmi) [3] scenes(Saves/scene/*.json) [4] textures(jpg/png/tif)
22// [5] clothing(/Clothing/) [6] hair(/Hair/) [7] plugins(.cs/.cslist/.dll) [8] appearance(.vap)
23// [9] meta_found [10] meta_lho [11] meta_method [12] meta_csize [13] meta_usize
24//
25// ★2026-09-11 EXTENSION (VaM census lane, ADDITIVE -- every legacy slot above keeps its meaning):
26// (1) THE WALK IS FACTORED INTO CALLABLES so vf_probe, the census CLI (nx_varcensus) and the gate share ONE walk:
27// vf_cd_locate reads the EOCD and names the outcome (OK / NO-EOCD / ZIP64 / WINDOW / SHORT) instead of one
28// ok=0 for five different reasons, and WINDOW hands the caller the exact window it must read (file_size -
29// cd_abs, derived from the archive itself, so no tail size has to be guessed); vf_cd_entry reads ONE record.
30// (2) vf_member_data: STORED bytes in place, DEFLATE through the incumbent nx_deflate_inflate sized from the member's
31// DECLARED usize and required to decode to exactly usize -- no capacity constant to tune. CRC is NOT verified here
32// (imprecision chosen: a corrupt meta.json fails its JSON parse and is counted as unparsed, it is never trusted).
33// (3) vf_json_*: a DEPTH-AWARE reader for meta.json. A key is matched only as a DIRECT member of the object it is asked
34// of, so a nested dependency's own licenseType can never answer for the package (vf_license below takes the FIRST
35// occurrence in the byte stream; the census prints both readings so their agreement is measured, not assumed).
36// license_tier: ORIGINAL No hw writes (Rule 26).
37import "nx_syscalls.nx"
38import "nx_deflate.nx"
39import "nx_crc32.nx"
40const VF_MAGIC_1024: i64 = 1024
41const VF_MAGIC_4194304: i64 = 4194304
42
43const VF_N_SLOTS: i64 = 14
44
45// ---- PKZIP layout (APPNOTE 4.3.6 / 4.3.12 / 4.3.16), named so every offset below reads as a field ----
46const VF_SIG_EOCD: i64 = 0x06054b50
47const VF_SIG_CD: i64 = 0x02014b50
48const VF_SIG_LOCAL: i64 = 0x04034b50
49const VF_EOCD_LEN: i64 = 22
50const VF_CD_LEN: i64 = 46
51const VF_LOCAL_LEN: i64 = 30
52const VF_COMMENT_MAX: i64 = 65535
53const VF_U16_ALL: i64 = 0xffff
54const VF_U32_ALL: i64 = 0xffffffff
55const VF_EOCD_ENTRIES: i64 = 10
56const VF_EOCD_CDSIZE: i64 = 12
57const VF_EOCD_CDOFF: i64 = 16
58const VF_CD_FLAGS: i64 = 8
59const VF_CD_METHOD: i64 = 10
60const VF_CD_CRC: i64 = 16
61const VF_CD_CSIZE: i64 = 20
62const VF_CD_USIZE: i64 = 24
63const VF_CD_FNL: i64 = 28
64const VF_CD_EXL: i64 = 30
65const VF_CD_CML: i64 = 32
66const VF_CD_LHO: i64 = 42
67const VF_LOCAL_FNL: i64 = 26
68const VF_LOCAL_EXL: i64 = 28
69const VF_METHOD_STORE: i64 = 0
70const VF_METHOD_DEFLATE: i64 = 8
71
72// vf_cd_locate box slots + states
73const VF_L_EOCD: i64 = 0
74const VF_L_ENTRIES: i64 = 1
75const VF_L_CDSIZE: i64 = 2
76const VF_L_CDABS: i64 = 3
77const VF_L_CD: i64 = 4
78const VF_L_STATE: i64 = 5
79const VF_L_N: i64 = 8
80const VF_LOC_OK: i64 = 0
81const VF_LOC_NOEOCD: i64 = 1
82const VF_LOC_ZIP64: i64 = 2
83const VF_LOC_WINDOW: i64 = 3
84const VF_LOC_SHORT: i64 = 4
85
86// vf_cd_entry record slots
87const VF_E_METHOD: i64 = 0
88const VF_E_CSIZE: i64 = 1
89const VF_E_USIZE: i64 = 2
90const VF_E_NAMELEN: i64 = 3
91const VF_E_LHO: i64 = 4
92const VF_E_NAMEOFF: i64 = 5
93const VF_E_NEXT: i64 = 6
94const VF_E_FLAGS: i64 = 7
95const VF_E_Z64: i64 = 8
96const VF_E_CRC: i64 = 9
97const VF_E_N: i64 = 10
98// ZIP64 extended information extra field (APPNOTE 4.5.3). MEASURED 2026-09-11 on real VaM packages: a writer can mark
99// EVERY central-directory size as 0xFFFFFFFF with the true values in extra 0x0001 even when the archive is small and its
100// EOCD is plain 32-bit -- so a reader that trusts the fixed fields reads 4294967295-byte members and every meta.json read fails.
101const VF_CD_DISK: i64 = 34
102const VF_X_ZIP64: i64 = 0x0001
103const VF_X_HDR: i64 = 4
104const VF_U64_LEN: i64 = 8
105const VF_Z64_NONE: i64 = 0
106const VF_Z64_RESOLVED: i64 = 1
107const VF_Z64_UNRESOLVED: i64 = 0 - 1
108
109// vf_member_data states
110const VF_MD_STORED: i64 = 0
111const VF_MD_INFLATED: i64 = 1
112const VF_MD_FAILED: i64 = 2
113const VF_MD_UNSUPPORTED: i64 = 3
114const VF_MD_SIZE_MISMATCH: i64 = 4
115const VF_MD_CRC_MISMATCH: i64 = 5
116
117// JSON structural bytes
118const VF_J_QUOTE: i64 = 34
119const VF_J_BSL: i64 = 92
120const VF_J_COLON: i64 = 58
121const VF_J_COMMA: i64 = 44
122const VF_J_LBRACE: i64 = 123
123const VF_J_RBRACE: i64 = 125
124const VF_J_LBRACK: i64 = 91
125const VF_J_RBRACK: i64 = 93
126const VF_J_SPACE: i64 = 32
127const VF_J_TAB: i64 = 9
128const VF_J_LF: i64 = 10
129const VF_J_CR: i64 = 13
130// JSON value kinds (vf_json_kind)
131const VF_JK_BAD: i64 = 0
132const VF_JK_STRING: i64 = 1
133const VF_JK_OBJECT: i64 = 2
134const VF_JK_ARRAY: i64 = 3
135const VF_JK_SCALAR: i64 = 4
136// vf_json_member box slots
137const VF_JM_KS: i64 = 0
138const VF_JM_KE: i64 = 1
139const VF_JM_VS: i64 = 2
140const VF_JM_VE: i64 = 3
141const VF_JM_N: i64 = 4
142// UTF-8 encoder structure (RFC 3629)
143const VF_UTF8_CONT: i64 = 128
144const VF_UTF8_LEAD2: i64 = 192
145const VF_UTF8_LEAD3: i64 = 224
146const VF_UTF8_LEAD4: i64 = 240
147const VF_UTF8_SEXTET: i64 = 64
148const VF_UTF8_MAX1: i64 = 128
149const VF_UTF8_MAX2: i64 = 2048
150const VF_UTF8_MAX3: i64 = 65536
151const VF_SURR_HI: i64 = 0xd800
152const VF_SURR_LO: i64 = 0xdc00
153const VF_SURR_END: i64 = 0xe000
154const VF_SURR_BASE: i64 = 0x10000
155const VF_SURR_SPAN: i64 = 1024
156const VF_HEX_DIGITS: i64 = 4
157const VF_HEX_BASE: i64 = 16
158
159func vf_u16(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) }
160func vf_u32(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) | ((b[o+3] as i64 & 0xff) << 24) }
161
162// name[0,nl) ends with suffix?
163func vf_ends(name: *u8, nl: i64, suf: *u8) -> i64 {
164 var m: i64 = 0
165 while suf[m] != (0 as u8) { m = m + 1 }
166 if nl < m { return 0 }
167 var i: i64 = 0
168 while i < m { if (name[nl-m+i] as i64) != (suf[i] as i64) { return 0 } i = i + 1 }
169 return 1
170}
171func vf_has(name: *u8, nl: i64, sub: *u8) -> i64 {
172 var m: i64 = 0
173 while sub[m] != (0 as u8) { m = m + 1 }
174 if nl < m { return 0 }
175 var i: i64 = 0
176 while i + m <= nl {
177 var k: i64 = 0
178 while k < m { if (name[i+k] as i64) != (sub[k] as i64) { k = m + 9 } else { k = k + 1 } }
179 if k == m { return 1 }
180 i = i + 1
181 }
182 return 0
183}
184
185// the first window a caller must read to be sure it holds the EOCD: the record plus the longest legal comment
186func vf_eocd_window(file_size: i64) -> i64 {
187 var w: i64 = VF_EOCD_LEN + VF_COMMENT_MAX
188 if w > file_size { w = file_size }
189 if w < 0 { w = 0 }
190 return w
191}
192
193// locate the central directory inside a tail window (tail[0] sits at absolute offset file_size - tailn). Fills box
194// (VF_L_*) and returns the NAMED state. WINDOW means the CD starts before the window: read file_size - box[VF_L_CDABS]
195// bytes from the end and locate again. ZIP64 is refused by name, never parsed as a 32-bit archive.
196func vf_cd_locate(tail: *u8, tailn: i64, file_size: i64, box: *i64) -> i64 {
197 var i: i64 = 0
198 while i < VF_L_N { box[i] = 0 - 1; i = i + 1 }
199 box[VF_L_STATE] = VF_LOC_NOEOCD
200 if tailn < VF_EOCD_LEN { return VF_LOC_NOEOCD }
201 var e: i64 = tailn - VF_EOCD_LEN
202 var eocd: i64 = 0 - 1
203 var run: i64 = 1
204 while run == 1 {
205 if e < 0 { run = 0 } else {
206 if vf_u32(tail, e) == VF_SIG_EOCD { eocd = e; run = 0 } else { e = e - 1 }
207 }
208 }
209 if eocd < 0 { return VF_LOC_NOEOCD }
210 box[VF_L_EOCD] = eocd
211 let entries: i64 = vf_u16(tail, eocd + VF_EOCD_ENTRIES)
212 let cd_size: i64 = vf_u32(tail, eocd + VF_EOCD_CDSIZE)
213 let cd_abs: i64 = vf_u32(tail, eocd + VF_EOCD_CDOFF)
214 box[VF_L_ENTRIES] = entries
215 box[VF_L_CDSIZE] = cd_size
216 box[VF_L_CDABS] = cd_abs
217 var z64: i64 = 0
218 if entries == VF_U16_ALL { z64 = 1 }
219 if cd_size == VF_U32_ALL { z64 = 1 }
220 if cd_abs == VF_U32_ALL { z64 = 1 }
221 if z64 == 1 { box[VF_L_STATE] = VF_LOC_ZIP64; return VF_LOC_ZIP64 }
222 let base: i64 = file_size - tailn
223 let cd: i64 = cd_abs - base
224 if cd < 0 { box[VF_L_STATE] = VF_LOC_WINDOW; return VF_LOC_WINDOW }
225 if cd + cd_size > tailn { box[VF_L_STATE] = VF_LOC_SHORT; return VF_LOC_SHORT }
226 box[VF_L_CD] = cd
227 box[VF_L_STATE] = VF_LOC_OK
228 return VF_LOC_OK
229}
230
231// ONE central-directory record at tail[off]. Fills rec (VF_E_*); returns 1, or 0 when the record or its name would
232// read outside the window or the signature is not a CD record (REFUSE, never a partial row).
233func vf_cd_entry(tail: *u8, tailn: i64, off: i64, rec: *i64) -> i64 {
234 if off < 0 { return 0 }
235 if off + VF_CD_LEN > tailn { return 0 }
236 if vf_u32(tail, off) != VF_SIG_CD { return 0 }
237 let fnl: i64 = vf_u16(tail, off + VF_CD_FNL)
238 let exl: i64 = vf_u16(tail, off + VF_CD_EXL)
239 let cml: i64 = vf_u16(tail, off + VF_CD_CML)
240 if off + VF_CD_LEN + fnl > tailn { return 0 }
241 rec[VF_E_CRC] = vf_u32(tail, off + VF_CD_CRC)
242 rec[VF_E_METHOD] = vf_u16(tail, off + VF_CD_METHOD)
243 rec[VF_E_CSIZE] = vf_u32(tail, off + VF_CD_CSIZE)
244 rec[VF_E_USIZE] = vf_u32(tail, off + VF_CD_USIZE)
245 rec[VF_E_NAMELEN] = fnl
246 rec[VF_E_LHO] = vf_u32(tail, off + VF_CD_LHO)
247 rec[VF_E_NAMEOFF] = off + VF_CD_LEN
248 rec[VF_E_NEXT] = off + VF_CD_LEN + fnl + exl + cml
249 rec[VF_E_FLAGS] = vf_u16(tail, off + VF_CD_FLAGS)
250 rec[VF_E_Z64] = vf_zip64_extra(tail, tailn, off + VF_CD_LEN + fnl, exl, rec)
251 return 1
252}
253
254func vf_u64(b: *u8, o: i64) -> i64 { return vf_u32(b, o) | (vf_u32(b, o + 4) << 32) }
255
256// resolve the CD fields that read 0xFFFFFFFF from extra field 0x0001 (values present ONLY for the overflowed fields, in the
257// fixed order usize, csize, lho). The need flags are taken BEFORE any field is rewritten, so resolving one cannot change
258// whether the next is read. Returns VF_Z64_NONE (nothing overflowed), VF_Z64_RESOLVED, or VF_Z64_UNRESOLVED (a field
259// overflowed and no well-formed 0x0001 record carries it -- the caller must refuse that member's sizes by name).
260func vf_zip64_extra(tail: *u8, tailn: i64, xs: i64, xl: i64, rec: *i64) -> i64 {
261 var nu: i64 = 0
262 var nc: i64 = 0
263 var nlh: i64 = 0
264 if rec[VF_E_USIZE] == VF_U32_ALL { nu = 1 }
265 if rec[VF_E_CSIZE] == VF_U32_ALL { nc = 1 }
266 if rec[VF_E_LHO] == VF_U32_ALL { nlh = 1 }
267 if nu + nc + nlh == 0 { return VF_Z64_NONE }
268 let xe: i64 = xs + xl
269 if xe > tailn { return VF_Z64_UNRESOLVED }
270 var p: i64 = xs
271 while p + VF_X_HDR <= xe {
272 let id: i64 = vf_u16(tail, p)
273 let sz: i64 = vf_u16(tail, p + 2)
274 let ds: i64 = p + VF_X_HDR
275 let de: i64 = ds + sz
276 if de > xe { return VF_Z64_UNRESOLVED }
277 if id == VF_X_ZIP64 {
278 if (nu + nc + nlh) * VF_U64_LEN > sz { return VF_Z64_UNRESOLVED }
279 var q: i64 = ds
280 if nu == 1 { rec[VF_E_USIZE] = vf_u64(tail, q); q = q + VF_U64_LEN }
281 if nc == 1 { rec[VF_E_CSIZE] = vf_u64(tail, q); q = q + VF_U64_LEN }
282 if nlh == 1 { rec[VF_E_LHO] = vf_u64(tail, q); q = q + VF_U64_LEN }
283 return VF_Z64_RESOLVED
284 }
285 p = de
286 }
287 return VF_Z64_UNRESOLVED
288}
289
290// probe the TAIL of a .var: tail holds the last tailn bytes, file_size is the true size. (Legacy contract, now a
291// consumer of vf_cd_locate + vf_cd_entry: every slot and every refusal is what it was.)
292func vf_probe(tail: *u8, tailn: i64, file_size: i64, facts: *i64) -> i64 {
293 var i: i64 = 0
294 while i < VF_N_SLOTS { facts[i] = 0 - 1; i = i + 1 }
295 facts[0] = 0
296 let box: *i64 = sys_mmap(VF_L_N * 8) as *i64
297 if vf_cd_locate(tail, tailn, file_size, box) != VF_LOC_OK { return 0 }
298 let entries: i64 = box[VF_L_ENTRIES]
299 let rec: *i64 = sys_mmap(VF_E_N * 8) as *i64
300 var morphs: i64 = 0
301 var scenes: i64 = 0
302 var tex: i64 = 0
303 var cloth: i64 = 0
304 var hair: i64 = 0
305 var plug: i64 = 0
306 var vap: i64 = 0
307 var off: i64 = box[VF_L_CD]
308 var k: i64 = 0
309 while k < entries {
310 if vf_cd_entry(tail, tailn, off, rec) != 1 { facts[0] = 0; return 0 }
311 let nm: *u8 = ((tail as i64) + rec[VF_E_NAMEOFF]) as *u8
312 let fnl: i64 = rec[VF_E_NAMELEN]
313 if vf_ends(nm, fnl, ".vmi" as *u8) == 1 { morphs = morphs + 1 }
314 if vf_has(nm, fnl, "Saves/scene/" as *u8) == 1 { if vf_ends(nm, fnl, ".json" as *u8) == 1 { scenes = scenes + 1 } }
315 if vf_ends(nm, fnl, ".jpg" as *u8) == 1 { tex = tex + 1 }
316 if vf_ends(nm, fnl, ".png" as *u8) == 1 { tex = tex + 1 }
317 if vf_ends(nm, fnl, ".tif" as *u8) == 1 { tex = tex + 1 }
318 if vf_has(nm, fnl, "/Clothing/" as *u8) == 1 { cloth = cloth + 1 }
319 if vf_has(nm, fnl, "/Hair/" as *u8) == 1 { hair = hair + 1 }
320 if vf_ends(nm, fnl, ".cs" as *u8) == 1 { plug = plug + 1 }
321 if vf_ends(nm, fnl, ".cslist" as *u8) == 1 { plug = plug + 1 }
322 if vf_ends(nm, fnl, ".dll" as *u8) == 1 { plug = plug + 1 }
323 if vf_ends(nm, fnl, ".vap" as *u8) == 1 { vap = vap + 1 }
324 if fnl == 9 { if vf_has(nm, fnl, "meta.json" as *u8) == 1 {
325 facts[9] = 1
326 facts[10] = rec[VF_E_LHO]
327 facts[11] = rec[VF_E_METHOD]
328 facts[12] = rec[VF_E_CSIZE]
329 facts[13] = rec[VF_E_USIZE]
330 } }
331 off = rec[VF_E_NEXT]
332 k = k + 1
333 }
334 facts[0] = 1
335 facts[1] = entries
336 facts[2] = morphs
337 facts[3] = scenes
338 facts[4] = tex
339 facts[5] = cloth
340 facts[6] = hair
341 facts[7] = plug
342 facts[8] = vap
343 if facts[9] != 1 { facts[9] = 0 }
344 return 0
345}
346
347// the data of ONE member whose compressed bytes are body[0..csize). out[0] = data length, out[1] = VF_MD_* state.
348// Returns the data pointer (body itself when STORED) or 0. DEFLATE output is sized from the DECLARED usize and must
349// decode to EXACTLY usize bytes, so a lying header refuses instead of truncating.
350func vf_member_data(body: *u8, csize: i64, method: i64, usize: i64, out: *i64) -> *u8 {
351 out[0] = 0
352 out[1] = VF_MD_FAILED
353 if csize < 0 { return 0 as *u8 }
354 if usize < 0 { return 0 as *u8 }
355 if method == VF_METHOD_STORE {
356 if csize != usize { out[1] = VF_MD_SIZE_MISMATCH; return 0 as *u8 }
357 out[0] = csize
358 out[1] = VF_MD_STORED
359 return body
360 }
361 if method != VF_METHOD_DEFLATE { out[1] = VF_MD_UNSUPPORTED; return 0 as *u8 }
362 if csize <= 0 { return 0 as *u8 }
363 if usize <= 0 { return 0 as *u8 }
364 let dr: *NxDeflateResult = nx_deflate_inflate(body, csize, usize)
365 if (dr as i64) == 0 { return 0 as *u8 }
366 if (dr.error_code as i64) != 0 { return 0 as *u8 }
367 if (dr.output_size as i64) != usize { return 0 as *u8 }
368 out[0] = usize
369 out[1] = VF_MD_INFLATED
370 return dr.output_data
371}
372
373// ---- depth-aware JSON reading for meta.json ----
374func vf_json_ws(c: i64) -> i64 {
375 if c == VF_J_SPACE { return 1 }
376 if c == VF_J_TAB { return 1 }
377 if c == VF_J_LF { return 1 }
378 if c == VF_J_CR { return 1 }
379 return 0
380}
381func vf_json_skip(src: *u8, n: i64, p: i64) -> i64 {
382 var q: i64 = p
383 var run: i64 = 1
384 while run == 1 {
385 if q >= n { run = 0 } else {
386 if vf_json_ws(src[q] as i64 & 0xff) == 1 { q = q + 1 } else { run = 0 }
387 }
388 }
389 return q
390}
391func vf_json_open(c: i64) -> i64 { if c == VF_J_LBRACE { return 1 } if c == VF_J_LBRACK { return 1 } return 0 }
392func vf_json_close(c: i64) -> i64 { if c == VF_J_RBRACE { return 1 } if c == VF_J_RBRACK { return 1 } return 0 }
393
394// end (exclusive, one past the closing quote) of the string whose opening quote is at p; -1 when unterminated
395func vf_json_str_end(src: *u8, n: i64, p: i64) -> i64 {
396 var j: i64 = p + 1
397 while j < n {
398 let c: i64 = src[j] as i64 & 0xff
399 if c == VF_J_BSL { j = j + 2 } else {
400 if c == VF_J_QUOTE { return j + 1 }
401 j = j + 1
402 }
403 }
404 return 0 - 1
405}
406
407// kind of the value that starts at p
408func vf_json_kind(src: *u8, n: i64, p: i64) -> i64 {
409 if p < 0 { return VF_JK_BAD }
410 if p >= n { return VF_JK_BAD }
411 let c: i64 = src[p] as i64 & 0xff
412 if c == VF_J_QUOTE { return VF_JK_STRING }
413 if c == VF_J_LBRACE { return VF_JK_OBJECT }
414 if c == VF_J_LBRACK { return VF_JK_ARRAY }
415 if vf_json_close(c) == 1 { return VF_JK_BAD }
416 if c == VF_J_COMMA { return VF_JK_BAD }
417 if c == VF_J_COLON { return VF_JK_BAD }
418 return VF_JK_SCALAR
419}
420
421// end (exclusive) of the JSON value starting at p; -1 when malformed or unterminated. Brackets are matched with string
422// awareness, so a brace inside a description never closes an object.
423func vf_json_value_end(src: *u8, n: i64, p: i64) -> i64 {
424 let kind: i64 = vf_json_kind(src, n, p)
425 if kind == VF_JK_BAD { return 0 - 1 }
426 if kind == VF_JK_STRING { return vf_json_str_end(src, n, p) }
427 if kind == VF_JK_SCALAR {
428 var s: i64 = p
429 while s < n {
430 let c: i64 = src[s] as i64 & 0xff
431 if c == VF_J_COMMA { return s }
432 if vf_json_close(c) == 1 { return s }
433 if vf_json_ws(c) == 1 { return s }
434 s = s + 1
435 }
436 return n
437 }
438 var depth: i64 = 0
439 var j: i64 = p
440 while j < n {
441 let c2: i64 = src[j] as i64 & 0xff
442 if c2 == VF_J_QUOTE {
443 let se: i64 = vf_json_str_end(src, n, j)
444 if se < 0 { return 0 - 1 }
445 j = se
446 } else {
447 if vf_json_open(c2) == 1 { depth = depth + 1 }
448 if vf_json_close(c2) == 1 {
449 depth = depth - 1
450 if depth == 0 { return j + 1 }
451 }
452 j = j + 1
453 }
454 }
455 return 0 - 1
456}
457
458// ONE member of an object whose value span ends at `end` (exclusive; src[end-1] is its closing brace), starting at p
459// (whitespace already skipped). box: key content span [KS,KE) (inside the quotes), value span [VS,VE). Returns where the
460// next member starts (end-1 after the last member), or -1 when there is no member here or the object is malformed.
461func vf_json_member(src: *u8, end: i64, p: i64, box: *i64) -> i64 {
462 if p < 0 { return 0 - 1 }
463 if p >= end - 1 { return 0 - 1 }
464 if (src[p] as i64 & 0xff) != VF_J_QUOTE { return 0 - 1 }
465 let ke: i64 = vf_json_str_end(src, end, p)
466 if ke < 0 { return 0 - 1 }
467 let c: i64 = vf_json_skip(src, end, ke)
468 if c >= end { return 0 - 1 }
469 if (src[c] as i64 & 0xff) != VF_J_COLON { return 0 - 1 }
470 let vs: i64 = vf_json_skip(src, end, c + 1)
471 let ve: i64 = vf_json_value_end(src, end, vs)
472 if ve < 0 { return 0 - 1 }
473 box[VF_JM_KS] = p + 1
474 box[VF_JM_KE] = ke - 1
475 box[VF_JM_VS] = vs
476 box[VF_JM_VE] = ve
477 var q: i64 = vf_json_skip(src, end, ve)
478 if q < end { if (src[q] as i64 & 0xff) == VF_J_COMMA { q = vf_json_skip(src, end, q + 1) } }
479 if q <= p { return 0 - 1 }
480 return q
481}
482
483// src[s..e) equals the NUL-terminated lit, byte for byte
484func vf_span_is(src: *u8, s: i64, e: i64, lit: *u8) -> i64 {
485 var m: i64 = 0
486 while lit[m] != (0 as u8) { m = m + 1 }
487 if e - s != m { return 0 }
488 var i: i64 = 0
489 while i < m { if (src[s+i] as i64 & 0xff) != (lit[i] as i64 & 0xff) { return 0 } i = i + 1 }
490 return 1
491}
492
493// the value start of the DIRECT member `key` of the object whose opening brace is at obj; box gets the member spans.
494// -1 when obj is not an object, the key is not a direct member, or the object is malformed before the key.
495func vf_json_find(src: *u8, n: i64, obj: i64, key: *u8, box: *i64) -> i64 {
496 if vf_json_kind(src, n, obj) != VF_JK_OBJECT { return 0 - 1 }
497 let end: i64 = vf_json_value_end(src, n, obj)
498 if end < 0 { return 0 - 1 }
499 var p: i64 = vf_json_skip(src, end, obj + 1)
500 var run: i64 = 1
501 while run == 1 {
502 if p >= end - 1 { run = 0 } else {
503 let nx: i64 = vf_json_member(src, end, p, box)
504 if nx < 0 { run = 0 } else {
505 if vf_span_is(src, box[VF_JM_KS], box[VF_JM_KE], key) == 1 { return box[VF_JM_VS] }
506 p = nx
507 }
508 }
509 }
510 return 0 - 1
511}
512
513// number of DIRECT members of the object at obj, or -1 when it is not a well-formed object
514func vf_json_count_members(src: *u8, n: i64, obj: i64) -> i64 {
515 if vf_json_kind(src, n, obj) != VF_JK_OBJECT { return 0 - 1 }
516 let end: i64 = vf_json_value_end(src, n, obj)
517 if end < 0 { return 0 - 1 }
518 let box: *i64 = sys_mmap(VF_JM_N * 8) as *i64
519 var p: i64 = vf_json_skip(src, end, obj + 1)
520 var cnt: i64 = 0
521 var run: i64 = 1
522 while run == 1 {
523 if p >= end - 1 { run = 0 } else {
524 let nx: i64 = vf_json_member(src, end, p, box)
525 if nx < 0 { return 0 - 1 }
526 cnt = cnt + 1
527 p = nx
528 }
529 }
530 return cnt
531}
532
533// number of elements of the array at arr, or -1 when it is not a well-formed array
534func vf_json_count_elems(src: *u8, n: i64, arr: i64) -> i64 {
535 if vf_json_kind(src, n, arr) != VF_JK_ARRAY { return 0 - 1 }
536 let end: i64 = vf_json_value_end(src, n, arr)
537 if end < 0 { return 0 - 1 }
538 var p: i64 = vf_json_skip(src, end, arr + 1)
539 var cnt: i64 = 0
540 var run: i64 = 1
541 while run == 1 {
542 if p >= end - 1 { run = 0 } else {
543 let ve: i64 = vf_json_value_end(src, end, p)
544 if ve < 0 { return 0 - 1 }
545 cnt = cnt + 1
546 var q: i64 = vf_json_skip(src, end, ve)
547 if q < end { if (src[q] as i64 & 0xff) == VF_J_COMMA { q = vf_json_skip(src, end, q + 1) } }
548 if q <= p { return 0 - 1 }
549 p = q
550 }
551 }
552 return cnt
553}
554
555func vf_hexval(c: i64) -> i64 {
556 if c >= 48 { if c <= 57 { return c - 48 } }
557 if c >= 65 { if c <= 70 { return c - 55 } }
558 if c >= 97 { if c <= 102 { return c - 87 } }
559 return 0 - 1
560}
561func vf_hex4(src: *u8, at: i64) -> i64 {
562 var v: i64 = 0
563 var i: i64 = 0
564 while i < VF_HEX_DIGITS {
565 let h: i64 = vf_hexval(src[at+i] as i64 & 0xff)
566 if h < 0 { return 0 - 1 }
567 v = v * VF_HEX_BASE + h
568 i = i + 1
569 }
570 return v
571}
572func vf_utf8(out: *u8, o: i64, cp: i64) -> i64 {
573 if cp < VF_UTF8_MAX1 { out[o] = cp as u8; return o + 1 }
574 if cp < VF_UTF8_MAX2 {
575 out[o] = (VF_UTF8_LEAD2 + (cp / VF_UTF8_SEXTET)) as u8
576 out[o+1] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8
577 return o + 2
578 }
579 if cp < VF_UTF8_MAX3 {
580 out[o] = (VF_UTF8_LEAD3 + (cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET))) as u8
581 out[o+1] = (VF_UTF8_CONT + ((cp / VF_UTF8_SEXTET) % VF_UTF8_SEXTET)) as u8
582 out[o+2] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8
583 return o + 3
584 }
585 out[o] = (VF_UTF8_LEAD4 + (cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET * VF_UTF8_SEXTET))) as u8
586 out[o+1] = (VF_UTF8_CONT + ((cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET)) % VF_UTF8_SEXTET)) as u8
587 out[o+2] = (VF_UTF8_CONT + ((cp / VF_UTF8_SEXTET) % VF_UTF8_SEXTET)) as u8
588 out[o+3] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8
589 return o + 4
590}
591
592// decode the string CONTENT span src[s..e) (inside the quotes) into out. The caller sizes out at e - s + 1: a decoded
593// string is never longer than its escaped span (\uXXXX is 6 bytes in and at most 3 out; a surrogate pair 12 in, 4 out).
594// A decoded TAB, CR or LF -- and any raw control byte -- becomes a space, so a value can never tear a TSV row.
595// Returns the decoded length, or -1 on a malformed escape.
596func vf_json_decode(src: *u8, s: i64, e: i64, out: *u8) -> i64 {
597 var i: i64 = s
598 var o: i64 = 0
599 while i < e {
600 let c: i64 = src[i] as i64 & 0xff
601 if c != VF_J_BSL {
602 if c < VF_J_SPACE { out[o] = VF_J_SPACE as u8 } else { out[o] = c as u8 }
603 o = o + 1
604 i = i + 1
605 } else {
606 if i + 1 >= e { return 0 - 1 }
607 let x: i64 = src[i+1] as i64 & 0xff
608 var cp: i64 = 0 - 1
609 var adv: i64 = 2
610 if x == VF_J_QUOTE { cp = VF_J_QUOTE }
611 if x == VF_J_BSL { cp = VF_J_BSL }
612 if x == 47 { cp = 47 }
613 if x == 98 { cp = VF_J_SPACE }
614 if x == 102 { cp = VF_J_SPACE }
615 if x == 110 { cp = VF_J_SPACE }
616 if x == 114 { cp = VF_J_SPACE }
617 if x == 116 { cp = VF_J_SPACE }
618 if x == 117 {
619 if i + 2 + VF_HEX_DIGITS > e { return 0 - 1 }
620 cp = vf_hex4(src, i + 2)
621 if cp < 0 { return 0 - 1 }
622 adv = 2 + VF_HEX_DIGITS
623 if cp >= VF_SURR_HI { if cp < VF_SURR_LO {
624 if i + 4 + 2 * VF_HEX_DIGITS > e { return 0 - 1 }
625 if (src[i+6] as i64 & 0xff) != VF_J_BSL { return 0 - 1 }
626 if (src[i+7] as i64 & 0xff) != 117 { return 0 - 1 }
627 let lo: i64 = vf_hex4(src, i + 8)
628 if lo < VF_SURR_LO { return 0 - 1 }
629 if lo >= VF_SURR_END { return 0 - 1 }
630 cp = VF_SURR_BASE + (cp - VF_SURR_HI) * VF_SURR_SPAN + (lo - VF_SURR_LO)
631 adv = 4 + 2 * VF_HEX_DIGITS
632 } }
633 if cp >= VF_SURR_LO { if cp < VF_SURR_END { return 0 - 1 } }
634 if cp < VF_J_SPACE { cp = VF_J_SPACE }
635 }
636 if cp < 0 { return 0 - 1 }
637 o = vf_utf8(out, o, cp)
638 i = i + adv
639 }
640 }
641 out[o] = 0 as u8
642 return o
643}
644
645// decode the DIRECT string member `key` of the object at obj into a fresh buffer sized from its own span.
646// Returns the buffer (NUL-terminated) or 0 when absent, not a string, or malformed; len[0] = decoded length or -1.
647func vf_json_get_str(src: *u8, n: i64, obj: i64, key: *u8, len: *i64) -> *u8 {
648 len[0] = 0 - 1
649 let box: *i64 = sys_mmap(VF_JM_N * 8) as *i64
650 let vs: i64 = vf_json_find(src, n, obj, key, box)
651 if vs < 0 { return 0 as *u8 }
652 if vf_json_kind(src, n, vs) != VF_JK_STRING { return 0 as *u8 }
653 let ve: i64 = box[VF_JM_VE]
654 let out: *u8 = sys_mmap(ve - vs + 1)
655 let d: i64 = vf_json_decode(src, vs + 1, ve - 1, out)
656 if d < 0 { return 0 as *u8 }
657 len[0] = d
658 return out
659}
660
661// extract the declared licenseType from a meta.json BODY (already read from the archive; method 0
662// = as-is, method 8 = raw deflate via the incumbent core). Writes a NUL-terminated label (max 40
663// chars, whitelist alphabet) or "unparsed". The body is the package DESCRIBING itself -- the one
664// member this lane ever opens.
665func vf_license(body: *u8, csize: i64, method: i64, usize: i64, out: *u8) -> i64 {
666 var src: *u8 = body
667 var n: i64 = csize
668 if method == 8 {
669 var cap: i64 = usize + 64
670 if cap < VF_MAGIC_1024 { cap = VF_MAGIC_1024 }
671 if cap > VF_MAGIC_4194304 { out[0] = 0 as u8; return 0 - 1 }
672 let dr: *NxDeflateResult = nx_deflate_inflate(body, csize, cap)
673 if (dr as i64) == 0 { out[0] = 0 as u8; return 0 - 1 }
674 if dr.error_code != 0 { out[0] = 0 as u8; return 0 - 1 }
675 src = dr.output_data
676 n = dr.output_size
677 } else { if method != 0 { out[0] = 0 as u8; return 0 - 1 } }
678 // find "licenseType" then the next quoted string
679 let key: *u8 = "licenseType" as *u8
680 var at: i64 = 0 - 1
681 var i: i64 = 0
682 while i + 11 <= n {
683 var k: i64 = 0
684 while k < 11 { if (src[i+k] as i64) != (key[k] as i64) { k = 20 } else { k = k + 1 } }
685 if k == 11 { at = i + 11; i = n + 1 } else { i = i + 1 }
686 }
687 if at < 0 { out[0] = 0 as u8; return 0 - 1 }
688 // the value: first ':' after the key, then the first '"' after that opens it
689 var colon: i64 = 0 - 1
690 i = at
691 while i < n { if (src[i] as i64) == 58 { colon = i; i = n + 1 } else { i = i + 1 } }
692 if colon < 0 { out[0] = 0 as u8; return 0 - 1 }
693 var vq: i64 = 0 - 1
694 i = colon + 1
695 while i < n { if (src[i] as i64) == 34 { vq = i + 1; i = n + 1 } else { i = i + 1 } }
696 if vq < 0 { out[0] = 0 as u8; return 0 - 1 }
697 var p: i64 = 0
698 i = vq
699 while i < n {
700 let c2: i64 = src[i] as i64
701 if c2 == 34 { out[p] = 0 as u8; return p }
702 var ok: i64 = 0
703 if c2 >= 48 { if c2 <= 57 { ok = 1 } }
704 if c2 >= 65 { if c2 <= 90 { ok = 1 } }
705 if c2 >= 97 { if c2 <= 122 { ok = 1 } }
706 if c2 == 32 { ok = 1 }
707 if c2 == 45 { ok = 1 }
708 if c2 == 46 { ok = 1 }
709 if ok == 0 { out[0] = 0 as u8; return 0 - 1 }
710 if p >= 40 { out[0] = 0 as u8; return 0 - 1 }
711 out[p] = c2 as u8
712 p = p + 1
713 i = i + 1
714 }
715 out[0] = 0 as u8
716 return 0 - 1
717}
718
719// Checked member API: legacy decoder slots remain [length,state]; checked output adds expected/actual CRC.
720// CRC covers decoded bytes and is integrity evidence, never provenance/authenticity evidence.
721func vf_member_checked(body: *u8, csize: i64, method: i64, usize: i64, expected: i64, out: *i64) -> *u8 {
722 out[2] = expected
723 out[3] = 0 - 1
724 let data: *u8 = vf_member_data(body, csize, method, usize, out)
725 if (data as i64) == 0 { return 0 as *u8 }
726 out[3] = nx_crc32(data, out[0])
727 if out[3] != expected { out[1] = VF_MD_CRC_MISMATCH; return 0 as *u8 }
728 return data
729}