nx_xps.nx source
↩ module page · 83 lines · 5490 B
1// nx_xps.nx -- XPS / XNALara MODEL TO THE SOVEREIGN ASSET (/compare/modding MD2 xps_to_nxa, 2026-09-06). Reads either
2// dialect through nx_xps_lib (the reference readers mirrored and read first) and writes NXANIM01 VERT, TRIS, SKEL and SKIN
3// through nx_nxa_write_lib, the ONE container writer. Scale is DATA: units_per_xps names how many 0.01 mm units one XPS unit
4// is (default 100000, i.e. one XPS unit read as one metre, announced on every run). Axes are carried as authored; the world
5// derives the up axis from the asset's own extents, as it does for every other donor. Normals, colours, uvs and tangents are
6// read and counted, not carried (TEXC is the texture rung). Every refusal is named and exits 3; nothing is written on refusal.
7// usage: nx_xps <in.xps|in.mesh.ascii> <out.nxa> [units_per_xps=<n>]
8// exits: 0 ok | 2 usage | 3 REFUSED (the reason is on the line) | 4 the container could not be written (the reason is on the line)
9// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
10import "nx_syscalls.nx"
11import "nx_xps_lib.nx"
12import "nx_nxa_write_lib.nx"
13
14const XPS_EXIT_USAGE: i64 = 2
15const XPS_EXIT_REFUSE: i64 = 3
16const XPS_EXIT_WRITE: i64 = 4
17const XPS_UNITS_KEY: *u8 = "units_per_xps="
18const XPS_UNITS_KEY_LEN: i64 = 14
19
20func xps_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
21func xps_puts(s: *u8) -> i64 { sys_write(1, s, xps_len(s)); return 0 }
22func xps_putn(v: i64) -> i64 {
23 let t: *u8 = sys_mmap(32)
24 var m: i64 = v
25 var w: i64 = 0
26 if m < 0 { t[0] = 45 as u8; sys_write(1, t, 1); m = 0 - m }
27 if m == 0 { t[0] = 48 as u8; sys_write(1, t, 1); return 0 }
28 let d: *u8 = sys_mmap(32)
29 var k: i64 = 0
30 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 var j: i64 = 0
32 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 }
33 sys_write(1, t, w)
34 return 0
35}
36func xps_kv(k: *u8, v: i64) -> i64 { xps_puts(k); xps_putn(v); return 0 }
37func xps_prefix(s: *u8, p: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { if s[i] != p[i] { return 0 } i = i + 1 } return 1 }
38func xps_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c < 48 { return v } if c > 57 { return v } v = v * 10 + (c - 48); i = i + 1 } return v }
39// trailing units_per_xps=<n> is a SCAN so the positional contract stays fixed
40func xps_units_arg(argc: i64, argv: *i64) -> i64 {
41 var i: i64 = 3
42 while i < argc { let a: *u8 = argv[i] as *u8; if xps_prefix(a, XPS_UNITS_KEY, XPS_UNITS_KEY_LEN) == 1 { return xps_atoi(a + XPS_UNITS_KEY_LEN) } i = i + 1 }
43 return 0
44}
45func xps_dialect_name(d: i64) -> *u8 { if d == XP_DIALECT_BIN { return "binary" as *u8 } return "ascii" as *u8 }
46// THE CONTRACT SYMBOL the modding board watches: one XPS file in, one NXANIM01 out, the receipt printed, the counts in m.
47// returns 0, or a NEGATIVE xp error (refused, nothing written), or a POSITIVE nxw error code negated past -100 for the writer
48func xps_to_nxa(inpath: *u8, outpath: *u8, units_per_xps: i64, m: *i64, wrc: *i64) -> i64 {
49 let lp: *i64 = sys_mmap(16) as *i64
50 let b: *u8 = sys_read_file(inpath, lp)
51 if (b as i64) == 0 { xps_puts("XPS-REFUSE unreadable: " as *u8); xps_puts(inpath); xps_puts("\n" as *u8); return XP_ERR_TRUNCATED }
52 let rc: i64 = xp_read(b, lp[0], units_per_xps, m)
53 xps_puts("XPS dialect=" as *u8); xps_puts(xps_dialect_name(m[XP_M_DIALECT]))
54 xps_kv(" version=" as *u8, m[XP_M_VMAJ]); xps_puts("." as *u8); xps_putn(m[XP_M_VMIN])
55 xps_kv(" tangent=" as *u8, m[XP_M_TANGENT]); xps_kv(" variable_weights=" as *u8, m[XP_M_VARW])
56 xps_kv(" bones=" as *u8, m[XP_M_NJ]); xps_kv(" meshes=" as *u8, m[XP_M_NMESH]); xps_kv(" textures=" as *u8, m[XP_M_NTEX])
57 xps_kv(" uv_layers_max=" as *u8, m[XP_M_UVMAX]); xps_kv(" verts=" as *u8, m[XP_M_NV]); xps_kv(" tris=" as *u8, m[XP_M_NT])
58 xps_kv(" weights_fixed=" as *u8, m[XP_M_WFIX]); xps_kv(" weights_truncated_to_4=" as *u8, m[XP_M_WTRUNC])
59 xps_kv(" units_per_xps=" as *u8, m[XP_M_MUL]); xps_puts(" axes=as-authored normals=read-not-carried uvs=read-not-carried\n" as *u8)
60 if rc < 0 {
61 xps_puts("XPS-REFUSE " as *u8); xps_puts(xp_err_name(rc))
62 if m[XP_M_ERRPOS] >= 0 { xps_kv(" at_byte=" as *u8, m[XP_M_ERRPOS]) }
63 xps_kv(" bad_joint_refs=" as *u8, m[XP_M_BADJOINT]); xps_puts("\n" as *u8)
64 return rc
65 }
66 let w: i64 = nxw_write(outpath, m[XP_M_VX] as *i64, m[XP_M_NV], m[XP_M_TR] as *i64, m[XP_M_NT], m[XP_M_JPAR] as *i64, m[XP_M_JPOS] as *i64, m[XP_M_NJ], m[XP_M_SJ] as *i64, m[XP_M_SW] as *i64, wrc)
67 if w < 0 {
68 xps_puts("XPS-NXA-REFUSE " as *u8); xps_puts(nxw_err_name(w)); xps_kv(" first_bad_record=" as *u8, wrc[NXW_R_FIRSTBAD]); xps_puts("\n" as *u8)
69 return 0 - 100 + w
70 }
71 xps_kv("XPS-NXA wrote=" as *u8, w); xps_kv(" sections=" as *u8, wrc[NXW_R_SECTIONS]); xps_kv(" words=" as *u8, wrc[NXW_R_WORDS])
72 xps_puts(" out=" as *u8); xps_puts(outpath); xps_puts("\n" as *u8)
73 return 0
74}
75func main(argc: i64, argv: *i64) -> i64 {
76 if argc < 3 { xps_puts("usage: nx_xps <in.xps|in.mesh.ascii> <out.nxa> [units_per_xps=<n>]\n" as *u8); return XPS_EXIT_USAGE }
77 let m: *i64 = sys_mmap(8 * XP_M_N) as *i64
78 let wrc: *i64 = sys_mmap(8 * NXW_R_N) as *i64
79 let rc: i64 = xps_to_nxa(argv[1] as *u8, argv[2] as *u8, xps_units_arg(argc, argv), m, wrc)
80 if rc == 0 { return 0 }
81 if rc <= 0 - 100 { return XPS_EXIT_WRITE }
82 return XPS_EXIT_REFUSE
83}