nx_assetprobe_lib.nx source
↩ module page · 251 lines · 10405 B
1// nx_assetprobe_lib.nx -- THE ONE CONTENT-TYPE RULER FOR INGESTION (extracted 2026-09-06 from nx_assetprobe.nx, byte-for-byte
2// where the functions already existed, so the program and the bundle walker share ONE identify/classify/claim and cannot
3// drift). AN EXTENSION IS A CLAIM. THE MAGIC BYTES ARE THE MEASUREMENT. Binary formats match at an EXACT offset; text formats
4// get a BOUNDED scan of the first AP_TEXTWIN bytes.
5// GROWN in the extraction, each an exact-offset magic or a bounded keyword pair, never a guess: OBJ and MTL (Wavefront text --
6// the first ingest fixture on /compare/modding MD1 is an OBJ plus its MTL and four maps, and a walker that cannot type them by
7// content would fall back to the filename it exists to distrust), DDS (the texture container every Bethesda mod ships), NIF
8// (the Bethesda mesh container nx_nif_lib already decodes), 7z and RAR (the archives Nexus files usually arrive in, typed as
9// BUNDLE so a walker can REPORT them by name rather than read them as noise -- their readers are owed, not faked).
10// license_tier: ORIGINAL No hw writes (Rule 26).
11import "nx_syscalls.nx"
12
13const AP_TEXTWIN: i64 = 4096
14
15const AP_UNKNOWN: i64 = 0
16const AP_PMX: i64 = 1
17const AP_PMD: i64 = 2
18const AP_FBXB: i64 = 3
19const AP_GLB: i64 = 4
20const AP_PNG: i64 = 5
21const AP_JPG: i64 = 6
22const AP_HDR: i64 = 7
23const AP_OGG: i64 = 8
24const AP_MP3: i64 = 9
25const AP_MP4: i64 = 10
26const AP_WAV: i64 = 11
27const AP_ZIP: i64 = 12
28const AP_VMD: i64 = 13
29const AP_BVH: i64 = 14
30const AP_OBJ: i64 = 15
31const AP_MTL: i64 = 16
32const AP_DDS: i64 = 17
33const AP_NIF: i64 = 18
34const AP_7Z: i64 = 19
35const AP_RAR: i64 = 20
36
37const AP_C_UNKNOWN: i64 = 0
38const AP_C_MODEL: i64 = 1
39const AP_C_MOTION: i64 = 2
40const AP_C_AUDIO: i64 = 3
41const AP_C_VIDEO: i64 = 4
42const AP_C_TEXTURE: i64 = 5
43const AP_C_BUNDLE: i64 = 6
44const AP_C_MATERIAL: i64 = 7
45
46func ap_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i }
47
48func ap_u16(b: *u8, o: i64) -> i64 { return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8) }
49func ap_u32(b: *u8, o: i64) -> i64 {
50 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8)
51 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24)
52}
53
54// exact-offset magic
55func ap_at(b: *u8, len: i64, off: i64, pat: *u8) -> i64 {
56 let n: i64 = ap_slen(pat)
57 if off + n > len { return 0 }
58 var i: i64 = 0
59 while i < n { if b[off + i] != pat[i] { return 0 } i = i + 1 }
60 return 1
61}
62
63// bounded text scan: a keyword within the first AP_TEXTWIN bytes
64func ap_scan(b: *u8, len: i64, pat: *u8) -> i64 {
65 let n: i64 = ap_slen(pat)
66 var win: i64 = AP_TEXTWIN
67 if len < win { win = len }
68 if n > win { return 0 }
69 var i: i64 = 0
70 while i + n <= win {
71 var k: i64 = 0
72 var ok: i64 = 1
73 while k < n {
74 if b[i + k] != pat[k] { ok = 0; k = n } else { k = k + 1 }
75 }
76 if ok == 1 { return 1 }
77 i = i + 1
78 }
79 return 0
80}
81
82// a keyword at the START of a line inside the text window: at offset 0, or preceded by a newline
83func ap_scan_bol(b: *u8, len: i64, pat: *u8) -> i64 {
84 let n: i64 = ap_slen(pat)
85 var win: i64 = AP_TEXTWIN
86 if len < win { win = len }
87 if n > win { return 0 }
88 var i: i64 = 0
89 while i + n <= win {
90 var bol: i64 = 0
91 if i == 0 { bol = 1 } else { if b[i - 1] == (10 as u8) { bol = 1 } }
92 if bol == 1 {
93 var k: i64 = 0
94 var ok: i64 = 1
95 while k < n {
96 if b[i + k] != pat[k] { ok = 0; k = n } else { k = k + 1 }
97 }
98 if ok == 1 { return 1 }
99 }
100 i = i + 1
101 }
102 return 0
103}
104
105func ap_identify(b: *u8, len: i64) -> i64 {
106 if len < 4 { return AP_UNKNOWN }
107 // byte-exact binary magics first (numeric compares avoid non-ASCII literals)
108 if (b[0] & 0xff) as i64 == 0x89 {
109 if ap_at(b, len, 1, "PNG" as *u8) == 1 { return AP_PNG }
110 }
111 if (b[0] & 0xff) as i64 == 0xff {
112 if (b[1] & 0xff) as i64 == 0xd8 { if (b[2] & 0xff) as i64 == 0xff { return AP_JPG } }
113 }
114 if (b[0] & 0xff) as i64 == 0x50 {
115 if (b[1] & 0xff) as i64 == 0x4b {
116 if (b[2] & 0xff) as i64 == 0x03 { if (b[3] & 0xff) as i64 == 0x04 { return AP_ZIP } }
117 }
118 }
119 // 7z: 37 7A BC AF 27 1C at offset 0
120 if (b[0] & 0xff) as i64 == 0x37 {
121 if (b[1] & 0xff) as i64 == 0x7a { if (b[2] & 0xff) as i64 == 0xbc { if (b[3] & 0xff) as i64 == 0xaf { return AP_7Z } } }
122 }
123 if ap_at(b, len, 0, "Rar!" as *u8) == 1 { return AP_RAR }
124 if ap_at(b, len, 0, "DDS " as *u8) == 1 { return AP_DDS }
125 if ap_at(b, len, 0, "Gamebryo File Format" as *u8) == 1 { return AP_NIF }
126 if ap_at(b, len, 0, "NetImmerse File Format" as *u8) == 1 { return AP_NIF }
127 if ap_at(b, len, 0, "PMX " as *u8) == 1 { return AP_PMX }
128 if ap_at(b, len, 0, "Pmd" as *u8) == 1 { return AP_PMD }
129 if ap_at(b, len, 0, "Kaydara FBX Binary" as *u8) == 1 { return AP_FBXB }
130 if ap_at(b, len, 0, "glTF" as *u8) == 1 { return AP_GLB }
131 if ap_at(b, len, 0, "OggS" as *u8) == 1 { return AP_OGG }
132 if ap_at(b, len, 0, "ID3" as *u8) == 1 { return AP_MP3 }
133 if ap_at(b, len, 4, "ftyp" as *u8) == 1 { return AP_MP4 }
134 if ap_at(b, len, 0, "RIFF" as *u8) == 1 {
135 if ap_at(b, len, 8, "WAVE" as *u8) == 1 { return AP_WAV }
136 }
137 if ap_at(b, len, 0, "Vocaloid Motion Data" as *u8) == 1 { return AP_VMD }
138 // text formats: bounded scan (BOMs, comments, transport preambles precede the keyword)
139 if ap_scan(b, len, "#?RADIANCE" as *u8) == 1 { return AP_HDR }
140 if ap_scan(b, len, "HIERARCHY" as *u8) == 1 { return AP_BVH }
141 if ap_scan(b, len, "; FBX" as *u8) == 1 { return AP_FBXB }
142 // Wavefront: a material library declares newmtl at a line start; a mesh declares vertices (v ) AND faces or texture
143 // coordinates at line starts -- one keyword alone is a comment away from a false positive, two is a shape
144 if ap_scan_bol(b, len, "newmtl " as *u8) == 1 { return AP_MTL }
145 if ap_scan_bol(b, len, "v " as *u8) == 1 {
146 if ap_scan_bol(b, len, "f " as *u8) == 1 { return AP_OBJ }
147 if ap_scan_bol(b, len, "vt " as *u8) == 1 { return AP_OBJ }
148 if ap_scan_bol(b, len, "vn " as *u8) == 1 { return AP_OBJ }
149 }
150 return AP_UNKNOWN
151}
152
153func ap_name(c: i64) -> *u8 {
154 if c == AP_PMX { return "PMX" as *u8 }
155 if c == AP_PMD { return "PMD" as *u8 }
156 if c == AP_FBXB { return "FBX" as *u8 }
157 if c == AP_GLB { return "GLB" as *u8 }
158 if c == AP_PNG { return "PNG" as *u8 }
159 if c == AP_JPG { return "JPG" as *u8 }
160 if c == AP_HDR { return "HDR" as *u8 }
161 if c == AP_OGG { return "OGG" as *u8 }
162 if c == AP_MP3 { return "MP3" as *u8 }
163 if c == AP_MP4 { return "MP4" as *u8 }
164 if c == AP_WAV { return "WAV" as *u8 }
165 if c == AP_ZIP { return "ZIP" as *u8 }
166 if c == AP_VMD { return "VMD" as *u8 }
167 if c == AP_BVH { return "BVH" as *u8 }
168 if c == AP_OBJ { return "OBJ" as *u8 }
169 if c == AP_MTL { return "MTL" as *u8 }
170 if c == AP_DDS { return "DDS" as *u8 }
171 if c == AP_NIF { return "NIF" as *u8 }
172 if c == AP_7Z { return "7Z" as *u8 }
173 if c == AP_RAR { return "RAR" as *u8 }
174 return "UNKNOWN" as *u8
175}
176
177func ap_cat(c: i64) -> i64 {
178 if c == AP_PMX { return AP_C_MODEL }
179 if c == AP_PMD { return AP_C_MODEL }
180 if c == AP_FBXB { return AP_C_MODEL }
181 if c == AP_GLB { return AP_C_MODEL }
182 if c == AP_OBJ { return AP_C_MODEL }
183 if c == AP_NIF { return AP_C_MODEL }
184 if c == AP_MTL { return AP_C_MATERIAL }
185 if c == AP_VMD { return AP_C_MOTION }
186 if c == AP_BVH { return AP_C_MOTION }
187 if c == AP_OGG { return AP_C_AUDIO }
188 if c == AP_MP3 { return AP_C_AUDIO }
189 if c == AP_WAV { return AP_C_AUDIO }
190 if c == AP_MP4 { return AP_C_VIDEO }
191 if c == AP_PNG { return AP_C_TEXTURE }
192 if c == AP_JPG { return AP_C_TEXTURE }
193 if c == AP_HDR { return AP_C_TEXTURE }
194 if c == AP_DDS { return AP_C_TEXTURE }
195 if c == AP_ZIP { return AP_C_BUNDLE }
196 if c == AP_7Z { return AP_C_BUNDLE }
197 if c == AP_RAR { return AP_C_BUNDLE }
198 return AP_C_UNKNOWN
199}
200
201func ap_catname(k: i64) -> *u8 {
202 if k == AP_C_MODEL { return "model" as *u8 }
203 if k == AP_C_MOTION { return "motion" as *u8 }
204 if k == AP_C_AUDIO { return "audio" as *u8 }
205 if k == AP_C_VIDEO { return "video" as *u8 }
206 if k == AP_C_TEXTURE { return "texture" as *u8 }
207 if k == AP_C_BUNDLE { return "bundle" as *u8 }
208 if k == AP_C_MATERIAL { return "material" as *u8 }
209 return "unknown" as *u8
210}
211
212// lowercase-insensitive suffix test
213func ap_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
214func ap_ends(path: *u8, ext: *u8) -> i64 {
215 let pn: i64 = ap_slen(path)
216 let en: i64 = ap_slen(ext)
217 if en > pn { return 0 }
218 var i: i64 = 0
219 while i < en {
220 if ap_lc((path[pn - en + i] & 0xff) as i64) != ap_lc((ext[i] & 0xff) as i64) { return 0 }
221 i = i + 1
222 }
223 return 1
224}
225
226// the EXTENSION'S CLAIM -- never the answer, only the thing the answer is checked against
227func ap_claim(path: *u8) -> i64 {
228 if ap_ends(path, ".pmx" as *u8) == 1 { return AP_PMX }
229 if ap_ends(path, ".pmd" as *u8) == 1 { return AP_PMD }
230 if ap_ends(path, ".fbx" as *u8) == 1 { return AP_FBXB }
231 if ap_ends(path, ".glb" as *u8) == 1 { return AP_GLB }
232 if ap_ends(path, ".vrm" as *u8) == 1 { return AP_GLB }
233 if ap_ends(path, ".png" as *u8) == 1 { return AP_PNG }
234 if ap_ends(path, ".jpg" as *u8) == 1 { return AP_JPG }
235 if ap_ends(path, ".jpeg" as *u8) == 1 { return AP_JPG }
236 if ap_ends(path, ".hdr" as *u8) == 1 { return AP_HDR }
237 if ap_ends(path, ".ogg" as *u8) == 1 { return AP_OGG }
238 if ap_ends(path, ".mp3" as *u8) == 1 { return AP_MP3 }
239 if ap_ends(path, ".mp4" as *u8) == 1 { return AP_MP4 }
240 if ap_ends(path, ".wav" as *u8) == 1 { return AP_WAV }
241 if ap_ends(path, ".zip" as *u8) == 1 { return AP_ZIP }
242 if ap_ends(path, ".vmd" as *u8) == 1 { return AP_VMD }
243 if ap_ends(path, ".bvh" as *u8) == 1 { return AP_BVH }
244 if ap_ends(path, ".obj" as *u8) == 1 { return AP_OBJ }
245 if ap_ends(path, ".mtl" as *u8) == 1 { return AP_MTL }
246 if ap_ends(path, ".dds" as *u8) == 1 { return AP_DDS }
247 if ap_ends(path, ".nif" as *u8) == 1 { return AP_NIF }
248 if ap_ends(path, ".7z" as *u8) == 1 { return AP_7Z }
249 if ap_ends(path, ".rar" as *u8) == 1 { return AP_RAR }
250 return AP_UNKNOWN
251}