// File Structure Parser — PE / ELF / JPEG / PNG header analysis
///|
/// A parsed field from a file structure.
struct StructField {
offset : Int
size : Int
name : String
value : String
description : String
children : Array[StructField]
} derive(Debug)
///|
fn field(
off : Int,
sz : Int,
name : String,
val : String,
desc : String,
) -> StructField {
{ offset: off, size: sz, name, value: val, description: desc, children: [] }
}
///|
fn group(
off : Int,
sz : Int,
name : String,
desc : String,
kids : Array[StructField],
) -> StructField {
{ offset: off, size: sz, name, value: "", description: desc, children: kids }
}
///|
/// Parse structure of file bytes. Returns top-level fields or empty array.
pub fn parse_structure(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
guard len >= 4 else { return [] }
let magic_le = read_u32_le(bytes, 0)
let magic_be = read_u16_be(bytes, 0)
if magic_be == 0xFFD8 {
return parse_jpeg(bytes)
}
if magic_be == 0x8950 {
return parse_png(bytes)
}
if magic_be == 0x4D5A || magic_be == 0x5A4D {
return parse_pe(bytes)
}
if magic_le == 0x04034B50 {
return parse_zip(bytes)
}
if len >= 6 &&
(read_str(bytes, 0, 6) == "GIF87a" || read_str(bytes, 0, 6) == "GIF89a") {
return parse_gif(bytes)
}
if magic_be == 0x424D {
return parse_bmp(bytes)
}
if len >= 7 && read_str(bytes, 0, 4) == "Rar!" {
return parse_rar(bytes)
}
if len >= 512 && read_str(bytes, 257, 5) == "ustar" {
return parse_tar(bytes)
}
if len >= 2 && is_zlib(bytes) {
return parse_zlib(bytes)
}
// Audio/Video
if len >= 12 &&
read_str(bytes, 0, 4) == "RIFF" &&
read_str(bytes, 8, 4) == "WAVE" {
return parse_wav(bytes)
}
if len >= 12 &&
read_str(bytes, 0, 4) == "RIFF" &&
read_str(bytes, 8, 4) == "AVI " {
return parse_avi(bytes)
}
if magic_be == 0x664C {
return parse_flac(bytes)
} // "fL"
if len >= 4 && read_str(bytes, 0, 4) == "OggS" {
return parse_ogg(bytes)
}
if len >= 8 && read_str(bytes, 0, 3) == "ID3" {
return parse_mp3(bytes)
}
if len >= 8 && read_u32_be(bytes, 4) == 0x66747970 {
return parse_mp4(bytes)
} // "ftyp"
if read_u32_be(bytes, 0) == 0x1A45DFA3 {
return parse_webm(bytes)
}
// ELF
if len >= 4 &&
bytes[0] == b'\x7F' &&
bytes[1] == b'E' &&
bytes[2] == b'L' &&
bytes[3] == b'F' {
return parse_elf(bytes)
}
// GZip
if len >= 10 && bytes[0].to_int() == 0x1F && bytes[1].to_int() == 0x8B {
return parse_gzip(bytes)
}
// 7z
if len >= 12 &&
bytes[0].to_int() == 0x37 &&
bytes[1].to_int() == 0x7A &&
bytes[2].to_int() == 0xBC {
return parse_7z(bytes)
}
// BZip2
if len >= 10 &&
bytes[0].to_int() == 0x42 &&
bytes[1].to_int() == 0x5A &&
bytes[2].to_int() == 0x68 {
return parse_bzip2(bytes)
}
return []
}
///|
/// Check if data starts with a valid ZLIB header.
fn is_zlib(bytes : Bytes) -> Bool {
guard bytes.length() >= 2 else { return false }
let cmf = bytes[0].to_int()
let flg = bytes[1].to_int()
let cm = cmf & 0x0F
cm == 8 && (cmf * 256 + flg) % 31 == 0
}
///|
fn read_u16_be(bytes : Bytes, off : Int) -> Int {
guard off + 2 <= bytes.length() else { 0 }
bytes[off].to_int() * 256 + bytes[off + 1].to_int()
}
///|
fn read_u32_be(bytes : Bytes, off : Int) -> Int {
guard off + 4 <= bytes.length() else { 0 }
let mut r = 0
for i = 0; i < 4; i = i + 1 {
r = r * 256 + bytes[off + i].to_int()
}
r
}
///|
fn read_u16_le(bytes : Bytes, off : Int) -> Int {
guard off + 2 <= bytes.length() else { 0 }
bytes[off].to_int() + bytes[off + 1].to_int() * 256
}
///|
fn read_u32_le(bytes : Bytes, off : Int) -> Int {
guard off + 4 <= bytes.length() else { 0 }
let mut r = 0
for i = 3; i >= 0; i = i - 1 {
r = r * 256 + bytes[off + i].to_int()
}
r
}
///|
fn read_u64_le(bytes : Bytes, off : Int) -> Int {
guard off + 8 <= bytes.length() else { 0 }
let mut r = 0
for i = 7; i >= 0; i = i - 1 {
r = r * 256 + bytes[off + i].to_int()
}
r
}
///|
fn read_u64_be(bytes : Bytes, off : Int) -> Int {
guard off + 8 <= bytes.length() else { 0 }
let mut r = 0
for i = 0; i < 8; i = i + 1 {
r = r * 256 + bytes[off + i].to_int()
}
r
}
///|
fn hex(n : Int, width : Int) -> String {
format_offset(n, width~)
}
///|
fn read_str(bytes : Bytes, off : Int, max : Int) -> String {
let result = StringBuilder()
let len = bytes.length()
for i = 0; i < max; i = i + 1 {
if off + i >= len {
break
}
let b = bytes[off + i]
if b == b'\x00' {
break
}
if b.to_int() >= 32 && b.to_int() < 127 {
let ch = b.to_int().to_char()
match ch {
Some(c) => result.write_char(c)
None => ()
}
}
}
result.to_string()
}
// ============== JPEG ==============
// Helper: find FOURCC at or after a position, with max search bound
///|
fn find_fourcc(
data : Bytes,
start : Int,
fourcc : String,
max_off : Int,
) -> Int {
for p = start; p + 8 <= max_off && p + 8 <= data.length(); p = p + 2 {
if read_str(data, p, 4) == fourcc {
return p
}
}
-1
}
// Helper: read FOURCC as string from offset
///|
fn fourcc_at(data : Bytes, off : Int) -> String {
read_str(data, off, 4)
}
///|
fn parse_jpeg(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(field(0, 2, "SOI", "FF D8", "Start of Image"))
let mut pos = 2
for iter = 0; pos + 2 < len && iter < 50; iter = iter + 1 {
if bytes[pos].to_int() != 0xFF {
break
}
let marker = bytes[pos + 1].to_int()
if marker == 0xD9 {
fields.push(field(pos, 2, "EOI", "FF D9", "End of Image"))
break
}
if marker == 0xDA {
let sl = read_u16_be(bytes, pos + 2)
fields.push(field(pos, 2, "SOS", "FF DA", "Start of Scan"))
let scan_start = pos + 2 + sl
let mut scan_end = len
for j = scan_start; j + 1 < len; j = j + 1 {
if bytes[j].to_int() == 0xFF && bytes[j + 1].to_int() != 0x00 {
scan_end = j
break
}
}
if scan_end > scan_start {
fields.push(
field(
scan_start,
scan_end - scan_start,
"ScanData",
"\{scan_end - scan_start} bytes",
"Compressed image data",
),
)
}
pos = scan_end
continue
}
if marker >= 0xD0 && marker <= 0xD7 {
let nm = match marker {
0xD0 => "RST0"
0xD1 => "RST1"
0xD2 => "RST2"
0xD3 => "RST3"
0xD4 => "RST4"
0xD5 => "RST5"
0xD6 => "RST6"
0xD7 => "RST7"
_ => "RSTn"
}
fields.push(
field(
pos,
2,
nm,
"FF \{format_byte_hex(bytes[pos + 1])}",
"Restart marker",
),
)
pos = pos + 2
continue
}
if pos + 4 > len {
break
}
let sl = read_u16_be(bytes, pos + 2)
let nm = jpeg_marker_name(marker)
// Extract dimensions for SOF markers
let desc = if marker >= 0xC0 && marker <= 0xC2 && pos + 9 <= len {
let h = read_u16_be(bytes, pos + 5)
let w = read_u16_be(bytes, pos + 7)
"\{nm}: \{w}x\{h} (0x\{hex(w, 4)} x 0x\{hex(h, 4)}), \{bytes[pos + 4].to_int()}bpp, \{sl} bytes"
} else {
"\{nm} segment, \{sl} bytes"
}
fields.push(
field(pos, 2, nm, "FF \{format_byte_hex(bytes[pos + 1])}", desc),
)
pos = pos + 2 + sl
}
// Fallback: check end of file for EOI if not found during scan
if pos < len {
if len >= 2 &&
bytes[len - 2].to_int() == 0xFF &&
bytes[len - 1].to_int() == 0xD9 {
if pos < len - 2 {
fields.push(
field(
pos,
len - 2 - pos,
"Trailing",
"\{len - 2 - pos} bytes",
"Remaining data",
),
)
}
fields.push(field(len - 2, 2, "EOI", "FF D9", "End of Image"))
} else {
fields.push(
field(
pos,
len - pos,
"Trailing",
"\{len - pos} bytes",
"Remaining data",
),
)
}
}
fields
}
///|
fn jpeg_marker_name(m : Int) -> String {
match m {
0xE0 => "APP0"
0xE1 => "APP1"
0xE2 => "APP2"
0xE3 => "APP3"
0xE4 => "APP4"
0xE5 => "APP5"
0xE6 => "APP6"
0xE7 => "APP7"
0xE8 => "APP8"
0xE9 => "APP9"
0xEA => "APP10"
0xEB => "APP11"
0xEC => "APP12"
0xED => "APP13"
0xEE => "APP14"
0xEF => "APP15"
0xDB => "DQT"
0xC0 => "SOF0"
0xC1 => "SOF1"
0xC2 => "SOF2"
0xC4 => "DHT"
0xDD => "DRI"
0xFE => "COM"
_ => "UNK"
}
}
// ============== PNG ==============
///|
fn parse_png(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(
field(0, 8, "Signature", "89 50 4E 47 0D 0A 1A 0A", "PNG signature"),
)
let mut pos = 8
for _i = 0; pos + 12 <= len && _i < 50; _i = _i + 1 {
let dlen = read_u32_be(bytes, pos)
let ctype = read_str(bytes, pos + 4, 4)
let total = 12 + dlen
let desc = match ctype {
"IHDR" => {
let w = read_u32_be(bytes, pos + 8)
let h = read_u32_be(bytes, pos + 12)
"Image Header: \{w}x\{h} (0x\{hex(w, 8)} x 0x\{hex(h, 8)}), \{bytes[pos + 16].to_int()}bpp"
}
"PLTE" => "Palette: \{dlen / 3} colors"
"IDAT" => "Image data block"
"IEND" => "Image end"
"tEXt" => "Text metadata"
"tIME" => "Last modification time"
"pHYs" => "Physical dimensions"
_ => "\{dlen} bytes"
}
fields.push(field(pos, total, ctype, "\{dlen} bytes", desc))
if ctype == "IEND" {
break
}
pos = pos + total
}
fields
}
// ============== PE ==============
///|
fn parse_pe(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
guard len >= 64 else {
return [field(0, len, "TRUNCATED", "", "Too small for PE")]
}
let fields = []
let e_lfanew = read_u32_le(bytes, 60)
fields.push(
group(0, 64, "DOS Header", "MZ signature", [
field(0, 2, "e_magic", "MZ", "DOS signature"),
field(60, 4, "e_lfanew", "0x\{hex(e_lfanew, 8)}", "PE offset"),
]),
)
let pe_off = e_lfanew
if pe_off > 64 {
fields.push(
field(
64,
pe_off - 64,
"DOS Stub",
"\{pe_off - 64} bytes",
"DOS stub program",
),
)
}
guard pe_off + 4 <= len else { return fields }
let sig = read_str(bytes, pe_off, 4)
guard sig == "PE" else {
fields.push(field(pe_off, 4, "PE Sig?", sig, "Expected PE"))
return fields
}
fields.push(field(pe_off, 4, "Signature", "PE", "PE signature"))
let coff = pe_off + 4
guard coff + 20 <= len else { return fields }
let machine = read_u16_le(bytes, coff)
let nsections = read_u16_le(bytes, coff + 2)
let sz_opt = read_u16_le(bytes, coff + 16)
let mach_name = match machine {
0x014C => "i386"
0x8664 => "AMD64"
0x01C4 => "ARM"
0xAA64 => "ARM64"
_ => "0x\{hex(machine, 4)}"
}
fields.push(
group(
coff,
20,
"COFF Header",
"Machine: \{mach_name}, \{nsections} sections",
[
field(coff, 2, "Machine", mach_name, ""),
field(coff + 2, 2, "NumSections", "\{nsections}", ""),
field(
coff + 4,
4,
"Timestamp",
"0x\{hex(read_u32_le(bytes, coff + 4), 8)}",
"",
),
field(coff + 16, 2, "SizeOfOptional", "\{sz_opt}", ""),
],
),
)
let opt = coff + 20
guard opt + sz_opt <= len else { return fields }
if sz_opt >= 2 {
let omagic = read_u16_le(bytes, opt)
let pe_type = if omagic == 0x20B {
"PE32+"
} else if omagic == 0x10B {
"PE32"
} else {
"?"
}
let ep = if sz_opt >= 20 { read_u32_le(bytes, opt + 16) } else { 0 }
let ibase = if sz_opt >= 32 { read_u32_le(bytes, opt + 28) } else { 0 }
let kids = [
field(opt, 2, "Magic", pe_type, ""),
field(opt + 16, 4, "EntryPoint", "0x\{hex(ep, 8)}", ""),
field(opt + 28, 4, "ImageBase", "0x\{hex(ibase, 8)}", ""),
]
fields.push(group(opt, sz_opt, "Optional Header", pe_type, kids))
}
let sec_start = opt + sz_opt
for i = 0; i < nsections; i = i + 1 {
let so = sec_start + i * 40
if so + 40 > len {
break
}
let sn = read_str(bytes, so, 8)
let vs = read_u32_le(bytes, so + 8)
let va = read_u32_le(bytes, so + 12)
let rs = read_u32_le(bytes, so + 16)
let ro = read_u32_le(bytes, so + 20)
fields.push(
group(
so,
40,
"Section: \{sn}",
"VAddr=0x\{hex(va, 8)} RawSize=0x\{hex(rs, 8)}",
[
field(so, 8, "Name", sn, ""),
field(so + 8, 4, "VirtualSize", "0x\{hex(vs, 8)}", ""),
field(so + 12, 4, "VirtualAddress", "0x\{hex(va, 8)}", ""),
field(so + 16, 4, "RawSize", "0x\{hex(rs, 8)}", ""),
field(so + 20, 4, "RawOffset", "0x\{hex(ro, 8)}", ""),
],
),
)
}
fields
}
// ============== ZIP ==============
///|
fn parse_zip(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
// Scan local file headers
let mut pos = 0
let mut file_count = 0
for iter = 0; pos + 30 <= len && iter < 200; iter = iter + 1 {
let sig = read_u32_le(bytes, pos)
if sig == 0x04034B50 {
file_count = file_count + 1
let ver = read_u16_le(bytes, pos + 4)
let flags = read_u16_le(bytes, pos + 6)
let cmethod = read_u16_le(bytes, pos + 8)
let crc = read_u32_le(bytes, pos + 14)
let comp_size = read_u32_le(bytes, pos + 18)
let uncomp_size = read_u32_le(bytes, pos + 22)
let name_len = read_u16_le(bytes, pos + 26)
let file_time = read_u16_le(bytes, pos + 10)
let file_date = read_u16_le(bytes, pos + 12)
let extra_len = read_u16_le(bytes, pos + 28)
let cmethod_str = match cmethod {
0 => "Stored"
8 => "Deflated"
12 => "BZip2"
14 => "LZMA"
_ => "Unknown(\{cmethod})"
}
let name = if name_len > 0 && pos + 30 + name_len <= len {
read_str(bytes, pos + 30, name_len)
} else {
""
}
let total = 30 + name_len + extra_len
let mut desc = cmethod_str
if comp_size > 0 && uncomp_size > 0 {
desc = "\{desc}, \{comp_size} -> \{uncomp_size} bytes"
}
if name != "" {
desc = "\{desc}: \{name}"
}
// Check encryption flag
let encrypted = (flags & 1) != 0
if encrypted {
let pseudo = crc == 0 && comp_size == 0 && uncomp_size == 0
desc = if pseudo {
"\{desc} [Encrypted? - CRC/size in data descriptor]"
} else {
"\{desc} [Encrypted]"
}
}
fields.push(
group(pos, total, "LocalFile #\{file_count}", desc, [
field(pos, 4, "Signature", "PK\\03\\04", ""),
field(pos + 4, 2, "Version", "\{ver}", ""),
field(
pos + 6,
2,
"Flags",
"0x\{hex(flags, 4)}",
if (flags & 1) != 0 {
"Encrypted"
} else if (flags & 8) != 0 {
"DataDescriptor"
} else {
""
},
),
field(pos + 8, 2, "Method", cmethod_str, ""),
field(pos + 10, 2, "FileTime", "0x" + hex(file_time, 4), ""),
field(pos + 12, 2, "FileDate", "0x" + hex(file_date, 4), ""),
field(pos + 14, 4, "CRC32", "0x\{hex(crc, 8)}", ""),
field(pos + 18, 4, "CompressedSize", "\{comp_size}", ""),
field(pos + 22, 4, "UncompressedSize", "\{uncomp_size}", ""),
field(pos + 26, 2, "FileNameLen", "\{name_len}", ""),
field(pos + 28, 2, "ExtraLen", "\{extra_len}", ""),
]),
)
// File data follows
let data_start = pos + total
if comp_size > 0 && data_start + comp_size <= len {
fields.push(
field(
data_start,
comp_size,
"FileData #\{file_count}",
"\{comp_size} bytes",
"Compressed data",
),
)
pos = data_start + comp_size
} else {
pos = pos + total
}
} else if sig == 0x02014B50 {
// Central directory entry - stop scanning local files
break
} else if sig == 0x06054B50 {
// EOCD
let disk = read_u16_le(bytes, pos + 4)
let cd_entries = read_u16_le(bytes, pos + 10)
let cd_size = read_u32_le(bytes, pos + 12)
let cd_offset = read_u32_le(bytes, pos + 16)
let comment_len = read_u16_le(bytes, pos + 20)
fields.push(
group(
pos,
22 + comment_len,
"EOCD",
"\{cd_entries} entries in central dir",
[
field(pos, 4, "Signature", "PK\\05\\06", ""),
field(pos + 4, 2, "DiskNumber", "\{disk}", ""),
field(pos + 10, 2, "CentralDirEntries", "\{cd_entries}", ""),
field(pos + 12, 4, "CentralDirSize", "\{cd_size} bytes", ""),
field(pos + 16, 4, "CentralDirOffset", "0x\{hex(cd_offset, 8)}", ""),
field(pos + 20, 2, "CommentLen", "\{comment_len}", ""),
],
),
)
break
} else {
break
}
}
if fields.length() == 0 {
fields.push(
field(0, len, "Unknown ZIP", "\{len} bytes", "ZIP signature not found"),
)
}
fields
}
// ============== GIF ==============
///|
fn parse_gif(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let ver = read_str(bytes, 0, 6)
fields.push(field(0, 6, "Signature", ver, "GIF signature"))
let w = read_u16_le(bytes, 6)
let h = read_u16_le(bytes, 8)
let packed = bytes[10].to_int()
let bg = bytes[11].to_int()
let gct_size = 1 << ((packed & 7) + 1)
let has_gct = (packed >> 7) & 1
fields.push(
group(
6,
7,
"Screen Descriptor",
"\{w}x\{h} (0x\{hex(w, 4)} x 0x\{hex(h, 4)}), GCT=\{gct_size} colors",
[
field(6, 2, "Width", "\{w} (0x\{hex(w, 4)})", ""),
field(8, 2, "Height", "\{h} (0x\{hex(h, 4)})", ""),
field(
10,
1,
"Packed",
"0x\{hex(packed, 2)}",
"GCT=\{has_gct}, Size=\{gct_size}",
),
field(11, 1, "Background", "\{bg}", "Background color index"),
field(12, 1, "Aspect", "\{bytes[12].to_int()}", "Pixel aspect ratio"),
],
),
)
let mut pos = 13
if has_gct != 0 {
let gct_bytes = gct_size * 3
fields.push(
field(
pos,
gct_bytes,
"Global Color Table",
"\{gct_size} colors x 3 bytes",
"",
),
)
pos = pos + gct_bytes
}
let mut img_count = 0
for iter = 0; pos < len && iter < 50; iter = iter + 1 {
let b = bytes[pos].to_int()
if b == 0x2C {
img_count = img_count + 1
let iw = read_u16_le(bytes, pos + 5)
let ih = read_u16_le(bytes, pos + 7)
fields.push(
group(
pos,
10,
"Image #\{img_count}",
"\{iw}x\{ih} at (\{read_u16_le(bytes, pos + 1)}, \{read_u16_le(bytes, pos + 3)})",
[
field(pos, 1, "Separator", "0x2C", "Image descriptor"),
field(pos + 1, 2, "Left", "\{read_u16_le(bytes, pos + 1)}", ""),
field(pos + 3, 2, "Top", "\{read_u16_le(bytes, pos + 3)}", ""),
field(pos + 5, 2, "Width", "\{iw}", ""),
field(pos + 7, 2, "Height", "\{ih}", ""),
field(
pos + 9,
1,
"Packed",
"0x\{hex(bytes[pos + 9].to_int(), 2)}",
"",
),
],
),
)
pos = pos + 10
// Skip LZW code size and image data
if pos < len {
let lzw_size = bytes[pos].to_int()
fields.push(
field(pos, 1, "LZWMinCode", "\{lzw_size}", "LZW minimum code size"),
)
pos = pos + 1
while pos < len {
let block_size = bytes[pos].to_int()
if block_size == 0 {
pos = pos + 1
break
}
if pos + 1 + block_size <= len {
fields.push(
field(pos, 1 + block_size, "DataBlock", "\{block_size} bytes", ""),
)
pos = pos + 1 + block_size
} else {
break
}
}
}
} else if b == 0x21 {
if pos + 1 < len {
let ext_type = bytes[pos + 1].to_int()
let ext_name = match ext_type {
0xF9 => "GCE"
0xFE => "Comment"
0xFF => "App"
0x01 => "Text"
_ => "Ext"
}
let mut ext_pos = pos + 2
while ext_pos < len {
let bs = bytes[ext_pos].to_int()
if bs == 0 {
ext_pos = ext_pos + 1
break
}
if ext_pos + 1 + bs <= len {
fields.push(
field(ext_pos, 1 + bs, "\{ext_name}Data", "\{bs} bytes", ""),
)
ext_pos = ext_pos + 1 + bs
} else {
break
}
}
pos = ext_pos
} else {
break
}
} else if b == 0x3B {
fields.push(field(pos, 1, "Trailer", "0x3B", "End of GIF"))
break
} else {
break
}
}
fields
}
// ============== BMP ==============
///|
fn parse_bmp(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
guard len >= 14 else {
return [field(0, len, "TRUNCATED", "", "Too small for BMP")]
}
let file_size = read_u32_le(bytes, 2)
let data_off = read_u32_le(bytes, 10)
let hdr_size = read_u32_le(bytes, 14)
let w = read_u32_le(bytes, 18)
let h = read_u32_le(bytes, 22)
let planes = read_u16_le(bytes, 26)
let bpp = read_u16_le(bytes, 28)
let comp = read_u32_le(bytes, 30)
let comp_str = match comp {
0 => "RGB"
1 => "RLE8"
2 => "RLE4"
3 => "Bitfields"
_ => "?"
}
fields.push(
group(
0,
14,
"BMP Header",
"\{file_size} bytes, data at 0x\{hex(data_off, 8)}",
[
field(0, 2, "Signature", "BM", ""),
field(2, 4, "FileSize", "\{file_size}", ""),
field(10, 4, "DataOffset", "0x\{hex(data_off, 8)}", ""),
],
),
)
fields.push(
group(
14,
hdr_size,
"DIB Header",
"\{w}x\{h} (0x\{hex(w, 8)} x 0x\{hex(h, 8)}), \{bpp}bpp, \{comp_str}",
[
field(14, 4, "HeaderSize", "\{hdr_size}", ""),
field(18, 4, "Width", "\{w} (0x\{hex(w, 8)})", ""),
field(22, 4, "Height", "\{h} (0x\{hex(h, 8)})", ""),
field(26, 2, "Planes", "\{planes}", ""),
field(28, 2, "BitsPerPixel", "\{bpp}", ""),
field(30, 4, "Compression", comp_str, ""),
],
),
)
let pixel_start = 14 + hdr_size
if data_off > pixel_start {
fields.push(
field(
pixel_start,
data_off - pixel_start,
"Palette/Gap",
"\{data_off - pixel_start} bytes",
"Color table or gap",
),
)
}
if data_off < len {
fields.push(
field(
data_off,
len - data_off,
"PixelData",
"\{len - data_off} bytes",
"Bitmap pixels",
),
)
}
fields
}
// ============== RAR ==============
///|
fn parse_rar(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let ver = read_str(bytes, 0, 4)
fields.push(field(0, 7, "Signature", "\{ver}!\\x1A\\x07", "RAR archive"))
let is_rar5 = len >= 8 && bytes[7].to_int() == 1
fields.push(field(7, 1, "Version", if is_rar5 { "RAR5" } else { "RAR4" }, ""))
let mut pos = 8
let mut block_count = 0
for iter = 0; pos + 7 <= len && iter < 100; iter = iter + 1 {
let head_crc = read_u16_le(bytes, pos)
let head_type = bytes[pos + 2].to_int()
let head_flags = read_u16_le(bytes, pos + 3)
let head_size = read_u16_le(bytes, pos + 5)
if head_size == 0 || pos + head_size > len {
break
}
let type_str = match head_type {
0x72 => "Marker"
0x73 => "Archive"
0x74 => "File"
0x75 => "Comment"
0x76 => "Extra"
0x77 => "OldRecovery"
0x78 => "SubBlock"
0x79 => "Recovery"
0x7A => "Authenticity"
_ => "Type_\{head_type}"
}
let desc = if head_type == 0x74 {
let name_len = if pos + 32 <= len {
read_u16_le(bytes, pos + 7)
} else {
0
}
if name_len > 0 && pos + 32 + name_len <= len {
read_str(bytes, pos + 32, name_len)
} else {
""
}
} else {
""
}
let encrypted = if is_rar5 {
(head_flags & 0x80) != 0
} else {
(head_flags & 0x01) != 0
}
let mut desc_text = if desc != "" { ": \{desc}" } else { "" }
if encrypted {
desc_text = "\{desc_text} [Encrypted]"
}
fields.push(
group(
pos,
head_size,
"\{type_str} Block #\{block_count + 1}",
"\{head_size} bytes\{desc_text}",
[
field(pos, 2, "CRC16", "0x\{hex(head_crc, 4)}", ""),
field(pos + 2, 1, "Type", type_str, ""),
field(
pos + 3,
2,
"Flags",
"0x\{hex(head_flags, 4)}",
if encrypted {
"Encrypted"
} else {
""
},
),
field(pos + 5, 2, "Size", "\{head_size}", ""),
],
),
)
block_count = block_count + 1
pos = pos + head_size
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
// ============== TAR ==============
///|
fn parse_tar(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(field(257, 5, "USTAR", "ustar", "POSIX tar format"))
let mut pos = 0
let mut file_count = 0
for iter = 0; pos + 512 <= len && iter < 200; iter = iter + 1 {
// Check for end: two zero blocks
let mut empty = true
for i = pos; i < pos + 512 && i < len; i = i + 1 {
if bytes[i] != b'\x00' {
empty = false
break
}
}
if empty {
break
}
let name = read_str(bytes, pos, 100)
let sz_str = read_str(bytes, pos + 124, 12)
let size = parse_octal(sz_str)
let typeflag = bytes[pos + 156].to_int()
let tf = if typeflag == 0 || typeflag == '0'.to_int() {
"File"
} else if typeflag == '5'.to_int() {
"Dir"
} else if typeflag == '2'.to_int() {
"Symlink"
} else if typeflag == '1'.to_int() {
"Hardlink"
} else {
"?"
}
let blocks = (size + 511) / 512
let total = if size > 0 { (blocks + 1) * 512 } else { 512 }
if name != "" || size > 0 {
file_count = file_count + 1
fields.push(
group(pos, total, "\{tf} #\{file_count}", "\{name} (\{size} bytes)", [
field(pos, 100, "Name", name, ""),
field(pos + 100, 8, "Mode", read_str(bytes, pos + 100, 8), ""),
field(pos + 124, 12, "Size", "\{size}", "\{blocks} data blocks"),
field(
pos + 136,
12,
"MTime",
read_str(bytes, pos + 136, 12),
"Unix timestamp",
),
field(pos + 156, 1, "Type", tf, ""),
]),
)
}
pos = pos + total
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
///|
/// Parse octal string to integer (used by TAR).
fn parse_octal(s : String) -> Int {
let mut result = 0
for i = 0; i < s.length(); i = i + 1 {
let c = s.get_char(i).unwrap().to_int()
if c >= '0'.to_int() && c <= '7'.to_int() {
result = result * 8 + (c - '0'.to_int())
}
}
result
}
// ============== ZLIB ==============
///|
fn parse_zlib(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let cmf = bytes[0].to_int()
let flg = bytes[1].to_int()
let cm = cmf & 0x0F
let cinfo = (cmf >> 4) & 0x0F
let fdict = (flg >> 5) & 1
let flevel = (flg >> 6) & 3
let level_str = match flevel {
0 => "Fastest"
1 => "Fast"
2 => "Default"
3 => "Slowest/Best"
_ => "?"
}
let window = 1 << (cinfo + 8)
fields.push(
group(0, 2, "ZLIB Header", "Deflate, window=\{window}B, \{level_str}", [
field(0, 1, "CMF", "0x\{hex(cmf, 2)}", "Method=\{cm}, WinSize=\{window}"),
field(
1,
1,
"FLG",
"0x\{hex(flg, 2)}",
"Level=\{level_str}, FDICT=\{fdict}",
),
]),
)
let mut pos = 2
if fdict != 0 && pos + 4 <= len {
let dict_id = read_u32_be(bytes, pos)
fields.push(
field(pos, 4, "DictID", "0x\{hex(dict_id, 8)}", "Preset dictionary ID"),
)
pos = pos + 4
}
// Compressed data - until 4 bytes before end (Adler-32)
let data_end = len - 4
if data_end > pos {
fields.push(
field(
pos,
data_end - pos,
"CompressedData",
"\{data_end - pos} bytes",
"Deflate stream",
),
)
}
// Adler-32 checksum at end
if len >= 4 {
let adler = read_u32_be(bytes, len - 4)
fields.push(field(len - 4, 4, "Adler32", "0x\{hex(adler, 8)}", "Checksum"))
}
fields
}
// ============== WAV ==============
///|
fn parse_wav(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(field(0, 4, "RIFF", "RIFF", "Resource Interchange File Format"))
let file_size = read_u32_le(bytes, 4)
fields.push(
field(
4,
4,
"FileSize",
"\{file_size} (0x\{hex(file_size, 8)})",
"File size - 8",
),
)
fields.push(field(8, 4, "WAVE", "WAVE", "WAVE format"))
let mut pos = 12
for iter = 0; pos + 8 <= len && iter < 30; iter = iter + 1 {
let ck_id = read_str(bytes, pos, 4)
let ck_size = read_u32_le(bytes, pos + 4)
if ck_size == 0 || pos + 8 + ck_size > len {
break
}
if ck_id == "fmt " {
let audio_fmt = read_u16_le(bytes, pos + 8)
let channels = read_u16_le(bytes, pos + 10)
let sample_rate = read_u32_le(bytes, pos + 12)
let bps = read_u16_le(bytes, pos + 22)
let fmt_str = match audio_fmt {
1 => "PCM"
3 => "IEEE Float"
6 => "ALaw"
7 => "MuLaw"
_ => "0x\{hex(audio_fmt, 4)}"
}
fields.push(
group(
pos,
8 + ck_size,
"fmt ",
"\{fmt_str}, \{channels}ch, \{sample_rate}Hz, \{bps}bit",
[
field(pos, 4, "ChunkID", "fmt ", ""),
field(pos + 4, 4, "ChunkSize", "\{ck_size}", ""),
field(pos + 8, 2, "AudioFormat", fmt_str, ""),
field(pos + 10, 2, "Channels", "\{channels}", ""),
field(pos + 12, 4, "SampleRate", "\{sample_rate} Hz", ""),
field(
pos + 16,
4,
"ByteRate",
"\{read_u32_le(bytes, pos + 16)}",
"Bytes/sec",
),
field(
pos + 20,
2,
"BlockAlign",
"\{read_u16_le(bytes, pos + 20)}",
"",
),
field(pos + 22, 2, "BitsPerSample", "\{bps}", ""),
],
),
)
} else if ck_id == "fact" {
let fcount_val = read_u32_le(bytes, pos + 8)
fields.push(
group(pos, 8 + ck_size, "fact", fcount_val.to_string() + " samples", [
field(pos, 4, "ChunkID", "fact", ""),
field(pos + 4, 4, "ChunkSize", ck_size.to_string(), ""),
field(
pos + 8,
4,
"SampleLength",
fcount_val.to_string(),
"Uncompressed samples",
),
]),
)
} else if ck_id == "data" {
fields.push(
field(pos, 8 + ck_size, "data", "\{ck_size} bytes", "Audio samples"),
)
} else {
fields.push(
field(pos, 8 + ck_size, ck_id, "\{ck_size} bytes", "RIFF chunk"),
)
}
pos = pos + 8 + ck_size
if ck_size % 2 != 0 {
pos = pos + 1
}
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
// ============== FLAC ==============
///|
fn parse_flac(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(field(0, 4, "Signature", "fLaC", "FLAC magic"))
let mut pos = 4
let mut last_block = false
for iter = 0; pos + 4 <= len && iter < 50 && !last_block; iter = iter + 1 {
let header = bytes[pos].to_int()
last_block = header >> 7 != 0
let block_type = header & 0x7F
let block_size = read_u24_be(bytes, pos + 1)
if block_size == 0 || pos + 4 + block_size > len {
break
}
let type_str = match block_type {
0 => "STREAMINFO"
1 => "PADDING"
2 => "APPLICATION"
3 => "SEEKTABLE"
4 => "VORBIS_COMMENT"
5 => "CUESHEET"
6 => "PICTURE"
_ => "UNKNOWN"
}
let desc = if block_type == 0 && pos + 4 + 18 <= len {
let _min_block = read_u16_be(bytes, pos + 4)
let _max_block = read_u16_be(bytes, pos + 6)
let sample_rate = (bytes[pos + 10].to_int() << 12) |
(bytes[pos + 11].to_int() << 4) |
(bytes[pos + 12].to_int() >> 4)
let channels = ((bytes[pos + 12].to_int() >> 1) & 7) + 1
let bps = ((bytes[pos + 12].to_int() & 1) << 4) |
((bytes[pos + 13].to_int() >> 4) & 0xF)
let bps = bps + 1
let mut total_samples = bytes[pos + 13].to_int() & 0xF
for j = 14; j < 18; j = j + 1 {
total_samples = total_samples * 256 + bytes[pos + j].to_int()
}
"\{sample_rate}Hz, \{bps}bit, \{channels}ch, \{total_samples} samples"
} else {
"\{block_size} bytes"
}
fields.push(
group(pos, 4 + block_size, type_str, desc, [
field(
pos,
1,
"Header",
"0x\{hex(header, 2)}",
"Last=\{last_block}, Type=\{block_type}",
),
field(pos + 1, 3, "BlockSize", "\{block_size}", ""),
]),
)
pos = pos + 4 + block_size
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
///|
/// Read big-endian uint24 from bytes.
fn read_u24_be(bytes : Bytes, off : Int) -> Int {
guard off + 3 <= bytes.length() else { 0 }
bytes[off].to_int() * 65536 +
bytes[off + 1].to_int() * 256 +
bytes[off + 2].to_int()
}
// ============== OGG ==============
///|
fn parse_ogg(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let mut pos = 0
let mut page = 0
for iter = 0; pos + 27 <= len && iter < 100; iter = iter + 1 {
let sig = read_str(bytes, pos, 4)
if sig != "OggS" {
break
}
page = page + 1
let version = bytes[pos + 4].to_int()
let hdr_type = bytes[pos + 5].to_int()
let granule = read_u64_le(bytes, pos + 6)
let serial = read_u32_le(bytes, pos + 14)
let page_seq = read_u32_le(bytes, pos + 18)
let segments = bytes[pos + 26].to_int()
let mut total_seg_size = 0
for i = 0; i < segments; i = i + 1 {
if pos + 27 + i < len {
total_seg_size = total_seg_size + bytes[pos + 27 + i].to_int()
}
}
let page_size = 27 + segments + total_seg_size
let flags_desc = if hdr_type == 0 {
"Normal"
} else if hdr_type == 2 {
"BOS"
} else if hdr_type == 4 {
"EOS"
} else {
"0x" + hex(hdr_type, 2)
}
let granule_desc = if granule > 0 { "\{granule} samples" } else { "-" }
fields.push(
group(
pos,
page_size,
"Page #\{page}",
"Seq=\{page_seq}, \{segments} seg, \{format_size(total_seg_size)}",
[
field(pos, 4, "Signature", "OggS", ""),
field(pos + 4, 1, "Version", "\{version}", ""),
field(pos + 5, 1, "Flags", flags_desc, ""),
field(pos + 6, 8, "GranulePosition", granule_desc, "Codec samples"),
field(pos + 14, 4, "Serial", "0x\{hex(serial, 8)}", ""),
field(pos + 18, 4, "PageNo", "\{page_seq}", ""),
field(
pos + 26,
1,
"Segments",
"\{segments}",
"\{total_seg_size}B data",
),
],
),
)
pos = pos + page_size
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
// ============== MP4 ==============
///|
fn parse_mp4(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let mut pos = 0
let mut box_count = 0
for iter = 0; pos + 8 <= len && iter < 60; iter = iter + 1 {
let box_size = read_u32_be(bytes, pos)
let box_type = read_str(bytes, pos + 4, 4)
if box_size < 8 || pos + box_size > len {
break
}
box_count = box_count + 1
let desc = if box_type == "ftyp" && box_size >= 12 {
let brand = read_str(bytes, pos + 8, 4)
"Brand: \{brand}"
} else if box_type == "moov" {
"Movie metadata"
} else if box_type == "moof" {
"Movie fragment"
} else if box_type == "mdat" {
"Media data"
} else if box_type == "trak" {
"Track"
} else if box_type == "mdia" {
"Media"
} else {
"\{box_size} bytes"
}
fields.push(field(pos, box_size, box_type, "\{box_size} bytes", desc))
pos = pos + box_size
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
// ============== WebM/MKV ==============
///|
fn parse_webm(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let ebml_id = read_u32_be(bytes, 0)
fields.push(field(0, 4, "EBML", "0x\{hex(ebml_id, 8)}", "EBML header"))
let mut pos = 4
let mut el_count = 0
for iter = 0; pos + 2 <= len && iter < 80; iter = iter + 1 {
let (el_id, id_len) = read_ebml_id(bytes, pos, len)
let (el_size, size_len) = read_ebml_size(bytes, pos + id_len, len)
if el_size == 0 || pos + id_len + size_len + el_size > len {
break
}
el_count = el_count + 1
let el_name = ebml_name(el_id)
let total = id_len + size_len + el_size
fields.push(
group(pos, total, el_name, "\{el_size} bytes", [
field(pos, id_len, "ElementID", "0x\{el_id}", ""),
field(pos + id_len, size_len, "ElementSize", "\{el_size}", ""),
]),
)
pos = pos + total
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}
///|
fn read_ebml_id(bytes : Bytes, off : Int, len : Int) -> (Int, Int) {
guard off < len else { (0, 0) }
let first = bytes[off].to_int()
let mut id_len = 1
if (first & 0x80) != 0 {
id_len = 1
} else if (first & 0x40) != 0 {
id_len = 2
} else if (first & 0x20) != 0 {
id_len = 3
} else {
id_len = 4
}
if off + id_len > len {
return (0, id_len)
}
let mut id = 0
for i = 0; i < id_len; i = i + 1 {
id = (id << 8) | bytes[off + i].to_int()
}
(id, id_len)
}
///|
fn read_ebml_size(bytes : Bytes, off : Int, len : Int) -> (Int, Int) {
guard off < len else { (0, 0) }
let first = bytes[off].to_int()
let mut size_len = 1
let mut mask = 0x80
for i = 0; i < 8; i = i + 1 {
if (first & mask) != 0 {
size_len = i + 1
break
}
mask = mask >> 1
}
if off + size_len > len {
return (0, size_len)
}
// Clear the marker bit
let mut size = first & (mask - 1)
for i = 1; i < size_len; i = i + 1 {
size = (size << 8) | bytes[off + i].to_int()
}
(size, size_len)
}
///|
fn ebml_name(id : Int) -> String {
if id == 0x1A45DFA3 {
"EBML"
} else if id == 0x4286 {
"EBMLVersion"
} else if id == 0x42F7 {
"EBMLReadVersion"
} else if id == 0x42F2 {
"EBMLMaxIDLength"
} else if id == 0x42F3 {
"EBMLMaxSizeLength"
} else if id == 0x4282 {
"DocType"
} else if id == 0x4287 {
"DocTypeVersion"
} else if id == 0x4285 {
"DocTypeReadVersion"
} else if id == 0x18538067 {
"Segment"
} else if id == 0x114D9B74 {
"SeekHead"
} else if id == 0x1549A966 {
"Info"
} else if id == 0x1654AE6B {
"Tracks"
} else if id == 0x1F43B675 {
"Cluster"
} else if id == 0x1254C367 {
"Tags"
} else if id == 0x1C53BB6B {
"Cues"
} else if id == 0xE7 {
"Void"
} else if id == 0xEC {
"CRC32"
} else {
"EBML_0x\{hex(id, 8)}"
}
}
// ============== AVI ==============
///|
fn parse_avi(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
fields.push(field(0, 4, "RIFF", "RIFF", ""))
let file_size = read_u32_le(bytes, 4)
fields.push(field(4, 4, "FileSize", "\{file_size}", ""))
fields.push(field(8, 4, "AVI ", "AVI ", "AVI container"))
// --- Main AVI Header (avih) ---
let avih_off = find_fourcc(
bytes,
12,
"avih",
if len < 512 {
len
} else {
512
},
)
if avih_off > 0 && avih_off + 64 <= len {
let microsec = read_u32_le(bytes, avih_off + 8)
let max_bytes = read_u32_le(bytes, avih_off + 12)
let flags = read_u32_le(bytes, avih_off + 20)
let total_frames = read_u32_le(bytes, avih_off + 24)
let streams = read_u32_le(bytes, avih_off + 32)
let width = read_u32_le(bytes, avih_off + 40)
let height = read_u32_le(bytes, avih_off + 44)
let has_idx = (flags & 0x10) != 0
let scale = read_u32_le(bytes, avih_off + 48)
let rate = read_u32_le(bytes, avih_off + 52)
let length = read_u32_le(bytes, avih_off + 60)
fields.push(
group(
avih_off + 8,
56,
"Main Header (avih)",
"\{width}x\{height}, \{streams} streams, \{total_frames} frames",
[
field(
avih_off + 8,
4,
"MicroSecPerFrame",
"\{microsec}",
"\{if microsec > 0 { "\{1000000 / microsec} fps" } else { "" }}",
),
field(avih_off + 12, 4, "MaxBytesPerSec", format_size(max_bytes), ""),
field(
avih_off + 16,
4,
"Padding",
"\{read_u32_le(bytes, avih_off + 16)}",
"",
),
field(
avih_off + 20,
4,
"Flags",
"0x\{hex(flags, 8)}",
if has_idx {
"HasIndex"
} else {
""
},
),
field(avih_off + 24, 4, "TotalFrames", "\{total_frames}", ""),
field(avih_off + 32, 4, "Streams", "\{streams}", ""),
field(
avih_off + 36,
4,
"BufSize",
"\{read_u32_le(bytes, avih_off + 36)}",
"",
),
field(avih_off + 40, 4, "Width", "\{width}", ""),
field(avih_off + 44, 4, "Height", "\{height}", ""),
field(avih_off + 48, 4, "Scale", "\{scale}", "Time scale"),
field(avih_off + 52, 4, "Rate", "\{rate}", ""),
field(
avih_off + 56,
4,
"Start",
"\{read_u32_le(bytes, avih_off + 56)}",
"",
),
field(avih_off + 60, 4, "Length", "\{length}", "Duration in frames"),
],
),
)
}
// --- Stream Headers (strh + strf) ---
let mut search_pos = if avih_off > 0 { avih_off + 64 } else { 12 }
let mut stream_idx = 0
for iter = 0; iter < 10; iter = iter + 1 {
let strh_off = find_fourcc(bytes, search_pos, "strh", len)
if strh_off < 0 {
break
}
if strh_off + 56 > len {
break
}
let fcc_type = fourcc_at(bytes, strh_off + 8)
let fcc_handler = fourcc_at(bytes, strh_off + 12)
let str_flags = read_u32_le(bytes, strh_off + 16)
let str_scale = read_u32_le(bytes, strh_off + 24)
let str_rate = read_u32_le(bytes, strh_off + 28)
let str_start = read_u32_le(bytes, strh_off + 32)
let str_length = read_u32_le(bytes, strh_off + 36)
let quality = read_u32_le(bytes, strh_off + 44)
let sample_size = read_u32_le(bytes, strh_off + 48)
let type_label = match fcc_type {
"vids" => "Video"
"auds" => "Audio"
"mids" => "MIDI"
"txts" => "Text"
_ => fcc_type
}
let handler_label = match fcc_handler {
"DIB " => "Uncompressed"
"MJPG" => "MJPEG"
"H264" => "H.264"
"MP3 " => "MP3"
"ac3 " => "AC-3"
"PCM " => "PCM"
"ms " => "MS-RLE"
_ => fcc_handler
}
fields.push(
group(
strh_off + 8,
48,
"Stream #\{stream_idx}: \{type_label}",
"\{handler_label}, \{str_length} frames",
[
field(strh_off + 8, 4, "fccType", type_label, ""),
field(strh_off + 12, 4, "fccHandler", handler_label, "Codec"),
field(strh_off + 16, 4, "Flags", "0x\{hex(str_flags, 8)}", ""),
field(
strh_off + 20,
2,
"Priority",
"\{read_u16_le(bytes, strh_off + 20)}",
"",
),
field(strh_off + 24, 4, "Scale", "\{str_scale}", ""),
field(strh_off + 28, 4, "Rate", "\{str_rate}", ""),
field(strh_off + 32, 4, "Start", "\{str_start}", ""),
field(strh_off + 36, 4, "Length", "\{str_length}", "Frames"),
field(strh_off + 44, 4, "Quality", "\{quality}", ""),
field(strh_off + 48, 4, "SampleSize", "\{sample_size}", ""),
],
),
)
// Find strf after this strh (stream format)
let strf_off = find_fourcc(bytes, strh_off + 48, "strf", len)
if strf_off > 0 && strf_off + 8 <= len {
let fmt_sz = read_u32_le(bytes, strf_off + 4)
if fmt_sz >= 16 && strf_off + 8 + fmt_sz <= len {
if fcc_type == "vids" {
let vw = read_u32_le(bytes, strf_off + 12)
let vh = read_u32_le(bytes, strf_off + 16)
let bpp = read_u16_le(bytes, strf_off + 22)
let compr = fourcc_at(bytes, strf_off + 24)
fields.push(
group(
strf_off + 8,
fmt_sz,
"Video Format (strf)",
"\{vw}x\{vh}, \{bpp}bpp, \{compr}",
[
field(strf_off + 12, 4, "Width", "\{vw}", ""),
field(strf_off + 16, 4, "Height", "\{vh}", ""),
field(strf_off + 22, 2, "BitsPerPixel", "\{bpp}", ""),
field(strf_off + 24, 4, "Compression", compr, "FourCC"),
field(
strf_off + 28,
4,
"ImageSize",
"\{read_u32_le(bytes, strf_off + 28)}",
"",
),
],
),
)
} else if fcc_type == "auds" {
let fmt_tag = read_u16_le(bytes, strf_off + 8)
let ch = read_u16_le(bytes, strf_off + 10)
let sr = read_u32_le(bytes, strf_off + 12)
let br = read_u32_le(bytes, strf_off + 16)
let blk = read_u16_le(bytes, strf_off + 20)
let bps = read_u16_le(bytes, strf_off + 22)
let tag_label = if fmt_tag == 1 {
"PCM"
} else if fmt_tag == 0x55 {
"MP3"
} else {
"0x" + hex(fmt_tag, 4)
}
fields.push(
group(
strf_off + 8,
fmt_sz,
"Audio Format (strf)",
"\{tag_label}, \{ch}ch \{sr}Hz \{bps}bit",
[
field(
strf_off + 8,
2,
"FormatTag",
if fmt_tag == 1 {
"PCM"
} else if fmt_tag == 0x55 {
"MP3"
} else {
"0x" + hex(fmt_tag, 4)
},
"",
),
field(strf_off + 10, 2, "Channels", "\{ch}", ""),
field(strf_off + 12, 4, "SampleRate", "\{sr} Hz", ""),
field(strf_off + 16, 4, "ByteRate", "\{br} B/s", ""),
field(strf_off + 20, 2, "BlockAlign", "\{blk}", ""),
field(strf_off + 22, 2, "BitsPerSample", "\{bps}", ""),
],
),
)
}
}
}
search_pos = strh_off + 56
stream_idx = stream_idx + 1
}
// --- Index (idx1) ---
let idx_off = find_fourcc(bytes, search_pos, "idx1", len)
if idx_off > 0 && idx_off + 8 <= len {
let idx_sz = read_u32_le(bytes, idx_off + 4)
let entry_count = idx_sz / 16
if entry_count > 0 {
fields.push(
field(
idx_off,
idx_sz + 8,
"Index (idx1)",
"\{entry_count} entries, \{idx_sz} bytes",
"",
),
)
}
}
fields
}
// ============== MP3 ==============
///|
fn parse_mp3(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields = []
let ver = read_str(bytes, 0, 3)
let major = bytes[3].to_int()
let rev = bytes[4].to_int()
let flags = bytes[5].to_int()
let tag_size = read_u32_syncsafe(bytes, 6)
fields.push(
group(0, 10, "ID3v2 Header", "ID3v2.\{major}.\{rev}, \{tag_size} bytes", [
field(0, 3, "Signature", ver, ""),
field(3, 1, "Version", "2.\{major}.\{rev}", ""),
field(5, 1, "Flags", "0x\{hex(flags, 2)}", ""),
field(6, 4, "Size", "\{tag_size}", "Syncsafe integer"),
]),
)
let total_header = 10 + tag_size
if total_header < len {
fields.push(
field(10, tag_size, "ID3v2 Frames", "\{tag_size} bytes", "ID3 tag data"),
)
// Try to detect first MPEG frame after ID3 tag
let data_start = total_header
if data_start + 2 <= len {
let sync = read_u16_be(bytes, data_start)
if sync >> 5 == 0x7FF {
let mpeg_ver = (sync >> 3) & 3
let layer = (sync >> 1) & 3
let _bitrate_idx = bytes[data_start + 2].to_int() >> 4
let sample_rate_idx = (bytes[data_start + 2].to_int() >> 2) & 3
let mpeg_ver_str = match mpeg_ver {
3 => "MPEG1"
2 => "MPEG2"
_ => "?"
}
let layer_str = match layer {
3 => "LayerI"
2 => "LayerII"
1 => "LayerIII"
_ => "?"
}
let sr = match (mpeg_ver, sample_rate_idx) {
(3, 0) => "44100"
(3, 1) => "48000"
(3, 2) => "32000"
(2, 0) => "22050"
(2, 1) => "24000"
(2, 2) => "16000"
_ => "?"
}
fields.push(
group(
data_start,
len - data_start,
"MPEG Frames",
"\{mpeg_ver_str} \{layer_str}, \{sr}Hz",
[
field(data_start, 2, "Sync", "0x\{hex(sync, 4)}", "Frame sync"),
field(
data_start + 2,
1,
"Bitrate/SR",
"0x\{hex(bytes[data_start + 2].to_int(), 2)}",
"",
),
],
),
)
} else {
fields.push(
field(
data_start,
len - data_start,
"AudioData",
"\{len - data_start} bytes",
"MPEG audio",
),
)
}
}
}
fields
}
///|
/// Read syncsafe integer (ID3v2 uses this — each byte has MSB=0).
fn read_u32_syncsafe(bytes : Bytes, off : Int) -> Int {
guard off + 4 <= bytes.length() else { 0 }
((bytes[off].to_int() & 0x7F) << 21) |
((bytes[off + 1].to_int() & 0x7F) << 14) |
((bytes[off + 2].to_int() & 0x7F) << 7) |
(bytes[off + 3].to_int() & 0x7F)
}
// ============== ELF ==============
///|
fn parse_elf(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
guard len >= 52 else {
return [field(0, len, "TRUNCATED", "", "Too small for ELF")]
}
let fields = []
let is64 = bytes[4].to_int() == 2
let isle = bytes[5].to_int() == 1
let ei_ver = bytes[6].to_int()
let ei_osabi = bytes[7].to_int()
let ei_abiver = bytes[8].to_int()
let osabi_s = match ei_osabi {
0 => "UNIX"
2 => "NetBSD"
3 => "Linux"
6 => "Solaris"
9 => "FreeBSD"
_ => "?"
}
fields.push(
group(
0,
16,
"ELF Ident",
"\{if is64 { "64-bit" } else { "32-bit" }} \{if isle { "LE" } else { "BE" }}, \{osabi_s}",
[
field(0, 4, "Magic", "7F 45 4C 46", ""),
field(4, 1, "ei_class", if is64 { "ELF64" } else { "ELF32" }, ""),
field(
5,
1,
"ei_data",
if isle {
"LittleEndian"
} else {
"BigEndian"
},
"",
),
field(6, 1, "ei_version", "\{ei_ver}", ""),
field(7, 1, "ei_osabi", osabi_s, ""),
field(8, 1, "ei_abiversion", "\{ei_abiver}", ""),
],
),
)
let r16 = if isle {
fn(b, o) { read_u16_le(b, o) }
} else {
fn(b, o) { read_u16_be(b, o) }
}
let r32 = if isle {
fn(b, o) { read_u32_le(b, o) }
} else {
fn(b, o) { read_u32_be(b, o) }
}
let r64 = if isle {
fn(b, o) { read_u64_le(b, o) }
} else {
fn(b, o) { read_u64_be(b, o) }
}
let e_type = r16(bytes, 16)
let e_machine = r16(bytes, 18)
let e_entry = if is64 { r64(bytes, 24) } else { r32(bytes, 24) }
let e_flags = r32(bytes, if is64 { 48 } else { 36 })
let e_ehsize = r16(bytes, if is64 { 52 } else { 40 })
let e_phentsize = r16(bytes, if is64 { 54 } else { 42 })
let e_phnum = r16(bytes, if is64 { 56 } else { 44 })
let e_shentsize = r16(bytes, if is64 { 58 } else { 46 })
let e_shnum = r16(bytes, if is64 { 60 } else { 48 })
let e_shstrndx = r16(bytes, if is64 { 62 } else { 50 })
let e_phoff = if is64 { r64(bytes, 32) } else { r32(bytes, 28) }
let e_shoff = if is64 { r64(bytes, 40) } else { r32(bytes, 32) }
let tname = match e_type {
1 => "REL"
2 => "EXEC"
3 => "DYN"
_ => "?"
}
let mname = match e_machine {
3 => "i386"
62 => "AMD64"
40 => "ARM"
183 => "ARM64"
_ => "?"
}
let hdr_sz = if is64 { 64 } else { 52 }
fields.push(
group(16, hdr_sz - 16, "ELF Header", "Type:\{tname} Machine:\{mname}", [
field(16, 2, "e_type", tname, ""),
field(18, 2, "e_machine", mname, ""),
field(
24,
if is64 {
8
} else {
4
},
"e_entry",
"0x\{hex(e_entry, 8)}",
"Entry point",
),
field(
if is64 {
48
} else {
36
},
4,
"e_flags",
"0x\{hex(e_flags, 8)}",
"",
),
field(
if is64 {
32
} else {
28
},
if is64 {
8
} else {
4
},
"e_phoff",
"0x\{hex(e_phoff, 8)}",
"Program header offset",
),
field(
if is64 {
40
} else {
32
},
if is64 {
8
} else {
4
},
"e_shoff",
"0x\{hex(e_shoff, 8)}",
"Section header offset",
),
field(if is64 { 52 } else { 40 }, 2, "e_ehsize", "\{e_ehsize}", ""),
field(if is64 { 54 } else { 42 }, 2, "e_phentsize", "\{e_phentsize}", ""),
field(
if is64 {
56
} else {
44
},
2,
"e_phnum",
"\{e_phnum}",
"Program headers",
),
field(if is64 { 58 } else { 46 }, 2, "e_shentsize", "\{e_shentsize}", ""),
field(
if is64 {
60
} else {
48
},
2,
"e_shnum",
"\{e_shnum}",
"Section headers",
),
field(
if is64 {
62
} else {
50
},
2,
"e_shstrndx",
"\{e_shstrndx}",
"String table index",
),
]),
)
// --- Program Headers ---
if e_phnum > 0 && e_phoff > 0 && e_phoff + e_phnum * e_phentsize <= len {
let phdr_entries : Array[StructField] = []
for i = 0; i < e_phnum; i = i + 1 {
let po = e_phoff + i * e_phentsize
let p_type = r32(bytes, po)
let pn = match p_type {
0 => "PT_NULL"
1 => "PT_LOAD"
2 => "PT_DYNAMIC"
3 => "PT_INTERP"
4 => "PT_NOTE"
5 => "PT_SHLIB"
6 => "PT_PHDR"
7 => "PT_TLS"
_ => "?"
}
let pf = if is64 { r32(bytes, po + 4) } else { r32(bytes, po + 24) }
let r_ = if pf % 8 >= 4 { "R" } else { "" }
let w_ = if pf / 2 % 2 == 1 { "W" } else { "" }
let x_ = if pf % 2 == 1 { "E" } else { "" }
let flags_str = r_ + w_ + x_
let p_offset = if is64 { r64(bytes, po + 8) } else { r32(bytes, po + 4) }
let p_vaddr = if is64 { r64(bytes, po + 16) } else { r32(bytes, po + 8) }
let p_filesz = if is64 {
r64(bytes, po + 32)
} else {
r32(bytes, po + 16)
}
let p_memsz = if is64 { r64(bytes, po + 40) } else { r32(bytes, po + 20) }
phdr_entries.push(
group(
po,
e_phentsize,
"\{pn}",
"\{format_size(p_filesz)}, flags:\{flags_str}",
[
field(po, 4, "p_type", pn, ""),
field(
if is64 {
po + 4
} else {
po + 24
},
4,
"p_flags",
flags_str,
"R=4 W=2 E=1",
),
field(
if is64 {
po + 8
} else {
po + 4
},
if is64 {
8
} else {
4
},
"p_offset",
"0x\{hex(p_offset, 8)}",
"",
),
field(
if is64 {
po + 16
} else {
po + 8
},
if is64 {
8
} else {
4
},
"p_vaddr",
"0x\{hex(p_vaddr, 8)}",
"Virtual address",
),
field(
if is64 {
po + 32
} else {
po + 16
},
if is64 {
8
} else {
4
},
"p_filesz",
format_size(p_filesz),
"Size in file",
),
field(
if is64 {
po + 40
} else {
po + 20
},
if is64 {
8
} else {
4
},
"p_memsz",
format_size(p_memsz),
"Size in memory",
),
],
),
)
}
fields.push(
group(
e_phoff,
e_phnum * e_phentsize,
"Program Headers",
"\{e_phnum} entries",
phdr_entries,
),
)
}
// --- Section Headers ---
// Pre-read section name string table
let shstr = if e_shstrndx > 0 && e_shstrndx < e_shnum && e_shentsize >= 40 {
let strtab_shoff = e_shoff + e_shstrndx * e_shentsize
let strtab_sh_offset = if is64 {
r64(bytes, strtab_shoff + 24)
} else {
r32(bytes, strtab_shoff + 16)
}
let strtab_sh_size = if is64 {
r64(bytes, strtab_shoff + 32)
} else {
r32(bytes, strtab_shoff + 20)
}
(strtab_sh_offset, strtab_sh_size)
} else {
(-1, 0)
}
fn read_shstr(off : Int, sz : Int, sh_name : Int, bytes : Bytes) -> String {
if off < 0 || sh_name < 0 || sh_name >= sz {
return "?"
}
let mut end = sh_name
while end < sz && bytes[off + end].to_int() != 0 {
end = end + 1
}
let sb = StringBuilder()
for j = sh_name; j < end; j = j + 1 {
match bytes[off + j].to_int().to_char() {
Some(c) => sb.write_char(c)
None => ()
}
}
sb.to_string()
}
if e_shnum > 0 && e_shentsize >= 40 && e_shoff + e_shnum * e_shentsize <= len {
let shdr_entries : Array[StructField] = []
for i = 0; i < e_shnum; i = i + 1 {
let so = e_shoff + i * e_shentsize
let sh_name = r32(bytes, so)
let sh_type = r32(bytes, so + 4)
let sh_flags = if is64 { r64(bytes, so + 8) } else { r32(bytes, so + 8) }
let sh_addr = if is64 { r64(bytes, so + 16) } else { r32(bytes, so + 12) }
let sh_offset = if is64 {
r64(bytes, so + 24)
} else {
r32(bytes, so + 16)
}
let sh_size = if is64 { r64(bytes, so + 32) } else { r32(bytes, so + 20) }
let name_str = read_shstr(shstr.0, shstr.1, sh_name, bytes)
let tn = match sh_type {
0 => "SHT_NULL"
1 => "SHT_PROGBITS"
2 => "SHT_SYMTAB"
3 => "SHT_STRTAB"
4 => "SHT_RELA"
5 => "SHT_HASH"
6 => "SHT_DYNAMIC"
7 => "SHT_NOTE"
8 => "SHT_NOBITS"
9 => "SHT_REL"
11 => "SHT_DYNSYM"
_ => "?"
}
let fw = if sh_flags % 2 == 1 { "W" } else { "" }
let fa = if sh_flags / 2 % 2 == 1 { "A" } else { "" }
let fx = if sh_flags % 8 >= 4 { "X" } else { "" }
let fl = fw + fa + fx
let desc = "\{format_size(sh_size)}\{if fl != "" { " " + fl } else { "" }}"
shdr_entries.push(
group(so, e_shentsize, "\{name_str}", desc, [
field(so, 4, "sh_name", name_str, ""),
field(so + 4, 4, "sh_type", tn, ""),
field(so + 8, if is64 { 8 } else { 4 }, "sh_flags", fl, "W=1 A=2 X=4"),
field(
if is64 {
so + 16
} else {
so + 12
},
if is64 {
8
} else {
4
},
"sh_addr",
"0x\{hex(sh_addr, 8)}",
"",
),
field(
if is64 {
so + 24
} else {
so + 16
},
if is64 {
8
} else {
4
},
"sh_offset",
"0x\{hex(sh_offset, 8)}",
"File offset",
),
field(
if is64 {
so + 32
} else {
so + 20
},
if is64 {
8
} else {
4
},
"sh_size",
format_size(sh_size),
"",
),
]),
)
}
fields.push(
group(
e_shoff,
e_shnum * e_shentsize,
"Section Headers",
"\{e_shnum} sections",
shdr_entries,
),
)
}
fields
}
///|
/// Display parsed structure as readable text.
pub fn format_structure(fields : Array[StructField]) -> String {
format_fields(fields, 0)
}
///|
fn format_fields(fields : Array[StructField], indent : Int) -> String {
let sb = StringBuilder()
let pfx = StringBuilder()
for _i = 0; _i < indent; _i = _i + 1 {
pfx.write_string(" ")
}
let pad = pfx.to_string()
for i = 0; i < fields.length(); i = i + 1 {
let f = fields[i]
if f.children.length() > 0 {
sb.write_string("\{pad}0x\{hex(f.offset, 8)} \{f.name}\n")
if f.description != "" {
sb.write_string("\{pad} \{f.description}\n")
}
sb.write_string(format_fields(f.children, indent))
} else {
sb.write_string("\{pad}0x\{hex(f.offset, 8)} \{f.name} = \{f.value}")
if f.description != "" {
sb.write_string(" -- \{f.description}")
}
sb.write_char('\n')
}
}
sb.to_string()
}
///|
/// Escape a string for JSON output.
fn json_escape(s : String) -> String {
let sb = StringBuilder()
for i = 0; i < s.length(); i = i + 1 {
let ch = s.get_char(i).unwrap()
if ch == '"' {
sb.write_string("\\\"")
} else if ch == '\\' {
sb.write_string("\\\\")
} else if ch == '\n' {
sb.write_string("\\n")
} else if ch == '\r' {
sb.write_string("\\r")
} else if ch == '\t' {
sb.write_string("\\t")
} else {
sb.write_char(ch)
}
}
sb.to_string()
}
///|
/// Format StructField array as JSON string.
pub fn format_structure_json(fields : Array[StructField]) -> String {
let sb = StringBuilder()
sb.write_string("[\n")
format_fields_json(fields, sb, 1)
sb.write_string("]\n")
sb.to_string()
}
///|
fn format_fields_json(
fields : Array[StructField],
sb : StringBuilder,
depth : Int,
) -> Unit {
let pad = " "
for i = 0; i < fields.length(); i = i + 1 {
let f = fields[i]
for _d = 0; _d < depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("{\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"offset\": \"0x\{to_hex_string(f.offset, width=8)}\",\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"size\": \{f.size},\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"name\": \"\{json_escape(f.name)}\",\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"value\": \"\{json_escape(f.value)}\",\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"description\": \"\{json_escape(f.description)}\"")
if f.children.length() > 0 {
sb.write_string(",\n")
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("\"children\": [\n")
format_fields_json(f.children, sb, depth + 2)
for _d = 0; _d <= depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("]\n")
} else {
sb.write_string("\n")
}
for _d = 0; _d < depth; _d = _d + 1 {
sb.write_string(pad)
}
sb.write_string("}")
if i + 1 < fields.length() {
sb.write_string(",")
}
sb.write_string("\n")
}
}
// ========== Additional Format Parsers ==========
///|
fn parse_gzip(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields : Array[StructField] = []
fields.push(field(0, 2, "GZip Magic", "1F 8B", "GZip signature"))
let cm = bytes[2].to_int()
fields.push(
field(2, 1, "Method", "\{cm}", if cm == 8 { "Deflate" } else { "Unknown" }),
)
let flags = bytes[3].to_int()
let mut flag_str = ""
if (flags & 0x01) != 0 {
flag_str = flag_str + "TEXT "
}
if (flags & 0x04) != 0 {
flag_str = flag_str + "EXTRA "
}
if (flags & 0x08) != 0 {
flag_str = flag_str + "NAME "
}
if (flags & 0x10) != 0 {
flag_str = flag_str + "COMMENT "
}
if flag_str == "" {
flag_str = "None"
}
fields.push(field(3, 1, "Flags", "0x\{hex(flags, 2)}", flag_str))
let mtime = read_u32_le(bytes, 4)
fields.push(field(4, 4, "Modification Time", "\{mtime}", "Unix timestamp"))
let os = bytes[9].to_int()
let os_name = match os {
0 => "FAT"
3 => "Unix"
7 => "Macintosh"
11 => "NTFS"
255 => "Unknown"
_ => "OS \{os}"
}
fields.push(field(9, 1, "OS", "\{os}", os_name))
if (flags & 0x08) != 0 && len > 10 {
let name = read_str(bytes, 10, 256)
fields.push(
field(10, name.length(), "Original Name", name, "Original filename"),
)
}
if len >= 8 {
let orig_size = read_u32_le(bytes, len - 4)
fields.push(
field(
len - 4,
4,
"Original Size",
"\{orig_size} bytes",
format_size(orig_size),
),
)
}
fields.push(
field(0, len, "Compressed Size", "\{len} bytes", format_size(len)),
)
fields
}
///|
fn parse_7z(bytes : Bytes) -> Array[StructField] {
let fields : Array[StructField] = []
fields.push(field(0, 6, "7z Signature", "37 7A BC AF 27 1C", "7-Zip archive"))
let major = bytes[6].to_int()
let minor = bytes[7].to_int()
fields.push(field(6, 2, "Version", "\{major}.\{minor}", "Archive version"))
let header_crc = read_u32_le(bytes, 8)
fields.push(
field(8, 4, "Header CRC", "0x\{hex(header_crc, 8)}", "Start header CRC32"),
)
let next_offset = read_u32_le(bytes, 12)
fields.push(
field(
12,
8,
"Next Header Offset",
"0x\{hex(next_offset, 8)}",
"Offset to main header",
),
)
fields.push(
field(
0,
bytes.length(),
"File Size",
"\{bytes.length()} bytes",
format_size(bytes.length()),
),
)
fields
}
///|
fn parse_bzip2(bytes : Bytes) -> Array[StructField] {
let len = bytes.length()
let fields : Array[StructField] = []
fields.push(field(0, 2, "BZip2 Magic", "BZ", "BZip2 signature"))
let ver = bytes[2].to_int()
fields.push(
field(
2,
1,
"Version",
"h",
if ver == 0x68 {
"Huffman coding"
} else {
"Unknown"
},
),
)
let block = bytes[3].to_int() - 0x30
fields.push(field(3, 1, "Block Size", "\{block * 100} KB", "100K x \{block}"))
// Scan for compressed blocks (magic 0x314159265359) and EOS (0x177245385090)
let block_magic = b"\x31\x41\x59\x26\x53\x59"
let eos_magic = b"\x17\x72\x45\x38\x50\x90"
let results = find_all_bytes(bytes, block_magic)
let eos_results = find_all_bytes(bytes, eos_magic)
let mut pos = 4
let mut blk_num = 0
for i = 0; i < results.length(); i = i + 1 {
let blk_off = results[i]
if blk_off >= pos && blk_off + 10 <= len {
// Gap between blocks (or from header to first block)
if blk_off > pos {
fields.push(
field(pos, blk_off - pos, "Data", "\{blk_off - pos} bytes", ""),
)
}
let crc = read_u32_be(bytes, blk_off + 6)
let mut blk_end = len
if i + 1 < results.length() {
blk_end = results[i + 1]
} else if eos_results.length() > 0 {
blk_end = eos_results[0]
}
fields.push(
group(
blk_off,
blk_end - blk_off,
"Block #\{blk_num}",
"CRC: 0x\{hex(crc, 8)}, \{format_size(blk_end - blk_off - 10)} compressed",
[
field(blk_off, 6, "BlockMagic", "31 41 59 26 53 59", ""),
field(blk_off + 6, 4, "BlockCRC", "0x\{hex(crc, 8)}", ""),
field(
blk_off + 10,
blk_end - blk_off - 10,
"CompressedData",
"\{format_size(blk_end - blk_off - 10)}",
"",
),
],
),
)
blk_num = blk_num + 1
pos = blk_end
}
}
// EOS marker
for i = 0; i < eos_results.length(); i = i + 1 {
if eos_results[i] >= pos && eos_results[i] + 10 <= len {
if eos_results[i] > pos {
fields.push(
field(
pos,
eos_results[i] - pos,
"Padding",
"\{eos_results[i] - pos} bytes",
"",
),
)
}
let eos_crc = read_u32_be(bytes, eos_results[i] + 6)
fields.push(
group(eos_results[i], 10, "EOS Marker", "CRC: 0x\{hex(eos_crc, 8)}", [
field(
eos_results[i],
6,
"EOSMagic",
"17 72 45 38 50 90",
"End of stream",
),
field(
eos_results[i] + 6,
4,
"EOS CRC",
"0x\{hex(eos_crc, 8)}",
"Combined CRC",
),
]),
)
pos = eos_results[i] + 10
}
}
if pos < len {
fields.push(field(pos, len - pos, "Trailing", "\{len - pos} bytes", ""))
}
fields
}