nx_media_sniff.nx source
↩ module page · 1200 lines · 52799 B
1// nx_media_sniff.nx -- determine media type from magic bytes / MIME / extension.
2//
3// Four-tier detection (highest trust first):
4// 1. Magic-byte signature (binary formats; reads actual content)
5// 2. MIME-string parsing (when the source provided Content-Type)
6// 3. URL/path extension (text formats + container disambiguation)
7// 4. mostly-printable (LAST-RESORT fallback -> TEXT)
8//
9// Returns NX_MEDIA_TYPE_* (see nx_media_pool.nx). Returns
10// NX_MEDIA_TYPE_UNKNOWN when all four tiers fail.
11//
12// ===== 2026-07-30 REWRITE (rule 3: rewrite, do not patch-cascade) =====
13// ROOT DEFECT MEASURED: the old sniffer knew only 6 magics (PNG/JPEG/WebP/GIF/
14// MP4/WebM + MP3/WAV/OGG). It had NO MPEG-TS detector and no ".ts" extension
15// row, so the ENTIRE 53,567-file cam recording corpus migrated into the vault
16// as type=unknown (proven by reading live NXR1 records in
17// knowledge/store/mvault-seg-1785089367.docs: every _cam.ts record showed
18// type=unknown while _cam.jpg posters showed image). MV_TYPE_MODEL3D and
19// MV_TYPE_BOOK were likewise unreachable from bytes -- there was no sniff code
20// to return. This rewrite closes the door properly: STL/PLY/GLB/FBX 3D,
21// PDF/EPUB/MOBI/DJVU books, WARC web captures, and the full A/V container set.
22//
23// TRUST-ORDER FIX: "mostly printable" is a FALLBACK HEURISTIC, not a magic.
24// The old code returned it from inside the magic tier, which meant an ASCII
25// STL or a .obj mesh was sealed as TEXT before the extension tier ever ran.
26// nx_sniff_bytes_strict() omits that fallback so the composite can order the
27// tiers honestly; nx_sniff_bytes() keeps its original contract for existing
28// callers.
29//
30// CONTAINER REFINEMENT: ZIP and generic-TEXT are CONTAINER verdicts, not final
31// ones -- .epub/.cbz/.3mf/.wacz are all "PK\x03\x04", and .obj/.gcode/.dae are
32// all plain text. For those two verdicts ONLY, the extension tier refines the
33// answer. Every other magic still wins outright.
34//
35// Per the boundary cardinal: sniffer is a TRUST BOUNDARY -- treats
36// every byte as adversarial. Always reads through bounded loops
37// per the JPL Rule 2 cardinal. No const arrays (the CONST[i] trap
38// compiles clean then reads garbage); every signature is a literal.
39//
40// See: docs/NISHI_MEDIA_INGESTION_ROADMAP.md
41//
42// nx_safety_envelope:
43// intended_use: "Classify ingested bytes into NX_MEDIA_TYPE_*
44// based on magic bytes / MIME / extension."
45// sil_target: SIL2
46// asil_target: QM
47// dal_target: DAL C
48// iec_62304_class: NONE
49// evidence: [no_floating_point,
50// all_byte_reads_bounds_checked,
51// no_const_array_indexing,
52// sealed_media_type_output,
53// mpegts_verified_by_multi_packet_stride]
54// hazard_register: [bug-tape-magic-byte-false-positive,
55// bug-tape-mime-charset-confusion,
56// bug-tape-extension-spoofing,
57// bug-tape-single-sync-byte-false-positive]
58// residual_risk: "Extension tier is spoofable; real magic always wins.
59// ZIP-container and plain-TEXT verdicts are refined BY
60// extension because the bytes alone cannot distinguish
61// epub/cbz/3mf or obj/gcode/dae. Binary-STL detection
62// needs the true file size; when the caller passes only
63// a header window, pass filesize to nx_sniff_full or
64// STL falls through to the extension tier."
65// verdict: NOT_YET_EVALUATED
66
67import "nx_syscalls.nx"
68import "nx_media_pool.nx"
69
70// ===== byte loader (bounds-checked) ===============================
71
72func nx_sniff_load_u8(p: *u8, i: i64, n: i64) -> i64 {
73 if i < 0 { return -1 }
74 if i >= n { return -1 }
75 let q: *u8 = ((p as i64) + i) as *u8
76 return *q
77}
78
79// Compare `llen` bytes of `lit` against the buffer at `off`. 1 = match.
80// Bounded by `n`; a short buffer is a miss, never a read past the end.
81func nx_sniff_at(b: *u8, n: i64, off: i64, lit: *u8, llen: i64) -> i64 {
82 if off < 0 { return 0 }
83 if off + llen > n { return 0 }
84 var i: i64 = 0
85 while i < llen {
86 if nx_sniff_load_u8(b, off + i, n) != lit[i] { return 0 }
87 i = i + 1
88 }
89 return 1
90}
91
92// Bounded substring search over the first `limit` bytes. Returns the index
93// of the first match or -1. Used only for confirming text-format tokens
94// (ASCII STL "facet"), never for unbounded scanning.
95func nx_sniff_find(b: *u8, n: i64, limit: i64, lit: *u8, llen: i64) -> i64 {
96 var cap: i64 = limit
97 if cap > n { cap = n }
98 if llen <= 0 { return -1 }
99 var i: i64 = 0
100 while i + llen <= cap {
101 if nx_sniff_at(b, n, i, lit, llen) == 1 { return i }
102 i = i + 1
103 }
104 return -1
105}
106
107// little-endian u32 at `off`; -1 when out of bounds.
108func nx_sniff_u32le(b: *u8, n: i64, off: i64) -> i64 {
109 if off < 0 { return -1 }
110 if off + 4 > n { return -1 }
111 let a0: i64 = nx_sniff_load_u8(b, off, n)
112 let a1: i64 = nx_sniff_load_u8(b, off + 1, n)
113 let a2: i64 = nx_sniff_load_u8(b, off + 2, n)
114 let a3: i64 = nx_sniff_load_u8(b, off + 3, n)
115 return a0 | (a1 << 8) | (a2 << 16) | (a3 << 24)
116}
117
118// ===== magic-byte tier: IMAGE =====================================
119
120// PNG: 89 50 4E 47 0D 0A 1A 0A
121func nx_sniff_is_png(b: *u8, n: i64) -> i64 {
122 if n < 8 { return 0 }
123 if nx_sniff_load_u8(b, 0, n) != 0x89 { return 0 }
124 if nx_sniff_load_u8(b, 1, n) != 0x50 { return 0 }
125 if nx_sniff_load_u8(b, 2, n) != 0x4E { return 0 }
126 if nx_sniff_load_u8(b, 3, n) != 0x47 { return 0 }
127 if nx_sniff_load_u8(b, 4, n) != 0x0D { return 0 }
128 if nx_sniff_load_u8(b, 5, n) != 0x0A { return 0 }
129 if nx_sniff_load_u8(b, 6, n) != 0x1A { return 0 }
130 if nx_sniff_load_u8(b, 7, n) != 0x0A { return 0 }
131 return 1
132}
133
134// JPEG: FF D8 FF
135func nx_sniff_is_jpeg(b: *u8, n: i64) -> i64 {
136 if n < 3 { return 0 }
137 if nx_sniff_load_u8(b, 0, n) != 0xFF { return 0 }
138 if nx_sniff_load_u8(b, 1, n) != 0xD8 { return 0 }
139 if nx_sniff_load_u8(b, 2, n) != 0xFF { return 0 }
140 return 1
141}
142
143// GIF: "GIF87a" or "GIF89a"
144func nx_sniff_is_gif(b: *u8, n: i64) -> i64 {
145 if n < 6 { return 0 }
146 if nx_sniff_load_u8(b, 0, n) != 0x47 { return 0 } // G
147 if nx_sniff_load_u8(b, 1, n) != 0x49 { return 0 } // I
148 if nx_sniff_load_u8(b, 2, n) != 0x46 { return 0 } // F
149 if nx_sniff_load_u8(b, 3, n) != 0x38 { return 0 } // 8
150 let v: i64 = nx_sniff_load_u8(b, 4, n)
151 if v != 0x37 {
152 if v != 0x39 { return 0 } // 7 or 9
153 }
154 if nx_sniff_load_u8(b, 5, n) != 0x61 { return 0 } // a
155 return 1
156}
157
158// WebP: "RIFF" .... "WEBP"
159func nx_sniff_is_webp(b: *u8, n: i64) -> i64 {
160 if n < 12 { return 0 }
161 if nx_sniff_load_u8(b, 0, n) != 0x52 { return 0 } // R
162 if nx_sniff_load_u8(b, 1, n) != 0x49 { return 0 } // I
163 if nx_sniff_load_u8(b, 2, n) != 0x46 { return 0 } // F
164 if nx_sniff_load_u8(b, 3, n) != 0x46 { return 0 } // F
165 if nx_sniff_load_u8(b, 8, n) != 0x57 { return 0 } // W
166 if nx_sniff_load_u8(b, 9, n) != 0x45 { return 0 } // E
167 if nx_sniff_load_u8(b, 10, n) != 0x42 { return 0 } // B
168 if nx_sniff_load_u8(b, 11, n) != 0x50 { return 0 } // P
169 return 1
170}
171
172// BMP: "BM"
173func nx_sniff_is_bmp(b: *u8, n: i64) -> i64 {
174 let s: *u8 = "BM" as *u8
175 return nx_sniff_at(b, n, 0, s, 2)
176}
177
178// TIFF: "II*\0" (little-endian) or "MM\0*" (big-endian). Also covers the
179// TIFF-derived raw formats (CR2/NEF/ARW/DNG) -- all images.
180func nx_sniff_is_tiff(b: *u8, n: i64) -> i64 {
181 if n < 4 { return 0 }
182 if nx_sniff_load_u8(b, 0, n) == 0x49 {
183 if nx_sniff_load_u8(b, 1, n) == 0x49 {
184 if nx_sniff_load_u8(b, 2, n) == 0x2A {
185 if nx_sniff_load_u8(b, 3, n) == 0x00 { return 1 }
186 }
187 }
188 }
189 if nx_sniff_load_u8(b, 0, n) == 0x4D {
190 if nx_sniff_load_u8(b, 1, n) == 0x4D {
191 if nx_sniff_load_u8(b, 2, n) == 0x00 {
192 if nx_sniff_load_u8(b, 3, n) == 0x2A { return 1 }
193 }
194 }
195 }
196 return 0
197}
198
199// ICO / CUR: 00 00 01 00 / 00 00 02 00
200func nx_sniff_is_ico(b: *u8, n: i64) -> i64 {
201 if n < 4 { return 0 }
202 if nx_sniff_load_u8(b, 0, n) != 0x00 { return 0 }
203 if nx_sniff_load_u8(b, 1, n) != 0x00 { return 0 }
204 let v: i64 = nx_sniff_load_u8(b, 2, n)
205 if v != 0x01 {
206 if v != 0x02 { return 0 }
207 }
208 if nx_sniff_load_u8(b, 3, n) != 0x00 { return 0 }
209 return 1
210}
211
212// Photoshop: "8BPS"
213func nx_sniff_is_psd(b: *u8, n: i64) -> i64 {
214 let s: *u8 = "8BPS" as *u8
215 return nx_sniff_at(b, n, 0, s, 4)
216}
217
218// QOI: "qoif"
219func nx_sniff_is_qoi(b: *u8, n: i64) -> i64 {
220 let s: *u8 = "qoif" as *u8
221 return nx_sniff_at(b, n, 0, s, 4)
222}
223
224// DirectDraw Surface: "DDS "
225func nx_sniff_is_dds(b: *u8, n: i64) -> i64 {
226 let s: *u8 = "DDS " as *u8
227 return nx_sniff_at(b, n, 0, s, 4)
228}
229
230// OpenEXR: 76 2F 31 01
231func nx_sniff_is_exr(b: *u8, n: i64) -> i64 {
232 if n < 4 { return 0 }
233 if nx_sniff_load_u8(b, 0, n) != 0x76 { return 0 }
234 if nx_sniff_load_u8(b, 1, n) != 0x2F { return 0 }
235 if nx_sniff_load_u8(b, 2, n) != 0x31 { return 0 }
236 if nx_sniff_load_u8(b, 3, n) != 0x01 { return 0 }
237 return 1
238}
239
240// JPEG XL raw codestream: FF 0A (the ISOBMFF-wrapped form is caught by ftyp)
241func nx_sniff_is_jxl(b: *u8, n: i64) -> i64 {
242 if n < 2 { return 0 }
243 if nx_sniff_load_u8(b, 0, n) != 0xFF { return 0 }
244 if nx_sniff_load_u8(b, 1, n) != 0x0A { return 0 }
245 return 1
246}
247
248// Netpbm: "P1".."P7" followed by whitespace
249func nx_sniff_is_netpbm(b: *u8, n: i64) -> i64 {
250 if n < 3 { return 0 }
251 if nx_sniff_load_u8(b, 0, n) != 0x50 { return 0 } // P
252 let d: i64 = nx_sniff_load_u8(b, 1, n)
253 if d < 0x31 { return 0 }
254 if d > 0x37 { return 0 }
255 let w: i64 = nx_sniff_load_u8(b, 2, n)
256 if w == 0x0A { return 1 }
257 if w == 0x0D { return 1 }
258 if w == 0x20 { return 1 }
259 if w == 0x09 { return 1 }
260 return 0
261}
262
263// ===== magic-byte tier: VIDEO =====================================
264
265// MP4 / ISO BMFF: bytes 4..8 == "ftyp". Kept for API compatibility; the
266// strict path uses nx_sniff_isobmff_kind to tell video from audio/image.
267func nx_sniff_is_mp4(b: *u8, n: i64) -> i64 {
268 if n < 12 { return 0 }
269 if nx_sniff_load_u8(b, 4, n) != 0x66 { return 0 } // f
270 if nx_sniff_load_u8(b, 5, n) != 0x74 { return 0 } // t
271 if nx_sniff_load_u8(b, 6, n) != 0x79 { return 0 } // y
272 if nx_sniff_load_u8(b, 7, n) != 0x70 { return 0 } // p
273 return 1
274}
275
276// ISOBMFF major brand at offset 8 decides the REAL type. An .m4b audiobook
277// and an .avif still image are both "ftyp" files; typing them all VIDEO (the
278// old behaviour) mis-shelved every one of them.
279func nx_sniff_isobmff_kind(b: *u8, n: i64) -> i64 {
280 if nx_sniff_is_mp4(b, n) == 0 { return NX_MEDIA_TYPE_UNKNOWN }
281 let br_m4a: *u8 = "M4A " as *u8
282 let br_m4b: *u8 = "M4B " as *u8
283 let br_m4p: *u8 = "M4P " as *u8
284 let br_avif: *u8 = "avif" as *u8
285 let br_avis: *u8 = "avis" as *u8
286 let br_heic: *u8 = "heic" as *u8
287 let br_heix: *u8 = "heix" as *u8
288 let br_heim: *u8 = "heim" as *u8
289 let br_heis: *u8 = "heis" as *u8
290 let br_hevc: *u8 = "hevc" as *u8
291 let br_mif1: *u8 = "mif1" as *u8
292 let br_msf1: *u8 = "msf1" as *u8
293 let br_jxl: *u8 = "jxl " as *u8
294 let br_crx: *u8 = "crx " as *u8
295 if nx_sniff_at(b, n, 8, br_m4b, 4) == 1 { return NX_MEDIA_TYPE_AUDIOBOOK }
296 if nx_sniff_at(b, n, 8, br_m4a, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
297 if nx_sniff_at(b, n, 8, br_m4p, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
298 if nx_sniff_at(b, n, 8, br_avif, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
299 if nx_sniff_at(b, n, 8, br_avis, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
300 if nx_sniff_at(b, n, 8, br_heic, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
301 if nx_sniff_at(b, n, 8, br_heix, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
302 if nx_sniff_at(b, n, 8, br_heim, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
303 if nx_sniff_at(b, n, 8, br_heis, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
304 if nx_sniff_at(b, n, 8, br_hevc, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
305 if nx_sniff_at(b, n, 8, br_mif1, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
306 if nx_sniff_at(b, n, 8, br_msf1, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
307 if nx_sniff_at(b, n, 8, br_jxl, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
308 if nx_sniff_at(b, n, 8, br_crx, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
309 return NX_MEDIA_TYPE_VIDEO
310}
311
312// WebM / Matroska EBML header: 1A 45 DF A3
313func nx_sniff_is_webm(b: *u8, n: i64) -> i64 {
314 if n < 4 { return 0 }
315 if nx_sniff_load_u8(b, 0, n) != 0x1A { return 0 }
316 if nx_sniff_load_u8(b, 1, n) != 0x45 { return 0 }
317 if nx_sniff_load_u8(b, 2, n) != 0xDF { return 0 }
318 if nx_sniff_load_u8(b, 3, n) != 0xA3 { return 0 }
319 return 1
320}
321
322// MPEG-TS: 0x47 sync byte on a 188-byte stride.
323//
324// ★ THIS IS THE 53,567-RECORDING FIX. A SINGLE 0x47 is just ASCII 'G' and
325// would false-positive on ordinary text, so the packet stride is VERIFIED
326// across three consecutive packets before claiming MPEG-TS. That makes the
327// detector both correct for real captures and safe against text.
328func nx_sniff_is_mpegts(b: *u8, n: i64) -> i64 {
329 if n < 377 { return 0 }
330 if nx_sniff_load_u8(b, 0, n) != 0x47 { return 0 }
331 if nx_sniff_load_u8(b, 188, n) != 0x47 { return 0 }
332 if nx_sniff_load_u8(b, 376, n) != 0x47 { return 0 }
333 return 1
334}
335
336// M2TS / MTS (Blu-ray, AVCHD camcorders): 4-byte TP_extra_header then the
337// 0x47 sync, 192-byte stride.
338func nx_sniff_is_m2ts(b: *u8, n: i64) -> i64 {
339 if n < 389 { return 0 }
340 if nx_sniff_load_u8(b, 4, n) != 0x47 { return 0 }
341 if nx_sniff_load_u8(b, 196, n) != 0x47 { return 0 }
342 if nx_sniff_load_u8(b, 388, n) != 0x47 { return 0 }
343 return 1
344}
345
346// MPEG program stream / elementary video: 00 00 01 BA / 00 00 01 B3
347func nx_sniff_is_mpegps(b: *u8, n: i64) -> i64 {
348 if n < 4 { return 0 }
349 if nx_sniff_load_u8(b, 0, n) != 0x00 { return 0 }
350 if nx_sniff_load_u8(b, 1, n) != 0x00 { return 0 }
351 if nx_sniff_load_u8(b, 2, n) != 0x01 { return 0 }
352 let v: i64 = nx_sniff_load_u8(b, 3, n)
353 if v == 0xBA { return 1 }
354 if v == 0xB3 { return 1 }
355 return 0
356}
357
358// AVI: "RIFF" .... "AVI "
359func nx_sniff_is_avi(b: *u8, n: i64) -> i64 {
360 let r: *u8 = "RIFF" as *u8
361 let a: *u8 = "AVI " as *u8
362 if nx_sniff_at(b, n, 0, r, 4) == 0 { return 0 }
363 return nx_sniff_at(b, n, 8, a, 4)
364}
365
366// FLV: "FLV" 01
367func nx_sniff_is_flv(b: *u8, n: i64) -> i64 {
368 let s: *u8 = "FLV" as *u8
369 if nx_sniff_at(b, n, 0, s, 3) == 0 { return 0 }
370 if nx_sniff_load_u8(b, 3, n) != 0x01 { return 0 }
371 return 1
372}
373
374// ASF / WMV / WMA container GUID: 30 26 B2 75 8E 66 CF 11
375func nx_sniff_is_asf(b: *u8, n: i64) -> i64 {
376 if n < 8 { return 0 }
377 if nx_sniff_load_u8(b, 0, n) != 0x30 { return 0 }
378 if nx_sniff_load_u8(b, 1, n) != 0x26 { return 0 }
379 if nx_sniff_load_u8(b, 2, n) != 0xB2 { return 0 }
380 if nx_sniff_load_u8(b, 3, n) != 0x75 { return 0 }
381 if nx_sniff_load_u8(b, 4, n) != 0x8E { return 0 }
382 if nx_sniff_load_u8(b, 5, n) != 0x66 { return 0 }
383 if nx_sniff_load_u8(b, 6, n) != 0xCF { return 0 }
384 if nx_sniff_load_u8(b, 7, n) != 0x11 { return 0 }
385 return 1
386}
387
388// ===== magic-byte tier: AUDIO =====================================
389
390// MP3: ID3v2 "ID3" header OR MPEG frame sync (FF Fx)
391func nx_sniff_is_mp3(b: *u8, n: i64) -> i64 {
392 if n < 3 { return 0 }
393 let v0: i64 = nx_sniff_load_u8(b, 0, n)
394 let v1: i64 = nx_sniff_load_u8(b, 1, n)
395 let v2: i64 = nx_sniff_load_u8(b, 2, n)
396 if v0 == 0x49 {
397 if v1 == 0x44 {
398 if v2 == 0x33 { return 1 }
399 }
400 }
401 if v0 == 0xFF {
402 if (v1 & 0xE0) == 0xE0 { return 1 }
403 }
404 return 0
405}
406
407// WAV: "RIFF" .... "WAVE"
408func nx_sniff_is_wav(b: *u8, n: i64) -> i64 {
409 if n < 12 { return 0 }
410 if nx_sniff_load_u8(b, 0, n) != 0x52 { return 0 } // R
411 if nx_sniff_load_u8(b, 1, n) != 0x49 { return 0 } // I
412 if nx_sniff_load_u8(b, 2, n) != 0x46 { return 0 } // F
413 if nx_sniff_load_u8(b, 3, n) != 0x46 { return 0 } // F
414 if nx_sniff_load_u8(b, 8, n) != 0x57 { return 0 } // W
415 if nx_sniff_load_u8(b, 9, n) != 0x41 { return 0 } // A
416 if nx_sniff_load_u8(b, 10, n) != 0x56 { return 0 } // V
417 if nx_sniff_load_u8(b, 11, n) != 0x45 { return 0 } // E
418 return 1
419}
420
421// OGG: "OggS"
422func nx_sniff_is_ogg(b: *u8, n: i64) -> i64 {
423 if n < 4 { return 0 }
424 if nx_sniff_load_u8(b, 0, n) != 0x4F { return 0 } // O
425 if nx_sniff_load_u8(b, 1, n) != 0x67 { return 0 } // g
426 if nx_sniff_load_u8(b, 2, n) != 0x67 { return 0 } // g
427 if nx_sniff_load_u8(b, 3, n) != 0x53 { return 0 } // S
428 return 1
429}
430
431// FLAC: "fLaC"
432func nx_sniff_is_flac(b: *u8, n: i64) -> i64 {
433 let s: *u8 = "fLaC" as *u8
434 return nx_sniff_at(b, n, 0, s, 4)
435}
436
437// AIFF / AIFC: "FORM" .... "AIFF" | "AIFC"
438func nx_sniff_is_aiff(b: *u8, n: i64) -> i64 {
439 let f: *u8 = "FORM" as *u8
440 let a: *u8 = "AIFF" as *u8
441 let c: *u8 = "AIFC" as *u8
442 if nx_sniff_at(b, n, 0, f, 4) == 0 { return 0 }
443 if nx_sniff_at(b, n, 8, a, 4) == 1 { return 1 }
444 if nx_sniff_at(b, n, 8, c, 4) == 1 { return 1 }
445 return 0
446}
447
448// Standard MIDI: "MThd"
449func nx_sniff_is_midi(b: *u8, n: i64) -> i64 {
450 let s: *u8 = "MThd" as *u8
451 return nx_sniff_at(b, n, 0, s, 4)
452}
453
454// Monkey's Audio: "MAC "
455func nx_sniff_is_ape(b: *u8, n: i64) -> i64 {
456 let s: *u8 = "MAC " as *u8
457 return nx_sniff_at(b, n, 0, s, 4)
458}
459
460// ===== magic-byte tier: 3D MODELS =================================
461//
462// The operator's corpus runs from STLs to books; 3D was previously
463// UNREACHABLE (mv_is_glb existed but no caller in the live path ever
464// invoked it, and the sniffer had no MODEL3D code to return at all).
465
466// glTF binary container: "glTF"
467func nx_sniff_is_glb(b: *u8, n: i64) -> i64 {
468 let s: *u8 = "glTF" as *u8
469 return nx_sniff_at(b, n, 0, s, 4)
470}
471
472// Stanford PLY: "ply" then CR or LF
473func nx_sniff_is_ply(b: *u8, n: i64) -> i64 {
474 let s: *u8 = "ply" as *u8
475 if nx_sniff_at(b, n, 0, s, 3) == 0 { return 0 }
476 let w: i64 = nx_sniff_load_u8(b, 3, n)
477 if w == 0x0A { return 1 }
478 if w == 0x0D { return 1 }
479 return 0
480}
481
482// ASCII STL: "solid" at 0, CONFIRMED by a "facet" token in the header window.
483// The confirmation matters: plenty of plain-text files begin with the word
484// "solid", and mis-typing one as a mesh would be a silent shelving error.
485func nx_sniff_is_stl_ascii(b: *u8, n: i64) -> i64 {
486 let s: *u8 = "solid" as *u8
487 let f: *u8 = "facet" as *u8
488 if nx_sniff_at(b, n, 0, s, 5) == 0 { return 0 }
489 if nx_sniff_find(b, n, 2048, f, 5) < 0 { return 0 }
490 return 1
491}
492
493// Binary STL: 80-byte header, u32le triangle count at 80, then 50 bytes per
494// triangle. Binary STL has NO magic -- the structural identity IS the size
495// equation, so this needs the TRUE file size, not the header window length.
496// Callers with only a window pass filesize<=0 and fall through to extension.
497func nx_sniff_is_stl_binary(b: *u8, n: i64, filesize: i64) -> i64 {
498 if filesize < 84 { return 0 }
499 if n < 84 { return 0 }
500 let s: *u8 = "solid" as *u8
501 if nx_sniff_at(b, n, 0, s, 5) == 1 { return 0 } // that is the ASCII form
502 let tris: i64 = nx_sniff_u32le(b, n, 80)
503 if tris < 0 { return 0 }
504 if tris > 100000000 { return 0 } // bound the multiply
505 if (84 + (tris * 50)) != filesize { return 0 }
506 return 1
507}
508
509// Autodesk FBX binary: "Kaydara FBX Binary"
510func nx_sniff_is_fbx(b: *u8, n: i64) -> i64 {
511 let s: *u8 = "Kaydara FBX Binary" as *u8
512 return nx_sniff_at(b, n, 0, s, 18)
513}
514
515// Blender: "BLENDER"
516func nx_sniff_is_blend(b: *u8, n: i64) -> i64 {
517 let s: *u8 = "BLENDER" as *u8
518 return nx_sniff_at(b, n, 0, s, 7)
519}
520
521// MagicaVoxel: "VOX "
522func nx_sniff_is_vox(b: *u8, n: i64) -> i64 {
523 let s: *u8 = "VOX " as *u8
524 return nx_sniff_at(b, n, 0, s, 4)
525}
526
527// STEP / IGES CAD exchange: "ISO-10303"
528func nx_sniff_is_step(b: *u8, n: i64) -> i64 {
529 let s: *u8 = "ISO-10303" as *u8
530 return nx_sniff_at(b, n, 0, s, 9)
531}
532
533// ===== magic-byte tier: BOOKS / DOCUMENTS =========================
534
535// PDF: "%PDF"
536func nx_sniff_is_pdf(b: *u8, n: i64) -> i64 {
537 let s: *u8 = "%PDF" as *u8
538 return nx_sniff_at(b, n, 0, s, 4)
539}
540
541// EPUB: a ZIP whose first entry is the uncompressed "mimetype" member
542// carrying "application/epub+zip" at offset 38.
543func nx_sniff_is_epub(b: *u8, n: i64) -> i64 {
544 let m: *u8 = "mimetype" as *u8
545 let a: *u8 = "application/epub+zip" as *u8
546 if nx_sniff_at(b, n, 30, m, 8) == 0 { return 0 }
547 return nx_sniff_at(b, n, 38, a, 20)
548}
549
550// MOBI / AZW: "BOOKMOBI" at offset 60
551func nx_sniff_is_mobi(b: *u8, n: i64) -> i64 {
552 let s: *u8 = "BOOKMOBI" as *u8
553 return nx_sniff_at(b, n, 60, s, 8)
554}
555
556// DjVu: "AT&TFORM"
557func nx_sniff_is_djvu(b: *u8, n: i64) -> i64 {
558 let s: *u8 = "AT&TFORM" as *u8
559 return nx_sniff_at(b, n, 0, s, 8)
560}
561
562// Microsoft CHM help book: "ITSF"
563func nx_sniff_is_chm(b: *u8, n: i64) -> i64 {
564 let s: *u8 = "ITSF" as *u8
565 return nx_sniff_at(b, n, 0, s, 4)
566}
567
568// RTF: "{\rtf"
569func nx_sniff_is_rtf(b: *u8, n: i64) -> i64 {
570 if n < 5 { return 0 }
571 if nx_sniff_load_u8(b, 0, n) != 0x7B { return 0 } // {
572 if nx_sniff_load_u8(b, 1, n) != 0x5C { return 0 } // backslash
573 if nx_sniff_load_u8(b, 2, n) != 0x72 { return 0 } // r
574 if nx_sniff_load_u8(b, 3, n) != 0x74 { return 0 } // t
575 if nx_sniff_load_u8(b, 4, n) != 0x66 { return 0 } // f
576 return 1
577}
578
579// ===== magic-byte tier: WEB CAPTURES / ARCHIVES ===================
580
581// WARC: "WARC/1." -- the archived-website format.
582func nx_sniff_is_warc(b: *u8, n: i64) -> i64 {
583 let s: *u8 = "WARC/1." as *u8
584 return nx_sniff_at(b, n, 0, s, 7)
585}
586
587// MHTML single-file web capture: "From:" or "MIME-Version:"
588func nx_sniff_is_mhtml(b: *u8, n: i64) -> i64 {
589 let f: *u8 = "From:" as *u8
590 let m: *u8 = "MIME-Version:" as *u8
591 if nx_sniff_at(b, n, 0, f, 5) == 1 { return 1 }
592 if nx_sniff_at(b, n, 0, m, 13) == 1 { return 1 }
593 return 0
594}
595
596// ZIP local file header: "PK\x03\x04". A CONTAINER verdict -- epub/cbz/3mf/
597// wacz/docx are all this; the extension tier refines it.
598func nx_sniff_is_zip(b: *u8, n: i64) -> i64 {
599 if n < 4 { return 0 }
600 if nx_sniff_load_u8(b, 0, n) != 0x50 { return 0 } // P
601 if nx_sniff_load_u8(b, 1, n) != 0x4B { return 0 } // K
602 if nx_sniff_load_u8(b, 2, n) != 0x03 { return 0 }
603 if nx_sniff_load_u8(b, 3, n) != 0x04 { return 0 }
604 return 1
605}
606
607// RAR: "Rar!"
608func nx_sniff_is_rar(b: *u8, n: i64) -> i64 {
609 let s: *u8 = "Rar!" as *u8
610 return nx_sniff_at(b, n, 0, s, 4)
611}
612
613// 7-Zip: "7z" BC AF 27 1C
614func nx_sniff_is_7z(b: *u8, n: i64) -> i64 {
615 if n < 6 { return 0 }
616 if nx_sniff_load_u8(b, 0, n) != 0x37 { return 0 } // 7
617 if nx_sniff_load_u8(b, 1, n) != 0x7A { return 0 } // z
618 if nx_sniff_load_u8(b, 2, n) != 0xBC { return 0 }
619 if nx_sniff_load_u8(b, 3, n) != 0xAF { return 0 }
620 if nx_sniff_load_u8(b, 4, n) != 0x27 { return 0 }
621 if nx_sniff_load_u8(b, 5, n) != 0x1C { return 0 }
622 return 1
623}
624
625// ===== text-token tier ============================================
626
627// HTML: case-insensitive "<!DOCTYPE" or "<html" or "<HTML"
628// Tier-2 only; tier-1 magic for HTML doesn't exist.
629func nx_sniff_is_html_token(b: *u8, n: i64) -> i64 {
630 if n < 5 { return 0 }
631 let v0: i64 = nx_sniff_load_u8(b, 0, n)
632 if v0 != 0x3C { return 0 } // <
633 let v1: i64 = nx_sniff_load_u8(b, 1, n) | 0x20 // lowercase
634 let v2: i64 = nx_sniff_load_u8(b, 2, n) | 0x20
635 let v3: i64 = nx_sniff_load_u8(b, 3, n) | 0x20
636 let v4: i64 = nx_sniff_load_u8(b, 4, n) | 0x20
637 if v1 == 0x68 {
638 if v2 == 0x74 {
639 if v3 == 0x6D {
640 if v4 == 0x6C { return 1 } // <html
641 }
642 }
643 }
644 if v1 == 0x21 { // <!
645 if v2 == 0x64 { // <!d
646 if v3 == 0x6F { return 1 } // <!do (DOCTYPE)
647 }
648 }
649 return 0
650}
651
652// SVG: "<svg" token (vector image, not a page)
653func nx_sniff_is_svg_token(b: *u8, n: i64) -> i64 {
654 let s: *u8 = "<svg" as *u8
655 if nx_sniff_at(b, n, 0, s, 4) == 1 { return 1 }
656 if nx_sniff_find(b, n, 512, s, 4) >= 0 { return 1 }
657 return 0
658}
659
660func nx_sniff_is_mostly_printable(b: *u8, n: i64) -> i64 {
661 var limit: i64 = n
662 if limit > 256 { limit = 256 }
663 if limit == 0 { return 0 }
664 var printable: i64 = 0
665 var i: i64 = 0
666 while i < limit {
667 let v: i64 = nx_sniff_load_u8(b, i, n)
668 if v == 0x09 { printable = printable + 1 } // tab
669 if v == 0x0A { printable = printable + 1 } // lf
670 if v == 0x0D { printable = printable + 1 } // cr
671 if v >= 0x20 {
672 if v < 0x7F { printable = printable + 1 }
673 }
674 if v >= 0x80 { printable = printable + 1 } // utf-8 continuation
675 i = i + 1
676 }
677 if (printable * 10) >= (limit * 9) { return 1 } // >= 90%
678 return 0
679}
680
681// ===== magic tier, STRICT ========================================
682//
683// Every magic detector, in descending specificity, WITHOUT the
684// mostly-printable fallback. Returning UNKNOWN here (rather than TEXT)
685// is what lets the composite order the tiers honestly: a .obj mesh or a
686// .gcode file is "printable", but it is not prose, and the extension tier
687// must get a chance to say so.
688//
689// filesize is the TRUE size of the item (not the window length); pass <=0
690// when unknown and the size-derived binary-STL check is skipped.
691func nx_sniff_bytes_strict(b: *u8, n: i64, filesize: i64) -> i64 {
692 if n <= 0 { return NX_MEDIA_TYPE_UNKNOWN }
693
694 // --- containers whose payload identity is unambiguous -------------
695 if nx_sniff_is_epub(b, n) == 1 { return NX_MEDIA_TYPE_BOOK }
696 if nx_sniff_is_pdf(b, n) == 1 { return NX_MEDIA_TYPE_BOOK }
697 if nx_sniff_is_mobi(b, n) == 1 { return NX_MEDIA_TYPE_BOOK }
698 if nx_sniff_is_djvu(b, n) == 1 { return NX_MEDIA_TYPE_BOOK }
699 if nx_sniff_is_chm(b, n) == 1 { return NX_MEDIA_TYPE_BOOK }
700
701 // --- web captures --------------------------------------------------
702 if nx_sniff_is_warc(b, n) == 1 { return NX_MEDIA_TYPE_WARC }
703
704 // --- image ---------------------------------------------------------
705 if nx_sniff_is_png(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
706 if nx_sniff_is_jpeg(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
707 if nx_sniff_is_webp(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
708 if nx_sniff_is_gif(b, n) == 1 { return NX_MEDIA_TYPE_GIF }
709 if nx_sniff_is_tiff(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
710 if nx_sniff_is_psd(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
711 if nx_sniff_is_qoi(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
712 if nx_sniff_is_dds(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
713 if nx_sniff_is_exr(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
714 if nx_sniff_is_ico(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
715 if nx_sniff_is_netpbm(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
716 if nx_sniff_is_jxl(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
717
718 // --- ISOBMFF: brand decides video vs audio vs audiobook vs image ---
719 if nx_sniff_is_mp4(b, n) == 1 { return nx_sniff_isobmff_kind(b, n) }
720
721 // --- video ---------------------------------------------------------
722 if nx_sniff_is_webm(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
723 if nx_sniff_is_m2ts(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
724 if nx_sniff_is_mpegts(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
725 if nx_sniff_is_mpegps(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
726 if nx_sniff_is_avi(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
727 if nx_sniff_is_flv(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
728 if nx_sniff_is_asf(b, n) == 1 { return NX_MEDIA_TYPE_VIDEO }
729
730 // --- audio ---------------------------------------------------------
731 if nx_sniff_is_flac(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
732 if nx_sniff_is_aiff(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
733 if nx_sniff_is_midi(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
734 if nx_sniff_is_ape(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
735 if nx_sniff_is_ogg(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
736 if nx_sniff_is_wav(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
737 if nx_sniff_is_mp3(b, n) == 1 { return NX_MEDIA_TYPE_AUDIO }
738
739 // --- 3D models -----------------------------------------------------
740 if nx_sniff_is_glb(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
741 if nx_sniff_is_fbx(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
742 if nx_sniff_is_blend(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
743 if nx_sniff_is_vox(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
744 if nx_sniff_is_step(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
745 if nx_sniff_is_ply(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
746 if nx_sniff_is_stl_ascii(b, n) == 1 { return NX_MEDIA_TYPE_MODEL3D }
747 if nx_sniff_is_stl_binary(b, n, filesize) == 1 { return NX_MEDIA_TYPE_MODEL3D }
748
749 // --- documents / markup --------------------------------------------
750 if nx_sniff_is_rtf(b, n) == 1 { return NX_MEDIA_TYPE_TEXT }
751 if nx_sniff_is_svg_token(b, n) == 1 { return NX_MEDIA_TYPE_IMAGE }
752 if nx_sniff_is_html_token(b, n) == 1 { return NX_MEDIA_TYPE_HTML }
753 if nx_sniff_is_mhtml(b, n) == 1 { return NX_MEDIA_TYPE_WARC }
754
755 // --- generic containers: extension refines these -------------------
756 if nx_sniff_is_zip(b, n) == 1 { return NX_MEDIA_TYPE_ARCHIVE }
757 if nx_sniff_is_rar(b, n) == 1 { return NX_MEDIA_TYPE_ARCHIVE }
758 if nx_sniff_is_7z(b, n) == 1 { return NX_MEDIA_TYPE_ARCHIVE }
759
760 return NX_MEDIA_TYPE_UNKNOWN
761}
762
763// ===== top-level sniffer (ORIGINAL CONTRACT, unchanged) ===========
764//
765// Kept byte-for-byte in behaviour for existing callers: magic bytes first,
766// then text token detection, then the mostly-printable TEXT fallback.
767// New code should prefer nx_sniff / nx_sniff_full.
768func nx_sniff_bytes(b: *u8, n: i64) -> i64 {
769 if n <= 0 { return NX_MEDIA_TYPE_UNKNOWN }
770 let v: i64 = nx_sniff_bytes_strict(b, n, 0)
771 if v != NX_MEDIA_TYPE_UNKNOWN { return v }
772 if nx_sniff_is_mostly_printable(b, n) == 1 { return NX_MEDIA_TYPE_TEXT }
773 return NX_MEDIA_TYPE_UNKNOWN
774}
775
776// ===== MIME-string tier ===========================================
777//
778// Compare the leading MIME-type token (before ';') against known
779// strings. Case-insensitive. Returns NX_MEDIA_TYPE_UNKNOWN if
780// no match.
781
782func nx_sniff_mime_eq(mime: *u8, mlen: i64, lit: *u8, llen: i64) -> i64 {
783 if mlen < llen { return 0 }
784 var i: i64 = 0
785 while i < llen {
786 let mv: i64 = nx_sniff_load_u8(mime, i, mlen) | 0x20
787 let lv: i64 = nx_sniff_load_u8(lit, i, llen)
788 if mv != lv { return 0 }
789 i = i + 1
790 }
791 // Match terminator: end-of-string, ';', or whitespace.
792 if mlen == llen { return 1 }
793 let tail: i64 = nx_sniff_load_u8(mime, llen, mlen)
794 if tail == 0x3B { return 1 } // ;
795 if tail == 0x20 { return 1 } // space
796 if tail == 0x09 { return 1 } // tab
797 return 0
798}
799
800func nx_sniff_mime(mime: *u8, mlen: i64) -> i64 {
801 if mlen <= 0 { return NX_MEDIA_TYPE_UNKNOWN }
802 let s_text: *u8 = "text/plain" as *u8
803 let s_md: *u8 = "text/markdown" as *u8
804 let s_html: *u8 = "text/html" as *u8
805 let s_png: *u8 = "image/png" as *u8
806 let s_jpeg: *u8 = "image/jpeg" as *u8
807 let s_webp: *u8 = "image/webp" as *u8
808 let s_gif: *u8 = "image/gif" as *u8
809 let s_avif: *u8 = "image/avif" as *u8
810 let s_heic: *u8 = "image/heic" as *u8
811 let s_svg: *u8 = "image/svg+xml" as *u8
812 let s_tiff: *u8 = "image/tiff" as *u8
813 let s_mp4: *u8 = "video/mp4" as *u8
814 let s_webm: *u8 = "video/webm" as *u8
815 let s_mpeg: *u8 = "video/mpeg" as *u8
816 let s_mp2t: *u8 = "video/mp2t" as *u8
817 let s_qt: *u8 = "video/quicktime" as *u8
818 let s_mp3: *u8 = "audio/mpeg" as *u8
819 let s_wav: *u8 = "audio/wav" as *u8
820 let s_ogg: *u8 = "audio/ogg" as *u8
821 let s_flac: *u8 = "audio/flac" as *u8
822 let s_m4a: *u8 = "audio/mp4" as *u8
823 let s_pdf: *u8 = "application/pdf" as *u8
824 let s_epub: *u8 = "application/epub+zip" as *u8
825 let s_warc: *u8 = "application/warc" as *u8
826 let s_stl: *u8 = "model/stl" as *u8
827 let s_gltf: *u8 = "model/gltf+json" as *u8
828 let s_glb: *u8 = "model/gltf-binary" as *u8
829 let s_obj: *u8 = "model/obj" as *u8
830
831 if nx_sniff_mime_eq(mime, mlen, s_html, 9) == 1 { return NX_MEDIA_TYPE_HTML }
832 if nx_sniff_mime_eq(mime, mlen, s_md, 13) == 1 { return NX_MEDIA_TYPE_TEXT }
833 if nx_sniff_mime_eq(mime, mlen, s_text, 10) == 1 { return NX_MEDIA_TYPE_TEXT }
834 if nx_sniff_mime_eq(mime, mlen, s_png, 9) == 1 { return NX_MEDIA_TYPE_IMAGE }
835 if nx_sniff_mime_eq(mime, mlen, s_jpeg, 10) == 1 { return NX_MEDIA_TYPE_IMAGE }
836 if nx_sniff_mime_eq(mime, mlen, s_webp, 10) == 1 { return NX_MEDIA_TYPE_IMAGE }
837 if nx_sniff_mime_eq(mime, mlen, s_avif, 10) == 1 { return NX_MEDIA_TYPE_IMAGE }
838 if nx_sniff_mime_eq(mime, mlen, s_heic, 10) == 1 { return NX_MEDIA_TYPE_IMAGE }
839 if nx_sniff_mime_eq(mime, mlen, s_svg, 13) == 1 { return NX_MEDIA_TYPE_IMAGE }
840 if nx_sniff_mime_eq(mime, mlen, s_tiff, 10) == 1 { return NX_MEDIA_TYPE_IMAGE }
841 if nx_sniff_mime_eq(mime, mlen, s_gif, 9) == 1 { return NX_MEDIA_TYPE_GIF }
842 if nx_sniff_mime_eq(mime, mlen, s_mp4, 9) == 1 { return NX_MEDIA_TYPE_VIDEO }
843 if nx_sniff_mime_eq(mime, mlen, s_webm, 10) == 1 { return NX_MEDIA_TYPE_VIDEO }
844 if nx_sniff_mime_eq(mime, mlen, s_mpeg, 10) == 1 { return NX_MEDIA_TYPE_VIDEO }
845 if nx_sniff_mime_eq(mime, mlen, s_mp2t, 10) == 1 { return NX_MEDIA_TYPE_VIDEO }
846 if nx_sniff_mime_eq(mime, mlen, s_qt, 15) == 1 { return NX_MEDIA_TYPE_VIDEO }
847 if nx_sniff_mime_eq(mime, mlen, s_mp3, 10) == 1 { return NX_MEDIA_TYPE_AUDIO }
848 if nx_sniff_mime_eq(mime, mlen, s_wav, 9) == 1 { return NX_MEDIA_TYPE_AUDIO }
849 if nx_sniff_mime_eq(mime, mlen, s_ogg, 9) == 1 { return NX_MEDIA_TYPE_AUDIO }
850 if nx_sniff_mime_eq(mime, mlen, s_flac, 10) == 1 { return NX_MEDIA_TYPE_AUDIO }
851 if nx_sniff_mime_eq(mime, mlen, s_m4a, 9) == 1 { return NX_MEDIA_TYPE_AUDIO }
852 if nx_sniff_mime_eq(mime, mlen, s_pdf, 15) == 1 { return NX_MEDIA_TYPE_BOOK }
853 if nx_sniff_mime_eq(mime, mlen, s_epub, 20) == 1 { return NX_MEDIA_TYPE_BOOK }
854 if nx_sniff_mime_eq(mime, mlen, s_warc, 16) == 1 { return NX_MEDIA_TYPE_WARC }
855 if nx_sniff_mime_eq(mime, mlen, s_stl, 9) == 1 { return NX_MEDIA_TYPE_MODEL3D }
856 if nx_sniff_mime_eq(mime, mlen, s_gltf, 15) == 1 { return NX_MEDIA_TYPE_MODEL3D }
857 if nx_sniff_mime_eq(mime, mlen, s_glb, 17) == 1 { return NX_MEDIA_TYPE_MODEL3D }
858 if nx_sniff_mime_eq(mime, mlen, s_obj, 9) == 1 { return NX_MEDIA_TYPE_MODEL3D }
859 return NX_MEDIA_TYPE_UNKNOWN
860}
861
862// ===== extension tier =============================================
863//
864// Take the part of `path` after the last '.', lowercase it, compare.
865// This tier carries the TEXT-BASED formats that have no magic at all
866// (.obj/.gcode/.dae/.gltf meshes, .srt subtitles) and disambiguates the
867// ZIP container family (.epub/.cbz/.3mf/.wacz).
868
869func nx_sniff_ext_eq(p: *u8, plen: i64, off: i64,
870 lit: *u8, llen: i64) -> i64 {
871 if plen - off != llen { return 0 }
872 var i: i64 = 0
873 while i < llen {
874 let pv: i64 = nx_sniff_load_u8(p, off + i, plen) | 0x20
875 let lv: i64 = nx_sniff_load_u8(lit, i, llen)
876 if pv != lv { return 0 }
877 i = i + 1
878 }
879 return 1
880}
881
882func nx_sniff_ext_off(path: *u8, plen: i64) -> i64 {
883 if plen <= 0 { return -1 }
884 var dot: i64 = -1
885 var i: i64 = plen - 1
886 let BUDGET: i64 = plen + 1
887 var iter: i64 = 0
888 while i >= 0 {
889 if iter >= BUDGET { return -1 }
890 let v: i64 = nx_sniff_load_u8(path, i, plen)
891 if v == 0x2E { dot = i; i = -1 }
892 if v == 0x2F { i = -1 } // '/' - no ext
893 if v == 0x5C { i = -1 } // '\\' - no ext
894 i = i - 1
895 iter = iter + 1
896 }
897 if dot < 0 { return -1 }
898 return dot + 1
899}
900
901// --- extension sub-tables (split so each stays readable) ------------
902
903func nx_sniff_ext_image(p: *u8, plen: i64, off: i64) -> i64 {
904 let e_png: *u8 = "png" as *u8
905 let e_jpg: *u8 = "jpg" as *u8
906 let e_jpeg: *u8 = "jpeg" as *u8
907 let e_jpe: *u8 = "jpe" as *u8
908 let e_webp: *u8 = "webp" as *u8
909 let e_bmp: *u8 = "bmp" as *u8
910 let e_tif: *u8 = "tif" as *u8
911 let e_tiff: *u8 = "tiff" as *u8
912 let e_avif: *u8 = "avif" as *u8
913 let e_heic: *u8 = "heic" as *u8
914 let e_heif: *u8 = "heif" as *u8
915 let e_jxl: *u8 = "jxl" as *u8
916 let e_svg: *u8 = "svg" as *u8
917 let e_ico: *u8 = "ico" as *u8
918 let e_psd: *u8 = "psd" as *u8
919 let e_tga: *u8 = "tga" as *u8
920 let e_dds: *u8 = "dds" as *u8
921 let e_exr: *u8 = "exr" as *u8
922 let e_qoi: *u8 = "qoi" as *u8
923 let e_ppm: *u8 = "ppm" as *u8
924 let e_pgm: *u8 = "pgm" as *u8
925 let e_pbm: *u8 = "pbm" as *u8
926 let e_cr2: *u8 = "cr2" as *u8
927 let e_nef: *u8 = "nef" as *u8
928 let e_arw: *u8 = "arw" as *u8
929 let e_dng: *u8 = "dng" as *u8
930 if nx_sniff_ext_eq(p, plen, off, e_png, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
931 if nx_sniff_ext_eq(p, plen, off, e_jpg, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
932 if nx_sniff_ext_eq(p, plen, off, e_jpeg, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
933 if nx_sniff_ext_eq(p, plen, off, e_jpe, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
934 if nx_sniff_ext_eq(p, plen, off, e_webp, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
935 if nx_sniff_ext_eq(p, plen, off, e_bmp, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
936 if nx_sniff_ext_eq(p, plen, off, e_tif, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
937 if nx_sniff_ext_eq(p, plen, off, e_tiff, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
938 if nx_sniff_ext_eq(p, plen, off, e_avif, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
939 if nx_sniff_ext_eq(p, plen, off, e_heic, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
940 if nx_sniff_ext_eq(p, plen, off, e_heif, 4) == 1 { return NX_MEDIA_TYPE_IMAGE }
941 if nx_sniff_ext_eq(p, plen, off, e_jxl, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
942 if nx_sniff_ext_eq(p, plen, off, e_svg, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
943 if nx_sniff_ext_eq(p, plen, off, e_ico, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
944 if nx_sniff_ext_eq(p, plen, off, e_psd, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
945 if nx_sniff_ext_eq(p, plen, off, e_tga, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
946 if nx_sniff_ext_eq(p, plen, off, e_dds, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
947 if nx_sniff_ext_eq(p, plen, off, e_exr, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
948 if nx_sniff_ext_eq(p, plen, off, e_qoi, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
949 if nx_sniff_ext_eq(p, plen, off, e_ppm, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
950 if nx_sniff_ext_eq(p, plen, off, e_pgm, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
951 if nx_sniff_ext_eq(p, plen, off, e_pbm, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
952 if nx_sniff_ext_eq(p, plen, off, e_cr2, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
953 if nx_sniff_ext_eq(p, plen, off, e_nef, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
954 if nx_sniff_ext_eq(p, plen, off, e_arw, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
955 if nx_sniff_ext_eq(p, plen, off, e_dng, 3) == 1 { return NX_MEDIA_TYPE_IMAGE }
956 return NX_MEDIA_TYPE_UNKNOWN
957}
958
959func nx_sniff_ext_av(p: *u8, plen: i64, off: i64) -> i64 {
960 let e_gif: *u8 = "gif" as *u8
961 let e_mp4: *u8 = "mp4" as *u8
962 let e_m4v: *u8 = "m4v" as *u8
963 let e_webm: *u8 = "webm" as *u8
964 let e_mkv: *u8 = "mkv" as *u8
965 let e_mov: *u8 = "mov" as *u8
966 let e_avi: *u8 = "avi" as *u8
967 let e_ts: *u8 = "ts" as *u8
968 let e_m2ts: *u8 = "m2ts" as *u8
969 let e_mts: *u8 = "mts" as *u8
970 let e_mpg: *u8 = "mpg" as *u8
971 let e_mpeg: *u8 = "mpeg" as *u8
972 let e_flv: *u8 = "flv" as *u8
973 let e_wmv: *u8 = "wmv" as *u8
974 let e_ogv: *u8 = "ogv" as *u8
975 let e_3gp: *u8 = "3gp" as *u8
976 let e_mp3: *u8 = "mp3" as *u8
977 let e_wav: *u8 = "wav" as *u8
978 let e_ogg: *u8 = "ogg" as *u8
979 let e_oga: *u8 = "oga" as *u8
980 let e_opus: *u8 = "opus" as *u8
981 let e_flac: *u8 = "flac" as *u8
982 let e_m4a: *u8 = "m4a" as *u8
983 let e_aac: *u8 = "aac" as *u8
984 let e_wma: *u8 = "wma" as *u8
985 let e_aif: *u8 = "aif" as *u8
986 let e_aiff: *u8 = "aiff" as *u8
987 let e_mid: *u8 = "mid" as *u8
988 let e_midi: *u8 = "midi" as *u8
989 let e_ape: *u8 = "ape" as *u8
990 let e_m4b: *u8 = "m4b" as *u8
991 let e_aax: *u8 = "aax" as *u8
992 let e_aa: *u8 = "aa" as *u8
993 if nx_sniff_ext_eq(p, plen, off, e_gif, 3) == 1 { return NX_MEDIA_TYPE_GIF }
994 if nx_sniff_ext_eq(p, plen, off, e_mp4, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
995 if nx_sniff_ext_eq(p, plen, off, e_m4v, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
996 if nx_sniff_ext_eq(p, plen, off, e_webm, 4) == 1 { return NX_MEDIA_TYPE_VIDEO }
997 if nx_sniff_ext_eq(p, plen, off, e_mkv, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
998 if nx_sniff_ext_eq(p, plen, off, e_mov, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
999 if nx_sniff_ext_eq(p, plen, off, e_avi, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1000 if nx_sniff_ext_eq(p, plen, off, e_ts, 2) == 1 { return NX_MEDIA_TYPE_VIDEO }
1001 if nx_sniff_ext_eq(p, plen, off, e_m2ts, 4) == 1 { return NX_MEDIA_TYPE_VIDEO }
1002 if nx_sniff_ext_eq(p, plen, off, e_mts, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1003 if nx_sniff_ext_eq(p, plen, off, e_mpg, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1004 if nx_sniff_ext_eq(p, plen, off, e_mpeg, 4) == 1 { return NX_MEDIA_TYPE_VIDEO }
1005 if nx_sniff_ext_eq(p, plen, off, e_flv, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1006 if nx_sniff_ext_eq(p, plen, off, e_wmv, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1007 if nx_sniff_ext_eq(p, plen, off, e_ogv, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1008 if nx_sniff_ext_eq(p, plen, off, e_3gp, 3) == 1 { return NX_MEDIA_TYPE_VIDEO }
1009 if nx_sniff_ext_eq(p, plen, off, e_m4b, 3) == 1 { return NX_MEDIA_TYPE_AUDIOBOOK }
1010 if nx_sniff_ext_eq(p, plen, off, e_aax, 3) == 1 { return NX_MEDIA_TYPE_AUDIOBOOK }
1011 if nx_sniff_ext_eq(p, plen, off, e_aa, 2) == 1 { return NX_MEDIA_TYPE_AUDIOBOOK }
1012 if nx_sniff_ext_eq(p, plen, off, e_mp3, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1013 if nx_sniff_ext_eq(p, plen, off, e_wav, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1014 if nx_sniff_ext_eq(p, plen, off, e_ogg, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1015 if nx_sniff_ext_eq(p, plen, off, e_oga, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1016 if nx_sniff_ext_eq(p, plen, off, e_opus, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
1017 if nx_sniff_ext_eq(p, plen, off, e_flac, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
1018 if nx_sniff_ext_eq(p, plen, off, e_m4a, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1019 if nx_sniff_ext_eq(p, plen, off, e_aac, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1020 if nx_sniff_ext_eq(p, plen, off, e_wma, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1021 if nx_sniff_ext_eq(p, plen, off, e_aif, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1022 if nx_sniff_ext_eq(p, plen, off, e_aiff, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
1023 if nx_sniff_ext_eq(p, plen, off, e_mid, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1024 if nx_sniff_ext_eq(p, plen, off, e_midi, 4) == 1 { return NX_MEDIA_TYPE_AUDIO }
1025 if nx_sniff_ext_eq(p, plen, off, e_ape, 3) == 1 { return NX_MEDIA_TYPE_AUDIO }
1026 return NX_MEDIA_TYPE_UNKNOWN
1027}
1028
1029func nx_sniff_ext_model3d(p: *u8, plen: i64, off: i64) -> i64 {
1030 let e_stl: *u8 = "stl" as *u8
1031 let e_obj: *u8 = "obj" as *u8
1032 let e_ply: *u8 = "ply" as *u8
1033 let e_glb: *u8 = "glb" as *u8
1034 let e_gltf: *u8 = "gltf" as *u8
1035 let e_3mf: *u8 = "3mf" as *u8
1036 let e_fbx: *u8 = "fbx" as *u8
1037 let e_dae: *u8 = "dae" as *u8
1038 let e_step: *u8 = "step" as *u8
1039 let e_stp: *u8 = "stp" as *u8
1040 let e_iges: *u8 = "iges" as *u8
1041 let e_igs: *u8 = "igs" as *u8
1042 let e_3ds: *u8 = "3ds" as *u8
1043 let e_blend: *u8 = "blend" as *u8
1044 let e_usd: *u8 = "usd" as *u8
1045 let e_usdz: *u8 = "usdz" as *u8
1046 let e_off: *u8 = "off" as *u8
1047 let e_amf: *u8 = "amf" as *u8
1048 let e_vox: *u8 = "vox" as *u8
1049 let e_gcode: *u8 = "gcode" as *u8
1050 let e_scad: *u8 = "scad" as *u8
1051 if nx_sniff_ext_eq(p, plen, off, e_stl, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1052 if nx_sniff_ext_eq(p, plen, off, e_obj, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1053 if nx_sniff_ext_eq(p, plen, off, e_ply, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1054 if nx_sniff_ext_eq(p, plen, off, e_glb, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1055 if nx_sniff_ext_eq(p, plen, off, e_gltf, 4) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1056 if nx_sniff_ext_eq(p, plen, off, e_3mf, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1057 if nx_sniff_ext_eq(p, plen, off, e_fbx, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1058 if nx_sniff_ext_eq(p, plen, off, e_dae, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1059 if nx_sniff_ext_eq(p, plen, off, e_step, 4) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1060 if nx_sniff_ext_eq(p, plen, off, e_stp, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1061 if nx_sniff_ext_eq(p, plen, off, e_iges, 4) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1062 if nx_sniff_ext_eq(p, plen, off, e_igs, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1063 if nx_sniff_ext_eq(p, plen, off, e_3ds, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1064 if nx_sniff_ext_eq(p, plen, off, e_blend, 5) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1065 if nx_sniff_ext_eq(p, plen, off, e_usd, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1066 if nx_sniff_ext_eq(p, plen, off, e_usdz, 4) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1067 if nx_sniff_ext_eq(p, plen, off, e_off, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1068 if nx_sniff_ext_eq(p, plen, off, e_amf, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1069 if nx_sniff_ext_eq(p, plen, off, e_vox, 3) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1070 if nx_sniff_ext_eq(p, plen, off, e_gcode, 5) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1071 if nx_sniff_ext_eq(p, plen, off, e_scad, 4) == 1 { return NX_MEDIA_TYPE_MODEL3D }
1072 return NX_MEDIA_TYPE_UNKNOWN
1073}
1074
1075func nx_sniff_ext_doc(p: *u8, plen: i64, off: i64) -> i64 {
1076 let e_txt: *u8 = "txt" as *u8
1077 let e_md: *u8 = "md" as *u8
1078 let e_rst: *u8 = "rst" as *u8
1079 let e_tex: *u8 = "tex" as *u8
1080 let e_rtf: *u8 = "rtf" as *u8
1081 let e_log: *u8 = "log" as *u8
1082 let e_csv: *u8 = "csv" as *u8
1083 let e_json: *u8 = "json" as *u8
1084 let e_srt: *u8 = "srt" as *u8
1085 let e_vtt: *u8 = "vtt" as *u8
1086 let e_html: *u8 = "html" as *u8
1087 let e_htm: *u8 = "htm" as *u8
1088 let e_xhtml: *u8 = "xhtml" as *u8
1089 let e_warc: *u8 = "warc" as *u8
1090 let e_wacz: *u8 = "wacz" as *u8
1091 let e_mhtml: *u8 = "mhtml" as *u8
1092 let e_mht: *u8 = "mht" as *u8
1093 let e_har: *u8 = "har" as *u8
1094 let e_maff: *u8 = "maff" as *u8
1095 let e_pdf: *u8 = "pdf" as *u8
1096 let e_epub: *u8 = "epub" as *u8
1097 let e_mobi: *u8 = "mobi" as *u8
1098 let e_azw: *u8 = "azw" as *u8
1099 let e_azw3: *u8 = "azw3" as *u8
1100 let e_fb2: *u8 = "fb2" as *u8
1101 let e_djvu: *u8 = "djvu" as *u8
1102 let e_chm: *u8 = "chm" as *u8
1103 let e_doc: *u8 = "doc" as *u8
1104 let e_docx: *u8 = "docx" as *u8
1105 let e_odt: *u8 = "odt" as *u8
1106 let e_cbz: *u8 = "cbz" as *u8
1107 let e_cbr: *u8 = "cbr" as *u8
1108 let e_cb7: *u8 = "cb7" as *u8
1109 let e_cbt: *u8 = "cbt" as *u8
1110 if nx_sniff_ext_eq(p, plen, off, e_cbz, 3) == 1 { return NX_MEDIA_TYPE_MANGA }
1111 if nx_sniff_ext_eq(p, plen, off, e_cbr, 3) == 1 { return NX_MEDIA_TYPE_MANGA }
1112 if nx_sniff_ext_eq(p, plen, off, e_cb7, 3) == 1 { return NX_MEDIA_TYPE_MANGA }
1113 if nx_sniff_ext_eq(p, plen, off, e_cbt, 3) == 1 { return NX_MEDIA_TYPE_MANGA }
1114 if nx_sniff_ext_eq(p, plen, off, e_pdf, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1115 if nx_sniff_ext_eq(p, plen, off, e_epub, 4) == 1 { return NX_MEDIA_TYPE_BOOK }
1116 if nx_sniff_ext_eq(p, plen, off, e_mobi, 4) == 1 { return NX_MEDIA_TYPE_BOOK }
1117 if nx_sniff_ext_eq(p, plen, off, e_azw, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1118 if nx_sniff_ext_eq(p, plen, off, e_azw3, 4) == 1 { return NX_MEDIA_TYPE_BOOK }
1119 if nx_sniff_ext_eq(p, plen, off, e_fb2, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1120 if nx_sniff_ext_eq(p, plen, off, e_djvu, 4) == 1 { return NX_MEDIA_TYPE_BOOK }
1121 if nx_sniff_ext_eq(p, plen, off, e_chm, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1122 if nx_sniff_ext_eq(p, plen, off, e_doc, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1123 if nx_sniff_ext_eq(p, plen, off, e_docx, 4) == 1 { return NX_MEDIA_TYPE_BOOK }
1124 if nx_sniff_ext_eq(p, plen, off, e_odt, 3) == 1 { return NX_MEDIA_TYPE_BOOK }
1125 if nx_sniff_ext_eq(p, plen, off, e_warc, 4) == 1 { return NX_MEDIA_TYPE_WARC }
1126 if nx_sniff_ext_eq(p, plen, off, e_wacz, 4) == 1 { return NX_MEDIA_TYPE_WARC }
1127 if nx_sniff_ext_eq(p, plen, off, e_mhtml, 5) == 1 { return NX_MEDIA_TYPE_WARC }
1128 if nx_sniff_ext_eq(p, plen, off, e_mht, 3) == 1 { return NX_MEDIA_TYPE_WARC }
1129 if nx_sniff_ext_eq(p, plen, off, e_har, 3) == 1 { return NX_MEDIA_TYPE_WARC }
1130 if nx_sniff_ext_eq(p, plen, off, e_maff, 4) == 1 { return NX_MEDIA_TYPE_WARC }
1131 if nx_sniff_ext_eq(p, plen, off, e_html, 4) == 1 { return NX_MEDIA_TYPE_HTML }
1132 if nx_sniff_ext_eq(p, plen, off, e_htm, 3) == 1 { return NX_MEDIA_TYPE_HTML }
1133 if nx_sniff_ext_eq(p, plen, off, e_xhtml, 5) == 1 { return NX_MEDIA_TYPE_HTML }
1134 if nx_sniff_ext_eq(p, plen, off, e_txt, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1135 if nx_sniff_ext_eq(p, plen, off, e_md, 2) == 1 { return NX_MEDIA_TYPE_TEXT }
1136 if nx_sniff_ext_eq(p, plen, off, e_rst, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1137 if nx_sniff_ext_eq(p, plen, off, e_tex, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1138 if nx_sniff_ext_eq(p, plen, off, e_rtf, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1139 if nx_sniff_ext_eq(p, plen, off, e_log, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1140 if nx_sniff_ext_eq(p, plen, off, e_csv, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1141 if nx_sniff_ext_eq(p, plen, off, e_json, 4) == 1 { return NX_MEDIA_TYPE_TEXT }
1142 if nx_sniff_ext_eq(p, plen, off, e_srt, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1143 if nx_sniff_ext_eq(p, plen, off, e_vtt, 3) == 1 { return NX_MEDIA_TYPE_TEXT }
1144 return NX_MEDIA_TYPE_UNKNOWN
1145}
1146
1147func nx_sniff_extension(path: *u8, plen: i64) -> i64 {
1148 let off: i64 = nx_sniff_ext_off(path, plen)
1149 if off < 0 { return NX_MEDIA_TYPE_UNKNOWN }
1150 let v_img: i64 = nx_sniff_ext_image(path, plen, off)
1151 if v_img != NX_MEDIA_TYPE_UNKNOWN { return v_img }
1152 let v_av: i64 = nx_sniff_ext_av(path, plen, off)
1153 if v_av != NX_MEDIA_TYPE_UNKNOWN { return v_av }
1154 let v_3d: i64 = nx_sniff_ext_model3d(path, plen, off)
1155 if v_3d != NX_MEDIA_TYPE_UNKNOWN { return v_3d }
1156 return nx_sniff_ext_doc(path, plen, off)
1157}
1158
1159// ===== four-tier composite sniffer ================================
1160//
1161// Magic > MIME > extension > printable-fallback, with ONE deliberate
1162// exception: a ZIP/RAR/7z container verdict and a generic-TEXT verdict are
1163// both CONTAINER answers, and the extension is what distinguishes
1164// epub-vs-cbz-vs-3mf and obj-vs-prose. For those two the extension refines;
1165// every other magic wins outright.
1166//
1167// `filesize` is the TRUE byte length of the item. Pass it whenever known --
1168// binary STL has no magic and is identified purely by its size equation.
1169func nx_sniff_full(b: *u8, n: i64, filesize: i64,
1170 mime: *u8, mlen: i64,
1171 path: *u8, plen: i64) -> i64 {
1172 let v_magic: i64 = nx_sniff_bytes_strict(b, n, filesize)
1173
1174 if v_magic == NX_MEDIA_TYPE_ARCHIVE {
1175 let r: i64 = nx_sniff_extension(path, plen)
1176 if r != NX_MEDIA_TYPE_UNKNOWN { return r }
1177 return NX_MEDIA_TYPE_ARCHIVE
1178 }
1179 if v_magic != NX_MEDIA_TYPE_UNKNOWN { return v_magic }
1180
1181 let v_mime: i64 = nx_sniff_mime(mime, mlen)
1182 if v_mime != NX_MEDIA_TYPE_UNKNOWN { return v_mime }
1183
1184 let v_ext: i64 = nx_sniff_extension(path, plen)
1185 if v_ext != NX_MEDIA_TYPE_UNKNOWN { return v_ext }
1186
1187 // last resort: prose
1188 if nx_sniff_is_mostly_printable(b, n) == 1 { return NX_MEDIA_TYPE_TEXT }
1189 return NX_MEDIA_TYPE_UNKNOWN
1190}
1191
1192// Original three-arg-pair entry point. filesize is unknown here, so the
1193// buffer length stands in: correct whenever the caller passed the whole item
1194// (which every current vault caller does), and merely skips the binary-STL
1195// size check when it did not.
1196func nx_sniff(b: *u8, n: i64,
1197 mime: *u8, mlen: i64,
1198 path: *u8, plen: i64) -> i64 {
1199 return nx_sniff_full(b, n, n, mime, mlen, path, plen)
1200}