code wiki / (root) / nx_vmd.nx

nx_vmd.nx source

↩ module page · 552 lines · 22549 B

1// nx_vmd.nx -- VMD (Vocaloid Motion Data) MOTION DECODER: the MMD dance corpus, read sovereignly. 2// 3// WHY THIS EXISTS (measured 2026-08-07 against vrstormlab.com/dancexr/play, from their own served 4// bytes -- not their marketing copy): the free LW build ingests PMX + XPS models and VMD + BVH 5// motion. Our estate already owns the OTHER half of that pipeline -- nx_pose_retarget inverts the 6// sovereign FK, nx_gltf2mesh segments a donor into 74 named parts -- and owned ZERO of their four 7// ingest formats. A grep for "bvh" found nx_bvh and read as a match; nx_bvh is a BOUNDING VOLUME 8// HIERARCHY for the slicer, not BioVision Hierarchy motion capture. 9// *A NAME IS NOT A CAPABILITY. A GREP FINDS THE CLAIM; ONLY READING THE SOURCE SETTLES IT. 10// 11// VMD is the keystone of the four because the dance corpus is overwhelmingly VMD, and because it 12// carries exactly what our retarget consumes: per-bone, per-frame rotations on a named skeleton. 13// 14// FAIL-CLOSED BY CONSTRUCTION: every field is bounds-checked against the ACTUAL file length before 15// it is read. A count in a header is a CLAIM, never an authority. A truncated file is REFUSED, not 16// partially parsed into a plausible-looking track -- because a plausible segmentation is the most 17// dangerous kind, and a motion track that silently stops at frame 40 of 400 still animates. 18// 19// UNITS ARE DECLARED, NEVER IMPLIED (a threshold is meaningless until its unit is stated): 20// positions = VMD/MMD model units x V_SCALE_POS (1000). NOT millimetres; MMD units are their own. 21// rotations = quaternion components x V_SCALE_ROT (4096), each in [-4096, 4096]. 22// morph = weight x V_SCALE_POS (1000), nominally [0, 1000]. 23// 24// usage: nx_vmd info <file.vmd> version, model name, per-section counts, duration in frames 25// nx_vmd bones <file.vmd> per-bone keyframe census (raw name bytes + id) 26// nx_vmd dump <file.vmd> [n] first n bone keyframes as rows 27// nx_vmd --kat selftest: 7 teeth, 4 of them anti-vacuity 28// exit 0 ok | 2 unreadable | 3 usage | 4 REFUSED (bad magic / truncated / count exceeds bytes) 29// | 1 selftest RED 30// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 31import "nx_syscalls.nx" 32const V_MAGIC_2000: i64 = 2000 33 34// ---- format constants (VMD is a fixed-record binary format; every record size is exact) ---- 35const V_HDR_SIG: i64 = 30 // signature block, NUL/garbage padded 36const V_NAME_V2: i64 = 20 // model-name field, signature "...0002" 37const V_NAME_V1: i64 = 10 // model-name field, older signature 38const V_BONE_REC: i64 = 111 // name15 + frame4 + pos12 + quat16 + interp64 39const V_MORPH_REC: i64 = 23 // name15 + frame4 + weight4 40const V_CAM_REC: i64 = 61 // frame4 + dist4 + pos12 + rot12 + interp24 + fov4 + persp1 41const V_LIGHT_REC: i64 = 28 // frame4 + rgb12 + pos12 42const V_SHADOW_REC: i64 = 9 // frame4 + mode1 + dist4 43const V_NAME_BYTES: i64 = 15 // bone/morph name field width (Shift-JIS, NUL padded) 44 45// ---- scales (declared above; never inferred at a call site) ---- 46const V_SCALE_POS: i64 = 1000 47const V_SCALE_ROT: i64 = 4096 48 49// ---- f32 decode ---- 50const V_M_255: i64 = 255 51const V_M_8388607: i64 = 8388607 52const V_M_8388608: i64 = 8388608 53const V_M_EXPBIAS: i64 = 127 54const V_M_MANTBIT: i64 = 23 55const V_MAX_SH: i64 = 40 // refuse absurd exponents rather than overflow the shift 56const V_MAX_RS: i64 = 62 // a shift wider than the word is undefined -- return 0, never garbage 57 58// ---- census ---- 59const V_MAX_UNIQ: i64 = 512 // unique bone names held; exceeding this REFUSES (declared cap) 60const V_DUMP_DEF: i64 = 8 61 62// ---- layout slots (L array) ---- 63const V_L_VER: i64 = 0 64const V_L_NAMEOFF: i64 = 1 65const V_L_BONEOFF: i64 = 2 66const V_L_BONEN: i64 = 3 67const V_L_MORPHOFF:i64 = 4 68const V_L_MORPHN: i64 = 5 69const V_L_CAMOFF: i64 = 6 70const V_L_CAMN: i64 = 7 71const V_L_LIGHTOFF:i64 = 8 72const V_L_LIGHTN: i64 = 9 73const V_L_SHADOFF: i64 = 10 74const V_L_SHADN: i64 = 11 75const V_L_MAXFRAME:i64 = 12 76const V_L_IKOFF: i64 = 13 77const V_L_IKN: i64 = 14 78const V_L_END: i64 = 15 79const V_L_WORDS: i64 = 20 80const V_IK_NAME: i64 = 20 // per-IK-entry bone name field width 81const V_IK_FIXED: i64 = 9 // frame4 + show1 + ikcount4 82 83// ---- KAT fixture ---- 84const V_BUF: i64 = 4096 85const V_MODE_644: i64 = 420 86const V_F32_1_0: i64 = 1065353216 87const V_F32_2_0: i64 = 1073741824 88const V_F32_0_5: i64 = 1056964608 89const V_F32_N1_0: i64 = 3212836864 90const V_KAT_FRAME: i64 = 30 91const V_KAT_MORPHFRAME: i64 = 10 92const V_KAT_LIARCOUNT: i64 = 100000 93 94// ===== tiny io helpers ============================================ 95func v_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } 96func v_puts(s: *u8) -> i64 { sys_write(1, s, v_slen(s)); return 0 } 97 98func v_num(v: i64) -> i64 { 99 let t: *u8 = sys_mmap(32) 100 var m: i64 = v 101 var w: i64 = 0 102 if m < 0 { t[0] = 45 as u8; sys_write(1, t, 1); m = 0 - m } 103 if m == 0 { t[0] = 48 as u8; sys_write(1, t, 1); return 0 } 104 let d: *u8 = sys_mmap(32) 105 var k: i64 = 0 106 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 107 var j: i64 = 0 108 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 } 109 sys_write(1, t, w) 110 return 0 111} 112 113// ===== byte readers =============================================== 114func v_u32(b: *u8, o: i64) -> i64 { 115 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8) 116 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24) 117} 118 119// IEEE-754 binary32 -> integer x scale. Guards BOTH shift directions: an exponent that would 120// shift wider than the word is returned as 0 rather than as undefined bits. That guard is the 121// banked a_f64 lesson applied before it can bite, not after. 122func v_f32(b: *u8, o: i64, scale: i64) -> i64 { 123 let w: i64 = v_u32(b, o) 124 let sign: i64 = (w >> 31) & 1 125 let expo: i64 = (w >> V_M_MANTBIT) & V_M_255 126 if expo == 0 { return 0 } 127 let mant: i64 = (w & V_M_8388607) | V_M_8388608 128 let sh: i64 = expo - V_M_EXPBIAS 129 if sh > V_MAX_SH { return 0 } 130 var v: i64 = 0 131 if sh >= V_M_MANTBIT { 132 v = mant * scale * (1 << (sh - V_M_MANTBIT)) 133 } else { 134 let rs: i64 = V_M_MANTBIT - sh 135 if rs > V_MAX_RS { return 0 } 136 v = (mant * scale) >> rs 137 } 138 if sign == 1 { return 0 - v } 139 return v 140} 141 142// ===== signature ================================================== 143// Both VMD generations begin "Vocaloid Motion Data"; only the tail distinguishes them. 144// Returns 2 (name field 20B), 1 (name field 10B), or 0 = NOT a VMD. 145func v_version(b: *u8, len: i64) -> i64 { 146 if len < V_HDR_SIG { return 0 } 147 let sig: *u8 = "Vocaloid Motion Data" as *u8 148 let n: i64 = v_slen(sig) 149 var i: i64 = 0 150 while i < n { 151 if b[i] != sig[i] { return 0 } 152 i = i + 1 153 } 154 if b[21] == (48 as u8) { 155 if b[22] == (48 as u8) { 156 if b[23] == (48 as u8) { 157 if b[24] == (50 as u8) { return 2 } 158 } 159 } 160 } 161 return 1 162} 163 164func v_name_width(ver: i64) -> i64 { 165 if ver == 2 { return V_NAME_V2 } 166 return V_NAME_V1 167} 168 169// ===== layout walk (THE fail-closed core) ========================= 170// Fills L. Returns 0 ok, or a negative refusal code: 171// -1 bad signature -2 truncated before a count -3 a count exceeds the bytes actually present 172// 173// The third case is the one that matters: a VMD header states its own record counts, and a 174// corrupt or deliberately-crafted file states counts far larger than its body. Trusting that 175// number is how a parser walks off the end of its buffer. Every section therefore proves 176// off + n*rec <= len BEFORE the first record is touched. 177func v_section(buf: *u8, len: i64, off: i64, rec: i64, L: *i64, slot_off: i64, slot_n: i64) -> i64 { 178 if off + 4 > len { return 0 - 2 } 179 let n: i64 = v_u32(buf, off) 180 let body: i64 = off + 4 181 if n < 0 { return 0 - 3 } 182 if rec > 0 { 183 if n > (len - body) / rec { return 0 - 3 } 184 } 185 L[slot_off] = body 186 L[slot_n] = n 187 return body + n * rec 188} 189 190func v_layout(buf: *u8, len: i64, L: *i64) -> i64 { 191 let ver: i64 = v_version(buf, len) 192 if ver == 0 { return 0 - 1 } 193 L[V_L_VER] = ver 194 L[V_L_NAMEOFF] = V_HDR_SIG 195 196 var off: i64 = V_HDR_SIG + v_name_width(ver) 197 off = v_section(buf, len, off, V_BONE_REC, L, V_L_BONEOFF, V_L_BONEN) 198 if off < 0 { return off } 199 off = v_section(buf, len, off, V_MORPH_REC, L, V_L_MORPHOFF, V_L_MORPHN) 200 if off < 0 { return off } 201 off = v_section(buf, len, off, V_CAM_REC, L, V_L_CAMOFF, V_L_CAMN) 202 if off < 0 { return off } 203 off = v_section(buf, len, off, V_LIGHT_REC, L, V_L_LIGHTOFF, V_L_LIGHTN) 204 if off < 0 { return off } 205 off = v_section(buf, len, off, V_SHADOW_REC, L, V_L_SHADOFF, V_L_SHADN) 206 if off < 0 { return off } 207 208 // ---- IK/show section: VARIABLE length, so it cannot be skipped by arithmetic ---- 209 // Each record is frame4 + show1 + ikcount4, then ikcount entries of (name20 + enabled1). 210 // Walking it is what lets the chain reconcile to EOF, and that reconciliation is the 211 // structural proof: if any earlier record size were wrong by a single byte, the chain 212 // would not land on the end of the file. *THE FILE CARRIES ITS OWN ANSWER KEY. 213 // Older VMD files legitimately END before this section -- absence is not corruption. 214 L[V_L_IKOFF] = off 215 L[V_L_IKN] = 0 216 if off + 4 <= len { 217 let ikn: i64 = v_u32(buf, off) 218 var p: i64 = off + 4 219 if ikn >= 0 { 220 var i2: i64 = 0 221 var bad: i64 = 0 222 while i2 < ikn { 223 if bad == 0 { 224 if p + V_IK_FIXED > len { bad = 1 } 225 else { 226 let nik: i64 = v_u32(buf, p + 5) 227 p = p + V_IK_FIXED 228 if nik < 0 { bad = 1 } 229 else { 230 if p + nik * (V_IK_NAME + 1) > len { bad = 1 } 231 else { p = p + nik * (V_IK_NAME + 1) } 232 } 233 } 234 } 235 i2 = i2 + 1 236 } 237 if bad == 0 { L[V_L_IKN] = ikn; off = p } 238 } 239 } 240 L[V_L_END] = off 241 242 var mx: i64 = 0 243 let bo: i64 = L[V_L_BONEOFF] 244 let bn: i64 = L[V_L_BONEN] 245 var i: i64 = 0 246 while i < bn { 247 let f: i64 = v_u32(buf, bo + i * V_BONE_REC + V_NAME_BYTES) 248 if f > mx { mx = f } 249 i = i + 1 250 } 251 L[V_L_MAXFRAME] = mx 252 return 0 253} 254 255func v_refuse(code: i64) -> i64 { 256 v_puts("nx_vmd REFUSED code=" as *u8); v_num(0 - code) 257 if code == 0 - 1 { v_puts(" not-a-VMD (signature)" as *u8) } 258 if code == 0 - 2 { v_puts(" truncated-before-count" as *u8) } 259 if code == 0 - 3 { v_puts(" count-exceeds-bytes" as *u8) } 260 v_puts("\n" as *u8) 261 return 4 262} 263 264// ===== name helpers =============================================== 265func v_name_eq(buf: *u8, a: i64, tbl: *u8, slot: i64) -> i64 { 266 var i: i64 = 0 267 while i < V_NAME_BYTES { 268 if buf[a + i] != tbl[slot * V_NAME_BYTES + i] { return 0 } 269 i = i + 1 270 } 271 return 1 272} 273 274func v_name_put(buf: *u8, a: i64, tbl: *u8, slot: i64) -> i64 { 275 var i: i64 = 0 276 while i < V_NAME_BYTES { tbl[slot * V_NAME_BYTES + i] = buf[a + i]; i = i + 1 } 277 return 0 278} 279 280// print the raw name bytes up to the NUL pad (Shift-JIS: emitted as bytes, NOT transcoded -- 281// an untranscoded byte string is honest; a mojibake guess would be a fabricated fact) 282func v_name_print(tbl: *u8, slot: i64) -> i64 { 283 var n: i64 = 0 284 while n < V_NAME_BYTES { 285 if tbl[slot * V_NAME_BYTES + n] == (0 as u8) { n = V_NAME_BYTES + 9 } else { n = n + 1 } 286 } 287 if n > V_NAME_BYTES { n = n - V_NAME_BYTES - 9 } 288 sys_write(1, ((tbl as i64) + slot * V_NAME_BYTES) as *u8, n) 289 return 0 290} 291 292// ===== verbs ====================================================== 293func v_info(buf: *u8, len: i64, L: *i64) -> i64 { 294 v_puts("VMD version=" as *u8); v_num(L[V_L_VER]) 295 v_puts(" bytes=" as *u8); v_num(len) 296 v_puts("\nmodel=" as *u8) 297 var i: i64 = 0 298 let w: i64 = v_name_width(L[V_L_VER]) 299 while i < w { 300 if buf[V_HDR_SIG + i] == (0 as u8) { i = w + 9 } else { i = i + 1 } 301 } 302 var nn: i64 = i 303 if nn > w { nn = nn - w - 9 } 304 sys_write(1, ((buf as i64) + V_HDR_SIG) as *u8, nn) 305 v_puts("\nbone_frames=" as *u8); v_num(L[V_L_BONEN]) 306 v_puts("\nmorph_frames=" as *u8); v_num(L[V_L_MORPHN]) 307 v_puts("\ncamera_frames=" as *u8); v_num(L[V_L_CAMN]) 308 v_puts("\nlight_frames=" as *u8); v_num(L[V_L_LIGHTN]) 309 v_puts("\nshadow_frames=" as *u8); v_num(L[V_L_SHADN]) 310 v_puts("\nik_frames=" as *u8); v_num(L[V_L_IKN]) 311 v_puts("\nduration_frames=" as *u8); v_num(L[V_L_MAXFRAME]) 312 // THE STRUCTURAL PROOF: every section size is fixed and declared, so the walked chain must 313 // land on the end of the file. A mismatch means one of the record sizes is wrong -- which is 314 // exactly the failure a synthetic fixture written by the same author CANNOT reveal. 315 v_puts("\nchain_end=" as *u8); v_num(L[V_L_END]) 316 v_puts(" bytes=" as *u8); v_num(len) 317 v_puts(" trailing=" as *u8); v_num(len - L[V_L_END]) 318 if L[V_L_END] == len { v_puts(" reconcile=EXACT" as *u8) } 319 else { v_puts(" reconcile=RESIDUAL-inspect-record-sizes" as *u8) } 320 v_puts("\nunits pos_x1000 rot_x4096 fps30\n" as *u8) 321 return 0 322} 323 324func v_bones(buf: *u8, L: *i64) -> i64 { 325 let tbl: *u8 = sys_mmap(V_MAX_UNIQ * V_NAME_BYTES) 326 let cnt: *i64 = sys_mmap(V_MAX_UNIQ * 8) as *i64 327 var uniq: i64 = 0 328 let bo: i64 = L[V_L_BONEOFF] 329 let bn: i64 = L[V_L_BONEN] 330 var i: i64 = 0 331 while i < bn { 332 let a: i64 = bo + i * V_BONE_REC 333 var s: i64 = 0 334 var hit: i64 = 0 - 1 335 while s < uniq { 336 if v_name_eq(buf, a, tbl, s) == 1 { hit = s; s = uniq } else { s = s + 1 } 337 } 338 if hit < 0 { 339 if uniq >= V_MAX_UNIQ { 340 v_puts("nx_vmd REFUSED unique-bone-cap " as *u8); v_num(V_MAX_UNIQ); v_puts("\n" as *u8) 341 return 4 342 } 343 v_name_put(buf, a, tbl, uniq) 344 cnt[uniq] = 1 345 uniq = uniq + 1 346 } else { 347 cnt[hit] = cnt[hit] + 1 348 } 349 i = i + 1 350 } 351 v_puts("unique_bones=" as *u8); v_num(uniq); v_puts("\n" as *u8) 352 var k: i64 = 0 353 while k < uniq { 354 v_num(k); v_puts("\t" as *u8); v_num(cnt[k]); v_puts("\t" as *u8) 355 v_name_print(tbl, k) 356 v_puts("\n" as *u8) 357 k = k + 1 358 } 359 return 0 360} 361 362func v_dump(buf: *u8, L: *i64, maxn: i64) -> i64 { 363 let bo: i64 = L[V_L_BONEOFF] 364 var bn: i64 = L[V_L_BONEN] 365 if maxn < bn { bn = maxn } 366 v_puts("idx\tframe\tpx\tpy\tpz\tqx\tqy\tqz\tqw\tname\n" as *u8) 367 var i: i64 = 0 368 while i < bn { 369 let a: i64 = bo + i * V_BONE_REC 370 v_num(i); v_puts("\t" as *u8) 371 v_num(v_u32(buf, a + V_NAME_BYTES)); v_puts("\t" as *u8) 372 v_num(v_f32(buf, a + V_NAME_BYTES + 4, V_SCALE_POS)); v_puts("\t" as *u8) 373 v_num(v_f32(buf, a + V_NAME_BYTES + 8, V_SCALE_POS)); v_puts("\t" as *u8) 374 v_num(v_f32(buf, a + V_NAME_BYTES + 12, V_SCALE_POS)); v_puts("\t" as *u8) 375 v_num(v_f32(buf, a + V_NAME_BYTES + 16, V_SCALE_ROT)); v_puts("\t" as *u8) 376 v_num(v_f32(buf, a + V_NAME_BYTES + 20, V_SCALE_ROT)); v_puts("\t" as *u8) 377 v_num(v_f32(buf, a + V_NAME_BYTES + 24, V_SCALE_ROT)); v_puts("\t" as *u8) 378 v_num(v_f32(buf, a + V_NAME_BYTES + 28, V_SCALE_ROT)); v_puts("\t" as *u8) 379 sys_write(1, ((buf as i64) + a) as *u8, V_NAME_BYTES) 380 v_puts("\n" as *u8) 381 i = i + 1 382 } 383 return 0 384} 385 386// ===== KAT ======================================================== 387func v_put_u32(b: *u8, o: i64, v: i64) -> i64 { 388 b[o] = (v & 0xff) as u8 389 b[o+1] = ((v >> 8) & 0xff) as u8 390 b[o+2] = ((v >> 16) & 0xff) as u8 391 b[o+3] = ((v >> 24) & 0xff) as u8 392 return 0 393} 394 395func v_put_str(b: *u8, o: i64, s: *u8) -> i64 { 396 let n: i64 = v_slen(s) 397 var i: i64 = 0 398 while i < n { b[o + i] = s[i]; i = i + 1 } 399 return n 400} 401 402// Build a VALID 2-bone-frame + 1-morph-frame VMD. Returns total byte length. 403func v_kat_build(b: *u8) -> i64 { 404 var i: i64 = 0 405 while i < V_BUF { b[i] = 0 as u8; i = i + 1 } 406 v_put_str(b, 0, "Vocaloid Motion Data 0002" as *u8) 407 v_put_str(b, V_HDR_SIG, "KAT" as *u8) 408 var o: i64 = V_HDR_SIG + V_NAME_V2 409 v_put_u32(b, o, 2); o = o + 4 410 v_put_str(b, o, "center" as *u8) 411 v_put_u32(b, o + V_NAME_BYTES, 0) 412 v_put_u32(b, o + V_NAME_BYTES + 4, V_F32_1_0) 413 v_put_u32(b, o + V_NAME_BYTES + 8, V_F32_2_0) 414 v_put_u32(b, o + V_NAME_BYTES + 12, V_F32_N1_0) 415 v_put_u32(b, o + V_NAME_BYTES + 28, V_F32_1_0) 416 o = o + V_BONE_REC 417 v_put_str(b, o, "center" as *u8) 418 v_put_u32(b, o + V_NAME_BYTES, V_KAT_FRAME) 419 v_put_u32(b, o + V_NAME_BYTES + 4, V_F32_0_5) 420 v_put_u32(b, o + V_NAME_BYTES + 28, V_F32_1_0) 421 o = o + V_BONE_REC 422 v_put_u32(b, o, 1); o = o + 4 423 v_put_str(b, o, "a" as *u8) 424 v_put_u32(b, o + V_NAME_BYTES, V_KAT_MORPHFRAME) 425 v_put_u32(b, o + V_NAME_BYTES + 4, V_F32_0_5) 426 o = o + V_MORPH_REC 427 v_put_u32(b, o, 0); o = o + 4 428 v_put_u32(b, o, 0); o = o + 4 429 v_put_u32(b, o, 0); o = o + 4 430 return o 431} 432 433func v_kat() -> i64 { 434 let b: *u8 = sys_mmap(V_BUF) 435 let L: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 436 let n: i64 = v_kat_build(b) 437 var red: i64 = 0 438 439 if v_layout(b, n, L) != 0 { v_puts("T1 RED layout-refused-valid\n" as *u8); red = red + 1 } 440 else { 441 if L[V_L_VER] != 2 { v_puts("T1 RED version\n" as *u8); red = red + 1 } 442 if L[V_L_BONEN] != 2 { v_puts("T1 RED bonecount\n" as *u8); red = red + 1 } 443 if L[V_L_MORPHN] != 1{ v_puts("T1 RED morphcount\n" as *u8); red = red + 1 } 444 } 445 if red == 0 { v_puts("T1 GREEN counts\n" as *u8) } 446 447 let a0: i64 = L[V_L_BONEOFF] + V_NAME_BYTES + 4 448 let px: i64 = v_f32(b, a0, V_SCALE_POS) 449 let py: i64 = v_f32(b, a0 + 4, V_SCALE_POS) 450 let pz: i64 = v_f32(b, a0 + 8, V_SCALE_POS) 451 let qw: i64 = v_f32(b, L[V_L_BONEOFF] + V_NAME_BYTES + 28, V_SCALE_ROT) 452 if px != 1000 { v_puts("T2 RED px=" as *u8); v_num(px); v_puts("\n" as *u8); red = red + 1 } 453 if py != V_MAGIC_2000 { v_puts("T2 RED py=" as *u8); v_num(py); v_puts("\n" as *u8); red = red + 1 } 454 if pz != 0 - 1000 { v_puts("T2 RED pz=" as *u8); v_num(pz); v_puts("\n" as *u8); red = red + 1 } 455 if qw != V_SCALE_ROT { v_puts("T2 RED qw=" as *u8); v_num(qw); v_puts("\n" as *u8); red = red + 1 } 456 v_puts("T2 f32-exact px=" as *u8); v_num(px); v_puts(" py=" as *u8); v_num(py) 457 v_puts(" pz=" as *u8); v_num(pz); v_puts(" qw=" as *u8); v_num(qw); v_puts("\n" as *u8) 458 459 if L[V_L_MAXFRAME] != V_KAT_FRAME { 460 v_puts("T3 RED duration=" as *u8); v_num(L[V_L_MAXFRAME]); v_puts("\n" as *u8); red = red + 1 461 } else { v_puts("T3 GREEN duration=30\n" as *u8) } 462 463 let L2: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 464 if v_layout(b, V_HDR_SIG + V_NAME_V2 + 4 + V_BONE_REC, L2) >= 0 { 465 v_puts("T4 RED truncated-file-accepted\n" as *u8); red = red + 1 466 } else { v_puts("T4 GREEN truncation-refused\n" as *u8) } 467 468 let sav: u8 = b[0] 469 b[0] = 88 as u8 470 let L3: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 471 if v_layout(b, n, L3) != 0 - 1 { 472 v_puts("T5 RED bad-signature-accepted\n" as *u8); red = red + 1 473 } else { v_puts("T5 GREEN signature-refused\n" as *u8) } 474 b[0] = sav 475 476 let bc_off: i64 = V_HDR_SIG + V_NAME_V2 477 v_put_u32(b, bc_off, V_KAT_LIARCOUNT) 478 let L4: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 479 if v_layout(b, n, L4) != 0 - 3 { 480 v_puts("T6 RED lying-count-accepted\n" as *u8); red = red + 1 481 } else { v_puts("T6 GREEN lying-count-refused\n" as *u8) } 482 v_put_u32(b, bc_off, 2) 483 484 let L5: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 485 let r5: i64 = v_layout(b, n, L5) 486 if r5 != 0 { v_puts("T7 RED fixture-not-restored\n" as *u8); red = red + 1 } 487 else { v_puts("T7 GREEN fixture-idempotent\n" as *u8) } 488 489 // T8 THE STRUCTURAL PROOF: the walked section chain must land EXACTLY on the end of the file. 490 // Every VMD record size is fixed and declared, so if V_BONE_REC were 110 or 112 instead of 111 491 // the chain would overshoot or undershoot -- and no count check would notice, because the 492 // counts would still be self-consistent. This is the tooth that a real file makes decisive: 493 // hand this organ any genuine VMD and the reconciliation validates every record size at once. 494 if r5 == 0 { 495 if L5[V_L_END] != n { 496 v_puts("T8 RED chain_end=" as *u8); v_num(L5[V_L_END]) 497 v_puts(" bytes=" as *u8); v_num(n); v_puts("\n" as *u8); red = red + 1 498 } else { 499 v_puts("T8 GREEN chain reconciles to EOF at " as *u8); v_num(n); v_puts("\n" as *u8) 500 } 501 } 502 503 if red > 0 { v_puts("nx_vmd KAT RED teeth_failed=" as *u8); v_num(red); v_puts("\n" as *u8); return 1 } 504 v_puts("nx_vmd KAT GREEN 8/8\n" as *u8) 505 return 0 506} 507 508// ===== main ======================================================= 509func main(argc: i64, argv: *i64) -> i64 { 510 if argc < 2 { 511 v_puts("usage: nx_vmd info|bones|dump <file.vmd> [n] | --kat\n" as *u8) 512 return 3 513 } 514 let verb: *u8 = argv[1] as *u8 515 516 if v_slen(verb) == 5 { 517 if verb[0] == (45 as u8) { return v_kat() } 518 } 519 if argc < 3 { 520 v_puts("usage: nx_vmd info|bones|dump <file.vmd> [n] | --kat\n" as *u8) 521 return 3 522 } 523 524 let path: *u8 = argv[2] as *u8 525 let lenp: *i64 = sys_mmap(16) as *i64 526 let buf: *u8 = sys_read_file(path, lenp) 527 if (buf as i64) == 0 { 528 v_puts("nx_vmd unreadable: " as *u8); v_puts(path); v_puts("\n" as *u8) 529 return 2 530 } 531 let len: i64 = lenp[0] 532 let L: *i64 = sys_mmap(V_L_WORDS * 8) as *i64 533 let rc: i64 = v_layout(buf, len, L) 534 if rc < 0 { return v_refuse(rc) } 535 536 if verb[0] == (105 as u8) { return v_info(buf, len, L) } 537 if verb[0] == (98 as u8) { return v_bones(buf, L) } 538 if verb[0] == (100 as u8) { 539 var n: i64 = V_DUMP_DEF 540 if argc > 3 { 541 let s: *u8 = argv[3] as *u8 542 var acc: i64 = 0 543 var i: i64 = 0 544 let sl: i64 = v_slen(s) 545 while i < sl { acc = acc * 10 + ((s[i] & 0xff) as i64) - 48; i = i + 1 } 546 if acc > 0 { n = acc } 547 } 548 return v_dump(buf, L, n) 549 } 550 v_puts("nx_vmd unknown verb\n" as *u8) 551 return 3 552}