code wiki / (root) / nx_xps_lib.nx

nx_xps_lib.nx source

↩ module page · 510 lines · 22607 B

1// nx_xps_lib.nx -- XPS / XNALara MODEL READER, both dialects (/compare/modding MD2, 2026-09-06). The reference readers were 2// mirrored and READ before a line of this was written (knowledge/fetched/cmp_modding_xnalara_read_ascii_xps.py pin ac92fd7f, 3// read_bin_xps.py pin 0e1dca05, bin_ops.py, xps_const.py). What they say, and this lib follows: 4// ascii (.mesh.ascii): bone count; per bone a name line, a parent line, an xyz line; mesh count; per mesh a name line, a 5// uv-layer count, a texture count with a name line and a uv-layer line per texture, a vertex count with position, normal, 6// RGBA colour (four integers), one uv line per layer and -- when the model has bones -- a four-index line and a 7// four-weight line (short lines are zero-filled), then a triangle count and three indices per face. Numeric lines may 8// carry a trailing comment after a hash. 9// binary (.xps): u32 magic 323232, u16 major, u16 minor, a length-prefixed string (one byte; a second byte when the first 10// is at least 128, length = b1 % 128 + b2 * 128), u32 settings length in 32-bit words, three more strings (machine, 11// user, files), then the settings block of exactly that many words (old format raw, new format typed items -- skipped 12// whole, it carries render flags, never geometry); bones: u32 count, string, i16 parent, three float32; meshes: u32 count, 13// string, u32 uv layers, u32 textures (string + u32 layer each), u32 vertices (three float32 position, three float32 14// normal, four colour bytes, per layer two float32 uv plus four float32 tangent when TANGENT versions apply), bone 15// weights as i16 count on VARIABLE-WEIGHT versions else four, i16 indices then float32 weights; u32 triangles of three u32. 16// TANGENT versions: minor <= 12 and major <= 2. VARIABLE weights: major >= 3. (bin_ops.py, read verbatim.) 17// OUTPUT is the estate's own arrays for nx_nxa_write_lib: VERT in 0.01 mm model units at a DECLARED scale (units_per_xps, 18// data never a guess), every mesh merged into one vertex table with per-mesh index offsets, SKEL from the bone table with the 19// identity bind rotation the estate's rig convention declares, SKIN as four q12 weights per vertex summing to exactly 4096 20// (a float sum that rounds away from 4096 has the residual moved to its largest lane and the vertex COUNTED as fixed). 21// Normals, colours, uvs and tangents are READ and COUNTED but not carried (TEXC is the texture rung, nx_nxa_texc). 22// Every refusal is named; a truncated binary is refused at the byte it ran out on, never read as a smaller model. 23// license_tier: ORIGINAL No hw writes (Rule 26). 24import "nx_syscalls.nx" 25 26const XP_MAGIC: i64 = 323232 27const XP_STR_LIMIT: i64 = 128 28const XP_TANGENT_MAJOR_MAX: i64 = 2 29const XP_TANGENT_MINOR_MAX: i64 = 12 30const XP_VARW_MAJOR_MIN: i64 = 3 31const XP_INF: i64 = 4 32const XP_Q12: i64 = 4096 33const XP_UNITS_PER_XPS_DEFAULT: i64 = 100000 // 1 XPS unit read as 1 metre = 100000 units of 0.01 mm; DECLARED, overridable 34const XP_F32_SIGN: i64 = 2147483648 35const XP_F32_EXP_DIV: i64 = 8388608 36const XP_F32_MAN_MASK: i64 = 8388607 37const XP_F32_HIDDEN: i64 = 8388608 38const XP_F32_BIAS_MAN: i64 = 150 39const XP_F32_SHIFT_CAP: i64 = 20 40const XP_FRAC_DIGITS_MAX: i64 = 9 41const XP_VERT_MIN_BYTES: i64 = 16 // the smallest vertex either dialect can spell; caps are DERIVED from the input size 42const XP_TRI_MIN_BYTES: i64 = 6 43const XP_DIALECT_ASCII: i64 = 1 44const XP_DIALECT_BIN: i64 = 2 45// model slots (plain i64 slots, never struct fields) 46const XP_M_NV: i64 = 0 47const XP_M_NT: i64 = 1 48const XP_M_NJ: i64 = 2 49const XP_M_NMESH: i64 = 3 50const XP_M_VX: i64 = 4 51const XP_M_TR: i64 = 5 52const XP_M_JPAR: i64 = 6 53const XP_M_JPOS: i64 = 7 54const XP_M_SJ: i64 = 8 55const XP_M_SW: i64 = 9 56const XP_M_HASBONES: i64 = 10 57const XP_M_DIALECT: i64 = 11 58const XP_M_VMAJ: i64 = 12 59const XP_M_VMIN: i64 = 13 60const XP_M_TANGENT: i64 = 14 61const XP_M_VARW: i64 = 15 62const XP_M_CAPV: i64 = 16 63const XP_M_CAPT: i64 = 17 64const XP_M_NTEX: i64 = 18 65const XP_M_WFIX: i64 = 19 66const XP_M_UVMAX: i64 = 20 67const XP_M_MUL: i64 = 21 68const XP_M_ERRPOS: i64 = 22 69const XP_M_BADJOINT: i64 = 23 70const XP_M_WTRUNC: i64 = 24 71const XP_M_N: i64 = 32 72// errors 73const XP_OK: i64 = 0 74const XP_ERR_TRUNCATED: i64 = 0 - 1 75const XP_ERR_CAP_VERTS: i64 = 0 - 2 76const XP_ERR_CAP_TRIS: i64 = 0 - 3 77const XP_ERR_MAGIC: i64 = 0 - 4 78const XP_ERR_BAD_JOINT: i64 = 0 - 5 79const XP_ERR_BAD_TRI: i64 = 0 - 6 80const XP_ERR_EMPTY: i64 = 0 - 7 81const XP_ERR_BAD_NUMBER: i64 = 0 - 8 82 83func xp_err_name(e: i64) -> *u8 { 84 if e == XP_ERR_TRUNCATED { return "truncated-input" as *u8 } 85 if e == XP_ERR_CAP_VERTS { return "vertex-count-exceeds-what-the-input-size-can-hold" as *u8 } 86 if e == XP_ERR_CAP_TRIS { return "triangle-count-exceeds-what-the-input-size-can-hold" as *u8 } 87 if e == XP_ERR_MAGIC { return "not-xps-magic" as *u8 } 88 if e == XP_ERR_BAD_JOINT { return "skin-joint-outside-bone-table" as *u8 } 89 if e == XP_ERR_BAD_TRI { return "triangle-index-outside-vertex-table" as *u8 } 90 if e == XP_ERR_EMPTY { return "no-geometry" as *u8 } 91 if e == XP_ERR_BAD_NUMBER { return "unparseable-number" as *u8 } 92 return "unnamed" as *u8 93} 94func xp_pow2(k: i64) -> i64 { var v: i64 = 1; var i: i64 = 0; while i < k { v = v * 2; i = i + 1 } return v } 95// IEEE-754 single from its 32 bits, scaled by mul and rounded to the nearest integer (zero, denormal, inf and nan read as 0) 96func xp_f32fx(bits: i64, mul: i64) -> i64 { 97 let sign: i64 = (bits / XP_F32_SIGN) & 1 98 let exp: i64 = (bits / XP_F32_EXP_DIV) & 255 99 let man: i64 = bits & XP_F32_MAN_MASK 100 if exp == 0 { return 0 } 101 if exp == 255 { return 0 } 102 var v: i64 = (man | XP_F32_HIDDEN) * mul 103 var sh: i64 = exp - XP_F32_BIAS_MAN 104 if sh > 0 { 105 if sh > XP_F32_SHIFT_CAP { sh = XP_F32_SHIFT_CAP } 106 v = v * xp_pow2(sh) 107 } else { 108 if sh < 0 { 109 let k: i64 = 0 - sh 110 if k >= 62 { v = 0 } else { let d: i64 = xp_pow2(k); v = (v + d / 2) / d } 111 } 112 } 113 if sign == 1 { return 0 - v } 114 return v 115} 116// ---- binary cursor reads: every read is bounds-checked and a short read sets XP_M_ERRPOS and returns 0 ---- 117func xp_bu8(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { 118 if p[0] + 1 > n { m[XP_M_ERRPOS] = p[0]; return 0 } 119 let v: i64 = (b[p[0]] & 0xff) as i64 120 p[0] = p[0] + 1 121 return v 122} 123func xp_bu16(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { 124 if p[0] + 2 > n { m[XP_M_ERRPOS] = p[0]; return 0 } 125 let v: i64 = ((b[p[0]] & 0xff) as i64) | (((b[p[0] + 1] & 0xff) as i64) * 256) 126 p[0] = p[0] + 2 127 return v 128} 129func xp_bi16(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { let v: i64 = xp_bu16(b, n, p, m); if v >= 32768 { return v - 65536 } return v } 130func xp_bu32(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { 131 if p[0] + 4 > n { m[XP_M_ERRPOS] = p[0]; return 0 } 132 let q: i64 = p[0] 133 let v: i64 = ((b[q] & 0xff) as i64) | (((b[q + 1] & 0xff) as i64) * 256) | (((b[q + 2] & 0xff) as i64) * 65536) | (((b[q + 3] & 0xff) as i64) * 16777216) 134 p[0] = q + 4 135 return v 136} 137func xp_bf32(b: *u8, n: i64, p: *i64, m: *i64, mul: i64) -> i64 { let bits: i64 = xp_bu32(b, n, p, m); return xp_f32fx(bits, mul) } 138// a length-prefixed string: one byte, a second when the first reaches the limit; the bytes are skipped (names are not carried) 139func xp_bstr_skip(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { 140 let b1: i64 = xp_bu8(b, n, p, m) 141 var b2: i64 = 0 142 if b1 >= XP_STR_LIMIT { b2 = xp_bu8(b, n, p, m) } 143 let len: i64 = (b1 % XP_STR_LIMIT) + b2 * XP_STR_LIMIT 144 if p[0] + len > n { m[XP_M_ERRPOS] = p[0]; return 0 } 145 p[0] = p[0] + len 146 return len 147} 148// ---- ascii cursor reads: a line at a time; numeric lines stop at a hash comment ---- 149func xp_is_ws(c: i64) -> i64 { if c == 32 { return 1 } if c == 9 { return 1 } if c == 13 { return 1 } return 0 } 150func xp_is_digit(c: i64) -> i64 { if c >= 48 { if c <= 57 { return 1 } } return 0 } 151// advance to the start of the next line 152func xp_next_line(b: *u8, n: i64, p: *i64) -> i64 { 153 var i: i64 = p[0] 154 while i < n { if (b[i] as i64) == 10 { i = i + 1; break } i = i + 1 } 155 p[0] = i 156 return i 157} 158// skip whitespace inside the current line 159func xp_skip_ws(b: *u8, n: i64, p: *i64) -> i64 { 160 var i: i64 = p[0] 161 while i < n { if xp_is_ws(b[i] as i64) == 1 { i = i + 1 } else { break } } 162 p[0] = i 163 return i 164} 165// one decimal number from the current line (optionally signed, fractional, exponent), scaled by mul and rounded; the cursor 166// stays on the line so a second number can follow. ok[0]=1 when digits were consumed. 167func xp_afx(b: *u8, n: i64, p: *i64, mul: i64, ok: *i64) -> i64 { 168 ok[0] = 0 169 xp_skip_ws(b, n, p) 170 var i: i64 = p[0] 171 var neg: i64 = 0 172 if i < n { if (b[i] as i64) == 45 { neg = 1; i = i + 1 } else { if (b[i] as i64) == 43 { i = i + 1 } } } 173 var ip: i64 = 0 174 var got: i64 = 0 175 while i < n { let c: i64 = b[i] as i64; if xp_is_digit(c) == 1 { ip = ip * 10 + (c - 48); got = 1; i = i + 1 } else { break } } 176 var frac: i64 = 0 177 var fd: i64 = 0 178 if i < n { if (b[i] as i64) == 46 { 179 i = i + 1 180 while i < n { let c2: i64 = b[i] as i64; if xp_is_digit(c2) == 1 { if fd < XP_FRAC_DIGITS_MAX { frac = frac * 10 + (c2 - 48); fd = fd + 1 } got = 1; i = i + 1 } else { break } } 181 } } 182 var e: i64 = 0 183 if i < n { if got == 1 { var c3: i64 = b[i] as i64; if c3 == 101 { i = i + 1 } else { if c3 == 69 { i = i + 1 } else { c3 = 0 } } 184 if c3 != 0 { 185 var eneg: i64 = 0 186 if i < n { if (b[i] as i64) == 45 { eneg = 1; i = i + 1 } else { if (b[i] as i64) == 43 { i = i + 1 } } } 187 while i < n { let c4: i64 = b[i] as i64; if xp_is_digit(c4) == 1 { e = e * 10 + (c4 - 48); i = i + 1 } else { break } } 188 if eneg == 1 { e = 0 - e } 189 } 190 } } 191 p[0] = i 192 if got == 0 { return 0 } 193 ok[0] = 1 194 var v: i64 = ip * mul 195 if fd > 0 { var den: i64 = 1; var k: i64 = 0; while k < fd { den = den * 10; k = k + 1 } v = v + (frac * mul + den / 2) / den } 196 while e > 0 { v = v * 10; e = e - 1 } 197 while e < 0 { v = (v + 5) / 10; e = e + 1 } 198 if neg == 1 { return 0 - v } 199 return v 200} 201// an integer line: the first number on the line, then the cursor moves to the next line 202func xp_aint(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { 203 if p[0] >= n { m[XP_M_ERRPOS] = p[0]; return 0 } 204 let ok: *i64 = sys_mmap(16) as *i64 205 let v: i64 = xp_afx(b, n, p, 1, ok) 206 if ok[0] == 0 { m[XP_M_ERRPOS] = p[0]; xp_next_line(b, n, p); return 0 } 207 xp_next_line(b, n, p) 208 return v 209} 210// k numbers from one line scaled by mul into out[]; missing trailing values read as 0 (the reference zero-fills); then next line 211func xp_aline(b: *u8, n: i64, p: *i64, m: *i64, k: i64, mul: i64, out: *i64) -> i64 { 212 if p[0] >= n { m[XP_M_ERRPOS] = p[0]; return 0 } 213 let ok: *i64 = sys_mmap(16) as *i64 214 var got: i64 = 0 215 var i: i64 = 0 216 while i < k { 217 out[i] = xp_afx(b, n, p, mul, ok) 218 if ok[0] == 1 { got = got + 1 } else { out[i] = 0 } 219 i = i + 1 220 } 221 if got == 0 { m[XP_M_ERRPOS] = p[0] } 222 xp_next_line(b, n, p) 223 return got 224} 225// a string line is skipped whole (names are not carried into SKEL) 226func xp_aline_skip(b: *u8, n: i64, p: *i64, m: *i64) -> i64 { if p[0] >= n { m[XP_M_ERRPOS] = p[0]; return 0 } xp_next_line(b, n, p); return 1 } 227 228// ---- shared: weights to q12 summing to exactly 4096 ---- 229func xp_fix_weights(sw: *i64, base: i64, m: *i64) -> i64 { 230 var sum: i64 = 0 231 var big: i64 = 0 232 var k: i64 = 0 233 while k < XP_INF { 234 if sw[base + k] < 0 { sw[base + k] = 0 } 235 sum = sum + sw[base + k] 236 if sw[base + k] > sw[base + big] { big = k } 237 k = k + 1 238 } 239 if sum == XP_Q12 { return 0 } 240 if sum == 0 { sw[base] = XP_Q12; m[XP_M_WFIX] = m[XP_M_WFIX] + 1; return 1 } 241 sw[base + big] = sw[base + big] + (XP_Q12 - sum) 242 m[XP_M_WFIX] = m[XP_M_WFIX] + 1 243 return 1 244} 245func xp_alloc(m: *i64, n: i64) -> i64 { 246 let capv: i64 = n / XP_VERT_MIN_BYTES + 16 247 let capt: i64 = n / XP_TRI_MIN_BYTES + 16 248 m[XP_M_CAPV] = capv 249 m[XP_M_CAPT] = capt 250 m[XP_M_VX] = sys_mmap(8 * 3 * capv) as i64 251 m[XP_M_TR] = sys_mmap(8 * 3 * capt) as i64 252 m[XP_M_SJ] = sys_mmap(8 * XP_INF * capv) as i64 253 m[XP_M_SW] = sys_mmap(8 * XP_INF * capv) as i64 254 return capv 255} 256// dialect sniff: the binary magic is an exact u32 at offset 0 257func xp_dialect(b: *u8, n: i64) -> i64 { 258 if n >= 4 { 259 let v: i64 = ((b[0] & 0xff) as i64) | (((b[1] & 0xff) as i64) * 256) | (((b[2] & 0xff) as i64) * 65536) | (((b[3] & 0xff) as i64) * 16777216) 260 if v == XP_MAGIC { return XP_DIALECT_BIN } 261 } 262 return XP_DIALECT_ASCII 263} 264 265// ---- the ascii dialect ---- 266func xp_read_ascii(b: *u8, n: i64, m: *i64) -> i64 { 267 let p: *i64 = sys_mmap(16) as *i64 268 p[0] = 0 269 if n >= 3 { if (b[0] & 0xff) as i64 == 0xef { if (b[1] & 0xff) as i64 == 0xbb { if (b[2] & 0xff) as i64 == 0xbf { p[0] = 3 } } } } 270 let mul: i64 = m[XP_M_MUL] 271 let nj: i64 = xp_aint(b, n, p, m) 272 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 273 m[XP_M_NJ] = nj 274 let jpar: *i64 = sys_mmap(8 * (nj + 1)) as *i64 275 let jpos: *i64 = sys_mmap(8 * 3 * (nj + 1)) as *i64 276 m[XP_M_JPAR] = jpar as i64 277 m[XP_M_JPOS] = jpos as i64 278 let tmp: *i64 = sys_mmap(8 * 8) as *i64 279 var j: i64 = 0 280 while j < nj { 281 xp_aline_skip(b, n, p, m) 282 jpar[j] = xp_aint(b, n, p, m) 283 xp_aline(b, n, p, m, 3, mul, tmp) 284 jpos[j * 3] = tmp[0]; jpos[j * 3 + 1] = tmp[1]; jpos[j * 3 + 2] = tmp[2] 285 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 286 j = j + 1 287 } 288 m[XP_M_HASBONES] = (nj > 0) as i64 289 let nmesh: i64 = xp_aint(b, n, p, m) 290 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 291 m[XP_M_NMESH] = nmesh 292 xp_alloc(m, n) 293 let vx: *i64 = m[XP_M_VX] as *i64 294 let tr: *i64 = m[XP_M_TR] as *i64 295 let sj: *i64 = m[XP_M_SJ] as *i64 296 let sw: *i64 = m[XP_M_SW] as *i64 297 var nv: i64 = 0 298 var nt: i64 = 0 299 var mi: i64 = 0 300 while mi < nmesh { 301 xp_aline_skip(b, n, p, m) 302 let uvl: i64 = xp_aint(b, n, p, m) 303 if uvl > m[XP_M_UVMAX] { m[XP_M_UVMAX] = uvl } 304 let ntex: i64 = xp_aint(b, n, p, m) 305 var t: i64 = 0 306 while t < ntex { xp_aline_skip(b, n, p, m); xp_aint(b, n, p, m); t = t + 1 } 307 m[XP_M_NTEX] = m[XP_M_NTEX] + ntex 308 let mv: i64 = xp_aint(b, n, p, m) 309 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 310 if nv + mv > m[XP_M_CAPV] { return XP_ERR_CAP_VERTS } 311 let base: i64 = nv 312 var v: i64 = 0 313 while v < mv { 314 xp_aline(b, n, p, m, 3, mul, tmp) 315 vx[nv * 3] = tmp[0]; vx[nv * 3 + 1] = tmp[1]; vx[nv * 3 + 2] = tmp[2] 316 xp_aline(b, n, p, m, 3, 1, tmp) // normal: read, not carried 317 xp_aline(b, n, p, m, 4, 1, tmp) // colour 318 var u: i64 = 0 319 while u < uvl { xp_aline(b, n, p, m, 2, 1, tmp); u = u + 1 } 320 if m[XP_M_HASBONES] == 1 { 321 xp_aline(b, n, p, m, XP_INF, 1, tmp) 322 var k: i64 = 0 323 while k < XP_INF { 324 var ji: i64 = tmp[k] 325 if ji < 0 { m[XP_M_BADJOINT] = m[XP_M_BADJOINT] + 1; ji = 0 } 326 if ji >= nj { m[XP_M_BADJOINT] = m[XP_M_BADJOINT] + 1; ji = 0 } 327 sj[nv * XP_INF + k] = ji 328 k = k + 1 329 } 330 xp_aline(b, n, p, m, XP_INF, XP_Q12, tmp) 331 k = 0 332 while k < XP_INF { sw[nv * XP_INF + k] = tmp[k]; k = k + 1 } 333 xp_fix_weights(sw, nv * XP_INF, m) 334 } 335 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 336 nv = nv + 1 337 v = v + 1 338 } 339 let mt: i64 = xp_aint(b, n, p, m) 340 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 341 if nt + mt > m[XP_M_CAPT] { return XP_ERR_CAP_TRIS } 342 var f: i64 = 0 343 while f < mt { 344 xp_aline(b, n, p, m, 3, 1, tmp) 345 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 346 var c: i64 = 0 347 while c < 3 { 348 let ix: i64 = tmp[c] 349 if ix < 0 { return XP_ERR_BAD_TRI } 350 if ix >= mv { return XP_ERR_BAD_TRI } 351 tr[nt * 3 + c] = base + ix 352 c = c + 1 353 } 354 nt = nt + 1 355 f = f + 1 356 } 357 mi = mi + 1 358 } 359 m[XP_M_NV] = nv 360 m[XP_M_NT] = nt 361 if nv == 0 { return XP_ERR_EMPTY } 362 if nt == 0 { return XP_ERR_EMPTY } 363 if m[XP_M_BADJOINT] > 0 { return XP_ERR_BAD_JOINT } 364 return XP_OK 365} 366 367// ---- the binary dialect ---- 368func xp_read_bin(b: *u8, n: i64, m: *i64) -> i64 { 369 let p: *i64 = sys_mmap(16) as *i64 370 p[0] = 0 371 let mul: i64 = m[XP_M_MUL] 372 let magic: i64 = xp_bu32(b, n, p, m) 373 if magic != XP_MAGIC { return XP_ERR_MAGIC } 374 let vmaj: i64 = xp_bu16(b, n, p, m) 375 let vmin: i64 = xp_bu16(b, n, p, m) 376 m[XP_M_VMAJ] = vmaj 377 m[XP_M_VMIN] = vmin 378 var tangent: i64 = 0 379 if vmaj <= XP_TANGENT_MAJOR_MAX { if vmin <= XP_TANGENT_MINOR_MAX { tangent = 1 } } 380 var varw: i64 = 0 381 if vmaj >= XP_VARW_MAJOR_MIN { varw = 1 } 382 m[XP_M_TANGENT] = tangent 383 m[XP_M_VARW] = varw 384 xp_bstr_skip(b, n, p, m) // XNAaraL 385 let settings_words: i64 = xp_bu32(b, n, p, m) 386 xp_bstr_skip(b, n, p, m) // machine 387 xp_bstr_skip(b, n, p, m) // user 388 xp_bstr_skip(b, n, p, m) // files 389 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 390 if p[0] + settings_words * 4 > n { m[XP_M_ERRPOS] = p[0]; return XP_ERR_TRUNCATED } 391 p[0] = p[0] + settings_words * 4 // the settings block carries render flags, never geometry 392 let nj: i64 = xp_bu32(b, n, p, m) 393 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 394 m[XP_M_NJ] = nj 395 let jpar: *i64 = sys_mmap(8 * (nj + 1)) as *i64 396 let jpos: *i64 = sys_mmap(8 * 3 * (nj + 1)) as *i64 397 m[XP_M_JPAR] = jpar as i64 398 m[XP_M_JPOS] = jpos as i64 399 var j: i64 = 0 400 while j < nj { 401 xp_bstr_skip(b, n, p, m) 402 jpar[j] = xp_bi16(b, n, p, m) 403 jpos[j * 3] = xp_bf32(b, n, p, m, mul) 404 jpos[j * 3 + 1] = xp_bf32(b, n, p, m, mul) 405 jpos[j * 3 + 2] = xp_bf32(b, n, p, m, mul) 406 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 407 j = j + 1 408 } 409 m[XP_M_HASBONES] = (nj > 0) as i64 410 let nmesh: i64 = xp_bu32(b, n, p, m) 411 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 412 m[XP_M_NMESH] = nmesh 413 xp_alloc(m, n) 414 let vx: *i64 = m[XP_M_VX] as *i64 415 let tr: *i64 = m[XP_M_TR] as *i64 416 let sj: *i64 = m[XP_M_SJ] as *i64 417 let sw: *i64 = m[XP_M_SW] as *i64 418 let wtmp: *i64 = sys_mmap(8 * 64) as *i64 419 var nv: i64 = 0 420 var nt: i64 = 0 421 var mi: i64 = 0 422 while mi < nmesh { 423 xp_bstr_skip(b, n, p, m) 424 let uvl: i64 = xp_bu32(b, n, p, m) 425 if uvl > m[XP_M_UVMAX] { m[XP_M_UVMAX] = uvl } 426 let ntex: i64 = xp_bu32(b, n, p, m) 427 var t: i64 = 0 428 while t < ntex { xp_bstr_skip(b, n, p, m); xp_bu32(b, n, p, m); t = t + 1 } 429 m[XP_M_NTEX] = m[XP_M_NTEX] + ntex 430 let mv: i64 = xp_bu32(b, n, p, m) 431 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 432 if nv + mv > m[XP_M_CAPV] { return XP_ERR_CAP_VERTS } 433 let base: i64 = nv 434 var v: i64 = 0 435 while v < mv { 436 vx[nv * 3] = xp_bf32(b, n, p, m, mul) 437 vx[nv * 3 + 1] = xp_bf32(b, n, p, m, mul) 438 vx[nv * 3 + 2] = xp_bf32(b, n, p, m, mul) 439 xp_bu32(b, n, p, m); xp_bu32(b, n, p, m); xp_bu32(b, n, p, m) // normal 440 xp_bu32(b, n, p, m) // RGBA colour bytes 441 var u: i64 = 0 442 while u < uvl { 443 xp_bu32(b, n, p, m); xp_bu32(b, n, p, m) 444 if tangent == 1 { xp_bu32(b, n, p, m); xp_bu32(b, n, p, m); xp_bu32(b, n, p, m); xp_bu32(b, n, p, m) } 445 u = u + 1 446 } 447 if m[XP_M_HASBONES] == 1 { 448 var wc: i64 = XP_INF 449 if varw == 1 { wc = xp_bi16(b, n, p, m) } 450 if wc < 0 { wc = 0 } 451 if wc > 64 { m[XP_M_ERRPOS] = p[0]; return XP_ERR_TRUNCATED } 452 var k: i64 = 0 453 while k < wc { wtmp[k] = xp_bi16(b, n, p, m); k = k + 1 } 454 k = 0 455 while k < wc { wtmp[32 + k] = xp_bf32(b, n, p, m, XP_Q12); k = k + 1 } 456 if wc > XP_INF { m[XP_M_WTRUNC] = m[XP_M_WTRUNC] + 1 } 457 k = 0 458 while k < XP_INF { 459 var ji: i64 = 0 460 var wq: i64 = 0 461 if k < wc { ji = wtmp[k]; wq = wtmp[32 + k] } 462 if ji < 0 { m[XP_M_BADJOINT] = m[XP_M_BADJOINT] + 1; ji = 0 } 463 if ji >= nj { m[XP_M_BADJOINT] = m[XP_M_BADJOINT] + 1; ji = 0 } 464 sj[nv * XP_INF + k] = ji 465 sw[nv * XP_INF + k] = wq 466 k = k + 1 467 } 468 xp_fix_weights(sw, nv * XP_INF, m) 469 } 470 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 471 nv = nv + 1 472 v = v + 1 473 } 474 let mt: i64 = xp_bu32(b, n, p, m) 475 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 476 if nt + mt > m[XP_M_CAPT] { return XP_ERR_CAP_TRIS } 477 var f: i64 = 0 478 while f < mt { 479 var c: i64 = 0 480 while c < 3 { 481 let ix: i64 = xp_bu32(b, n, p, m) 482 if m[XP_M_ERRPOS] >= 0 { return XP_ERR_TRUNCATED } 483 if ix >= mv { return XP_ERR_BAD_TRI } 484 tr[nt * 3 + c] = base + ix 485 c = c + 1 486 } 487 nt = nt + 1 488 f = f + 1 489 } 490 mi = mi + 1 491 } 492 m[XP_M_NV] = nv 493 m[XP_M_NT] = nt 494 if nv == 0 { return XP_ERR_EMPTY } 495 if nt == 0 { return XP_ERR_EMPTY } 496 if m[XP_M_BADJOINT] > 0 { return XP_ERR_BAD_JOINT } 497 return XP_OK 498} 499// the entry: sniff the dialect, read, leave every count and array in m; units_per_xps is DATA (0 selects the declared default) 500func xp_read(b: *u8, n: i64, units_per_xps: i64, m: *i64) -> i64 { 501 var i: i64 = 0 502 while i < XP_M_N { m[i] = 0; i = i + 1 } 503 m[XP_M_ERRPOS] = 0 - 1 504 m[XP_M_MUL] = units_per_xps 505 if units_per_xps <= 0 { m[XP_M_MUL] = XP_UNITS_PER_XPS_DEFAULT } 506 let d: i64 = xp_dialect(b, n) 507 m[XP_M_DIALECT] = d 508 if d == XP_DIALECT_BIN { return xp_read_bin(b, n, m) } 509 return xp_read_ascii(b, n, m) 510}