code wiki / (root) / nx_media_sniff.nx

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}