code wiki / _hdl_build / nx_mobi_inspect.nx
nx_mobi_inspect.nx source
↩ module page · 104 lines · 6742 B
1// nx_mobi_inspect.nx -- diagnostic: dump a MOBI's PDB/PalmDOC/MOBI-header structure + LOCATE the HUFF/CDIC records
2// by scanning every PDB record for the "HUFF"/"CDIC" magics, so we can see why the Huffman Record Offset field read
3// disagrees with reality on a real kindlegen file. Usage: nx_mobi_inspect <mobi-path>. license_tier: ORIGINAL
4import "nx_syscalls.nx"
5
6func ip(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
7func inum(v0: i64) -> i64 { var v: i64=v0; if v<0 { sys_write(1,"-" as *u8,1); v=0-v } let b: *u8=sys_mmap(24); var k: i64=0; if v==0 {b[0]=48 as u8;k=1} while v>0 {b[k]=(48+(v%10)) as u8; v=v/10; k=k+1} let o: *u8=sys_mmap(24); var j: i64=0; while j<k {o[j]=b[k-1-j];j=j+1} sys_write(1,o,k); return 0 }
8func be16(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<8)|(b[o+1] as i64) }
9func be32(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<24)|((b[o+1] as i64)<<16)|((b[o+2] as i64)<<8)|(b[o+3] as i64) }
10func tag4(z: *u8, o: i64) -> i64 { sys_write(1, (z as i64 + o) as *u8, 4); return 0 }
11func islen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
12func find_sub(hay: *u8, hl: i64, needle: *u8) -> i64 {
13 let nl: i64 = islen(needle); if nl==0 { return 0-1 }
14 var i: i64 = 0
15 while i + nl <= hl { var k: i64=0; var hit: i64=1; while k<nl { if hay[i+k]!=needle[k]{hit=0;k=nl}else{k=k+1} } if hit==1 {return i} i=i+1 }
16 return 0-1
17}
18
19func main(argc: i64, argv: *i64) -> i64 {
20 var path: *u8 = "knowledge/benchmarks/refs/libmobi-sample-huffdic.ref" as *u8
21 if argc >= 2 { path = argv[1] as *u8 }
22 let lp: *i64 = sys_mmap(8) as *i64; lp[0]=0
23 let z: *u8 = sys_read_file(path, lp)
24 if (z as i64)==0 { ip("INSPECT-FAIL read\n" as *u8); sys_exit(1); return 1 }
25 let zl: i64 = lp[0]
26 let numRec: i64 = be16(z, 76)
27 let rec0off: i64 = be32(z, 78)
28 ip("file=" as *u8); ip(path); ip(" bytes=" as *u8); inum(zl); ip(" numRec=" as *u8); inum(numRec); ip(" rec0off=" as *u8); inum(rec0off); ip("\n" as *u8)
29 ip("PDB name=\"" as *u8); var nn: i64=0; while nn<32 { if z[nn]==(0 as u8) { nn=32 } else { sys_write(1,(z as i64+nn) as *u8,1); nn=nn+1 } } ip("\" type/creator=" as *u8); tag4(z,60); tag4(z,64); ip("\n" as *u8)
30 let comp: i64 = be16(z, rec0off+0)
31 let textLen: i64 = be32(z, rec0off+4)
32 let recCount: i64 = be16(z, rec0off+8)
33 let enc: i64 = be16(z, rec0off+12)
34 ip("PalmDOC: comp=" as *u8); inum(comp); ip(" textLen=" as *u8); inum(textLen); ip(" textRecCount=" as *u8); inum(recCount); ip(" enc=" as *u8); inum(enc); ip("\n" as *u8)
35 ip("MOBI hdr @" as *u8); inum(rec0off+16); ip(" magic=" as *u8); tag4(z, rec0off+16); ip(" hdrLen=" as *u8); inum(be32(z, rec0off+20)); ip("\n" as *u8)
36 ip(" Huffman Record Offset(0x70)=" as *u8); inum(be32(z, rec0off+16+112)); ip(" Count(0x74)=" as *u8); inum(be32(z, rec0off+16+116)); ip("\n" as *u8)
37
38 // dump MOBI-header dwords to LOCATE the real Huffman Record Offset field (looking for the value 32)
39 ip("MOBI hdr dwords (offset=value):\n" as *u8)
40 var d: i64 = 96
41 while d <= 160 {
42 ip(" +" as *u8); inum(d); ip("=" as *u8); inum(be32(z, rec0off+16+d)); ip("\n" as *u8)
43 d = d + 4
44 }
45
46 // scan EVERY record for the HUFF/CDIC magics -> the ground truth
47 ip("scan: records with HUFF/CDIC magic:\n" as *u8)
48 var i: i64 = 0
49 while i < numRec {
50 let ro: i64 = be32(z, 78 + i*8)
51 if ro + 4 <= zl {
52 let isH: i64 = 1; if z[ro]!=(0x48 as u8){ }
53 var hh: i64 = 0
54 if z[ro]==(0x48 as u8) { if z[ro+1]==(0x55 as u8) { if z[ro+2]==(0x46 as u8) { if z[ro+3]==(0x46 as u8) { hh=1 } } } }
55 var cc: i64 = 0
56 if z[ro]==(0x43 as u8) { if z[ro+1]==(0x44 as u8) { if z[ro+2]==(0x49 as u8) { if z[ro+3]==(0x43 as u8) { cc=1 } } } }
57 if hh==1 { ip(" rec[" as *u8); inum(i); ip("] @" as *u8); inum(ro); ip(" = HUFF hdrsize=" as *u8); inum(be32(z, ro+4)); ip("\n" as *u8) }
58 if cc==1 { ip(" rec[" as *u8); inum(i); ip("] @" as *u8); inum(ro); ip(" = CDIC\n" as *u8) }
59 }
60 i = i + 1
61 }
62 // EXTH records (looking for type 121 = KF8 Boundary -> the PDB record where the KF8 part begins)
63 let rec1off: i64 = be32(z, 78 + 8)
64 var r0ext: i64 = rec1off - rec0off
65 if r0ext <= 0 { r0ext = zl - rec0off }
66 let exrel: i64 = find_sub((z as i64 + rec0off) as *u8, r0ext, "EXTH" as *u8)
67 if exrel >= 0 {
68 let exth: i64 = rec0off + exrel
69 let cnt: i64 = be32(z, exth + 8)
70 ip("EXTH @" as *u8); inum(exth); ip(" records=" as *u8); inum(cnt); ip(":\n" as *u8)
71 var p: i64 = exth + 12; var ci: i64 = 0
72 while ci < cnt {
73 if p + 8 > zl { ci = cnt } else {
74 let rtype: i64 = be32(z, p); let rlen: i64 = be32(z, p+4)
75 if rlen < 8 { ci = cnt } else {
76 if rtype == 121 { ip(" *** EXTH 121 (KF8 boundary) value=" as *u8); inum(be32(z, p+8)); ip("\n" as *u8) }
77 if rtype == 116 { ip(" EXTH 116 (startReading) value=" as *u8); inum(be32(z, p+8)); ip("\n" as *u8) }
78 p = p + rlen; ci = ci + 1
79 }
80 }
81 }
82 }
83 // if a KF8 boundary record was identified, dump its PalmDOC header
84 ip("mobiType@(rec0+16+8)=" as *u8); inum(be32(z, rec0off+16+8)); ip(" (8 => KF8)\n" as *u8)
85 // dump records around the KF8 boundary (looking for "BOUNDARY" marker + the KF8 MOBI header)
86 ip("records 44..51 (idx @off first8 | +16tag):\n" as *u8)
87 var rr: i64 = 44
88 while rr <= 51 {
89 if rr < numRec { let ro: i64 = be32(z, 78 + rr*8)
90 ip(" [" as *u8); inum(rr); ip("] @" as *u8); inum(ro); ip(" \"" as *u8)
91 var b: i64=0; while b<8 { let cb: i64 = z[ro+b] as i64; if cb>=32 { if cb<127 { sys_write(1,(z as i64+ro+b) as *u8,1) } else { ip("." as *u8) } } else { ip("." as *u8) } b=b+1 }
92 ip("\" +16=" as *u8); tag4(z, ro+16); ip("\n" as *u8) }
93 rr = rr + 1
94 }
95 // dump the KF8 header (boundary record 46) PalmDOC + MOBI-hdr dwords (looking for the HUFF record index 86)
96 let kf: i64 = be32(z, 78 + 46*8)
97 ip("KF8 hdr @rec46 off=" as *u8); inum(kf); ip(" comp=" as *u8); inum(be16(z, kf)); ip(" recCount=" as *u8); inum(be16(z, kf+8)); ip(" MOBImagic=" as *u8); tag4(z, kf+16); ip(" hdrLen=" as *u8); inum(be32(z, kf+20)); ip("\n" as *u8)
98 ip("KF8 MOBI-hdr dwords:\n" as *u8)
99 var kd: i64 = 88
100 while kd <= 124 { ip(" +" as *u8); inum(kd); ip("=" as *u8); inum(be32(z, kf+16+kd)); ip("\n" as *u8); kd = kd + 4 }
101 ip(" also rec0-relative +112=" as *u8); inum(be32(z, kf+112)); ip(" +116=" as *u8); inum(be32(z, kf+116)); ip("\n" as *u8)
102 ip("INSPECT-OK\n" as *u8)
103 sys_exit(0); return 0
104}