code wiki / (root) / nx_varfacts_candidate_t139.nx

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}