code wiki / _hdl_build / nx_mobi_cover.nx

nx_mobi_cover.nx source

↩ module page · 52 lines · 3821 B

1// nx_mobi_cover.nx -- SHARED embedded-cover extraction for the Kindle decoders (DRY: nx_mobi_book + nx_kf8_book both 2// call it). The cover is read out of the file ITSELF, offline -- never generated, never downloaded (how Calibre does 3// it). Grounded on the registered MOBI spec (mobileread-mobi-spec.ref): the MOBI header's "First Image index" lives 4// at rec0off+108 (record-0-relative); EXTH 201 coveroffset / 202 thumboffset are ADDED to it to find the cover PDB 5// record. Image records are absolute in the PDB, so the SAME record-0 First Image index serves MOBI6 and KF8 (a dual 6// file shares its image records). Detects JPEG/PNG/GIF magic and writes <dir>/cover.<ext>; sets covername (or ""). 7// Returns 1 if a cover was written, else 0. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 10const K_MAGIC_1024: i64 = 1024 11 12func mc_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 13func mc_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+i } 14func mc_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) } 15func mc_p(s: *u8) -> i64 { sys_write(1, s, mc_slen(s)); return 0 } 16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 20func mc_pn(v: i64) -> i64 { nxi_out(v); return 0 } 21 22func mobi_extract_cover(z: *u8, zl: i64, numRec: i64, rec0off: i64, coveroff: i64, coverfound: i64, thumboff: i64, thumbfound: i64, dir: *u8, covername: *u8) -> i64 { 23 covername[0] = 0 as u8 24 let mobihdrlen: i64 = mc_be32(z, rec0off + 20) 25 if mobihdrlen < 96 { return 0 } // no First Image index field in a minimal header 26 let firstimg: i64 = mc_be32(z, rec0off + 108) 27 if firstimg < 1 { return 0 } 28 if firstimg >= numRec { return 0 } 29 var coverrec: i64 = firstimg 30 if coverfound == 1 { coverrec = firstimg + coveroff } else { if thumbfound == 1 { coverrec = firstimg + thumboff } } 31 if coverrec < 1 { return 0 } 32 if coverrec >= numRec { return 0 } 33 let cs2: i64 = mc_be32(z, 78 + coverrec*8) 34 var ce2: i64 = zl 35 if coverrec + 1 < numRec { ce2 = mc_be32(z, 78 + (coverrec+1)*8) } 36 let clen: i64 = ce2 - cs2 37 if clen < 4 { return 0 } 38 if cs2 + clen > zl { return 0 } 39 var ext: *u8 = 0 as *u8 40 let b0: i64 = z[cs2] as i64; let b1: i64 = z[cs2+1] as i64; let b2: i64 = z[cs2+2] as i64; let b3: i64 = z[cs2+3] as i64 41 if b0==0xff { if b1==0xd8 { if b2==0xff { ext = "jpg" as *u8 } } } // JPEG FF D8 FF 42 if b0==0x89 { if b1==0x50 { if b2==0x4e { if b3==0x47 { ext = "png" as *u8 } } } } // PNG 89 'PNG' 43 if b0==0x47 { if b1==0x49 { if b2==0x46 { ext = "gif" as *u8 } } } // GIF 'GIF' 44 if (ext as i64) == 0 { mc_p("MOBI-COVER-SKIP first-image record is not a known image (jpg/png/gif)\n" as *u8); return 0 } 45 let cvp: *u8 = sys_mmap(K_MAGIC_1024); var cvl: i64 = mc_cat(cvp, 0, dir); cvl = mc_cat(cvp, cvl, "/cover." as *u8); cvl = mc_cat(cvp, cvl, ext); cvp[cvl] = 0 as u8 46 let cvfd: i64 = sys_openat_wr(cvp, 0x1a4) 47 if cvfd < 0 { return 0 } 48 sys_write(cvfd, (z as i64 + cs2) as *u8, clen); sys_close(cvfd) 49 var nn: i64 = mc_cat(covername, 0, "cover." as *u8); nn = mc_cat(covername, nn, ext); covername[nn] = 0 as u8 50 mc_p("MOBI-COVER rec=" as *u8); mc_pn(coverrec); mc_p(" bytes=" as *u8); mc_pn(clen); mc_p(" fmt=" as *u8); mc_p(ext); mc_p(" -> " as *u8); mc_p(cvp); mc_p("\n" as *u8) 51 return 1 52}