nx_pmx.nx
buildroot/runtime/nx_pmx.nx
about
nx_pmx.nx -- PMX (MikuMikuDance 2.x) MODEL READER: header, section census, and the BONE TABLE.
WHY: measured 2026-08-07 from vrstormlab.com/dancexr/preparecontent -- PMX is DanceXR's primary
model format. It is also where the MMD standard BONE NAMES live, and bone names are what a
retarget binds to. nx_bvhfk gives us third-party motion in world space; PMX gives us the named
skeleton that motion is authored against.
*YOU CANNOT REACH THE BONES WITHOUT WALKING EVERYTHING, WHICH IS WHY THE WALK PROVES ITSELF.
PMX vertex records are VARIABLE LENGTH -- the size depends on a per-vertex weight-deform byte
(BDEF1/BDEF2/BDEF4/SDEF/QDEF) and on index widths declared in the header globals. Miscount one
vertex by one byte and the bone section is garbage. There is no way to "just skip to bones".
FAIL-CLOSED: every read is bounds-checked against the ACTUAL file length before it happens.
A count in a header is a CLAIM, never an authority -- the same discipline as nx_vmd, and here it
matters more because a lying vertex count would drive a variable-stride walk off the buffer.
TEXT: PMX globals[0] declares UTF-16LE (0) or UTF-8 (1). Names are emitted as UTF-8 either way;
UTF-16LE is transcoded (BMP). We do NOT guess an encoding -- the file states it.
usage: nx_pmx info <file.pmx> version, encoding, index widths, per-section counts
nx_pmx bones <file.pmx> bone table: index, parent, position, name
nx_pmx --kat selftest: 6 teeth, 3 of them anti-vacuity
exit 0 ok | 2 unreadable | 3 usage | 4 REFUSED | 1 KAT RED
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 1 imports · 0 importers
imports: nx_gate_verdict.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 26 | const PX_MAGIC_2147483648: i64 = 2147483648 |
| 27 | const PX_MAGIC_4294967296: i64 = 4294967296 |
| 28 | const PX_MAGIC_32768: i64 = 32768 |
| 29 | const PX_MAGIC_65536: i64 = 65536 |
| 30 | const PX_MAGIC_2048: i64 = 2048 |
| 31 | const PX_MAGIC_1073741824: i64 = 1073741824 |
| 32 | const PX_MAGIC_1000000: i64 = 1000000 |
| 34 | const PX_HDRSIG: i64 = 4 |
| 35 | const PX_MAXBONE: i64 = 1024 |
| 36 | const PX_NAMEW: i64 = 128 |
| 37 | const PX_ENC_U16: i64 = 0 |
| 38 | const PX_ENC_U8: i64 = 1 |
| 39 | const PX_KATBUF: i64 = 8192 |
| 42 | const PX_G_ENC: i64 = 0 |
| 43 | const PX_G_ADDUV: i64 = 1 |
| 44 | const PX_G_VIDX: i64 = 2 |
| 45 | const PX_G_TIDX: i64 = 3 |
| 46 | const PX_G_MIDX: i64 = 4 |
| 47 | const PX_G_BIDX: i64 = 5 |
| 48 | const PX_G_MOIDX: i64 = 6 |
| 49 | const PX_G_RIDX: i64 = 7 |
| 52 | const PX_BDEF1: i64 = 0 |
| 53 | const PX_BDEF2: i64 = 1 |
| 54 | const PX_BDEF4: i64 = 2 |
| 55 | const PX_SDEF: i64 = 3 |
| 56 | const PX_QDEF: i64 = 4 |
| 59 | const PX_BF_TAILIDX: i64 = 1 |
| 60 | const PX_BF_IK: i64 = 32 |
| 61 | const PX_BF_INH_ROT: i64 = 256 |
| 62 | const PX_BF_INH_TRN: i64 = 512 |
| 63 | const PX_BF_FIXAXIS: i64 = 1024 |
| 64 | const PX_BF_LOCALAX: i64 = 2048 |
| 65 | const PX_BF_EXTPAR: i64 = 8192 |
| 68 | const PX_R_MAGIC: i64 = 0 - 1 |
| 69 | const PX_R_TRUNC: i64 = 0 - 2 |
| 70 | const PX_R_COUNT: i64 = 0 - 3 |
| 71 | const PX_R_CAP: i64 = 0 - 4 |
| 72 | const PX_R_DEFORM:i64 = 0 - 5 |
| 197 | const PX_H_VER: i64 = 0 |
| 198 | const PX_H_NVERT: i64 = 1 |
| 199 | const PX_H_NSURF: i64 = 2 |
| 200 | const PX_H_NTEX: i64 = 3 |
| 201 | const PX_H_NMAT: i64 = 4 |
| 202 | const PX_H_NBONE: i64 = 5 |
| 203 | const PX_H_BONEOFF: i64 = 6 |
| 204 | const PX_H_WORDS: i64 = 12 |
functions
| 74 | func px_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } |
| 76 | func px_u8(b: *u8, o: i64) -> i64 { return (b[o] & 0xff) as i64 } |
| 77 | func px_u16(b: *u8, o: i64) -> i64 { return px_u8(b,o) | (px_u8(b,o+1) << 8) } |
| 78 | func px_u32(b: *u8, o: i64) -> i64 |
| 81 | func px_i32(b: *u8, o: i64) -> i64 |
| 87 | func px_idx(b: *u8, o: i64, w: i64) -> i64 |
| 94 | func px_need(C: *i64, len: i64, n: i64) -> i64 |
| 101 | func px_u16_to_u8(src: *u8, n: i64, dst: *u8, cap: i64) -> i64 |
| 130 | func px_text(b: *u8, C: *i64, len: i64, enc: i64, dst: *u8, cap: i64) -> i64 |
| 151 | func px_vertex(b: *u8, C: *i64, len: i64, G: *i64) -> i64 |
| 170 | func px_material(b: *u8, C: *i64, len: i64, G: *i64) -> i64 |
| 206 | func px_walk(b: *u8, len: i64, G: *i64, H: *i64, names: *u8, bparent: *i64, bpos: *i64) -> i64 |
| 350 | func px_refuse(c: i64) -> i64 |
| 362 | func px_put(b: *u8, o: i64, s: *u8) -> i64 |
| 368 | func px_p32(b: *u8, o: i64, v: i64) -> i64 |
| 373 | func px_ptext(b: *u8, o: i64, s: *u8) -> i64 |
| 383 | func px_fixture(b: *u8) -> i64 |
| 440 | func px_streq(a: *u8, s: *u8) -> i64 called by 1: px_kat |
| 447 | func px_kat() -> i64 |
| 510 | func main(argc: i64, argv: *i64) -> i64 |