Improve error handling and syntax

png-branch
an 2018-09-11 15:07:42 -04:00
parent 37e1c765d1
commit 6420e4a6e3
6 changed files with 163 additions and 120 deletions

View File

@ -2,18 +2,56 @@
pub type Ident = [u8; 4];
pub fn b_iden(b: &[u8]) -> Ident {[b[0], b[1], b[2], b[3]]}
pub type ResultS<T> = Result<T, &'static str>;
pub fn b_u32l(b: &[u8]) -> u32 {b[3] as (u32) << 24 | b[2] as (u32) << 16 |
b[1] as (u32) << 8 | b[0] as u32}
pub fn b_u16l(b: &[u8]) -> u16 {b[1] as (u16) << 8 | b[0] as u16}
pub fn b_u32b(b: &[u8]) -> u32 {b[0] as (u32) << 24 | b[1] as (u32) << 16 |
b[2] as (u32) << 8 | b[3] as u32}
pub fn b_u16b(b: &[u8]) -> u16 {b[0] as (u16) << 8 | b[1] as u16}
pub fn b_i32l(b: &[u8]) -> i32 {b_u32l(b) as i32}
pub fn b_i16l(b: &[u8]) -> i16 {b_u16l(b) as i16}
pub fn b_i32b(b: &[u8]) -> i32 {b_u32b(b) as i32}
pub fn b_i16b(b: &[u8]) -> i16 {b_u16b(b) as i16}
pub trait BinUtil
{
// Checked
fn c_iden(&self, i: usize) -> ResultS<Ident>;
fn c_u32b(&self, i: usize) -> ResultS<u32>;
fn c_u16b(&self, i: usize) -> ResultS<u16>;
fn c_i32b(&self, i: usize) -> ResultS<i32>
{match self.c_u32b(i) {Ok(n) => Ok(n as i32), Err(e) => Err(e)}}
fn c_i16b(&self, i: usize) -> ResultS<i16>
{match self.c_u16b(i) {Ok(n) => Ok(n as i16), Err(e) => Err(e)}}
// Optional
fn o_iden(&self, i: usize) -> Option<Ident> {self.c_iden(i).ok()}
fn o_u32b(&self, i: usize) -> Option<u32> {self.c_u32b(i).ok()}
fn o_u16b(&self, i: usize) -> Option<u16> {self.c_u16b(i).ok()}
fn o_i32b(&self, i: usize) -> Option<i32> {self.c_i32b(i).ok()}
fn o_i16b(&self, i: usize) -> Option<i16> {self.c_i16b(i).ok()}
// Unchecked
fn b_iden(&self, i: usize) -> Ident {self.c_iden(i).unwrap()}
fn b_u32b(&self, i: usize) -> u32 {self.c_u32b(i).unwrap()}
fn b_u16b(&self, i: usize) -> u16 {self.c_u16b(i).unwrap()}
fn b_i32b(&self, i: usize) -> i32 {self.c_i32b(i).unwrap()}
fn b_i16b(&self, i: usize) -> i16 {self.c_i16b(i).unwrap()}
}
impl BinUtil for [u8]
{
fn c_iden(&self, i: usize) -> ResultS<Ident>
{
if i + 3 >= self.len() {return Err("not enough data")}
Ok([self[i], self[i+1], self[i+2], self[i+3]])
}
fn c_u32b(&self, i: usize) -> ResultS<u32>
{
if i + 3 >= self.len() {return Err("not enough data")}
Ok(self[i ] as (u32) << 24 | self[i+1] as (u32) << 16 |
self[i+2] as (u32) << 8 | self[i+3] as (u32))
}
fn c_u16b(&self, i: usize) -> ResultS<u16>
{
if i + 1 >= self.len() {return Err("not enough data")}
Ok(self[i] as (u16) << 8 | self[i+1] as (u16))
}
}
pub fn d_u32b(n: u32) -> [u8; 4] {[(n >> 24) as u8, (n >> 16) as u8,
(n >> 8) as u8, (n >> 0) as u8]}

View File

@ -3,30 +3,29 @@
use durandal::bin::*;
/// Checks for an AppleSingle header. Returns offset to the resource fork.
pub fn check_apple_single(b: &[u8]) -> usize
pub fn check_apple_single(b: &[u8]) -> Option<usize>
{
if b_u32b(&b[0..4]) != 0x51600 || b_u32b(&b[4..8]) != 0x20000
{return 0}
if b.o_u32b(0)? != 0x51600 || b.o_u32b(4)? != 0x20000 {return None}
let num = b_u16b(&b[24..26]) as usize;
let num = b.o_u16b(24)? as usize;
for i in 0..num
{
let p = 26 + (12 * i);
let fid = b_u32b(&b[p+0..p+ 4]);
let ofs = b_u32b(&b[p+4..p+ 8]) as usize;
let len = b_u32b(&b[p+8..p+12]) as usize;
let fid = b.o_u32b(p )?;
let ofs = b.o_u32b(p+4)? as usize;
let len = b.o_u32b(p+8)? as usize;
if fid == 1 {return if ofs + len > b.len() {0} else {ofs}}
if fid == 1 {return if ofs + len > b.len() {None} else {Some(ofs)}}
}
0
None
}
/// Checks for a MacBin header. Returns offset to the resource fork.
pub fn check_mac_bin(b: &[u8]) -> usize
pub fn check_mac_bin(b: &[u8]) -> Option<usize>
{
if b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 0x81 {return 0}
if b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 0x81 {return None}
let mut crc = 0;
@ -39,15 +38,15 @@ pub fn check_mac_bin(b: &[u8]) -> usize
}
}
if crc == b_u16b(&b[124..126]) {128} else {0}
if crc == b.o_u16b(124)? {Some(128)} else {None}
}
/// Reads a MacBin or AppleSingle header if there is one and returns the
/// offset from the start of the header to the resource fork (if one is found.)
pub fn try_mac_header(b: &[u8]) -> usize
{
let ofs = check_mac_bin(b);
if ofs != 0 {ofs} else {check_apple_single(b)}
let ofs = check_mac_bin(b).unwrap_or(0);
if ofs != 0 {ofs} else {check_apple_single(b).unwrap_or(0)}
}
// EOF

View File

@ -12,11 +12,11 @@ const PACK_RLE16 : u16 = 3;
const PACK_RLE32 : u16 = 4;
/// Read a colorTable structure.
fn get_clut(b: &[u8]) -> Vec<Color>
fn get_clut(b: &[u8]) -> ResultS<Vec<Color>>
{
// = b_u32b(&b[ ..4]); ctSeed
let dev = b_u16b(&b[4..6]) & 0x8000 != 0; // ctFlags
let num = b_u16b(&b[6..8]) as usize + 1; // ctSize
// = b.c_u32b(0)?; ctSeed
let dev = b.c_u16b(4)? & 0x8000 != 0; // ctFlags
let num = b.c_u16b(6)? as usize + 1; // ctSize
let mut map = Vec::new();
map.resize(num, Color{r: 0, g: 0, b: 0, a: 0});
@ -24,16 +24,16 @@ fn get_clut(b: &[u8]) -> Vec<Color>
for i in 0..num
{
let p = 8 + i * 8;
let n = (b_u16b(&b[p ..p+2]) & 0xff) as usize;
let r = (b_u16b(&b[p+2..p+4]) >> 8 ) as u8;
let g = (b_u16b(&b[p+4..p+6]) >> 8 ) as u8;
let b = (b_u16b(&b[p+6..p+8]) >> 8 ) as u8;
let n = (b.c_u16b(p )? & 0xff) as usize;
let r = (b.c_u16b(p+2)? >> 8 ) as u8;
let g = (b.c_u16b(p+4)? >> 8 ) as u8;
let b = (b.c_u16b(p+6)? >> 8 ) as u8;
// with device mapping, we ignore the index entirely
map[if dev {i} else {n}] = Color{r, g, b, a: 255};
}
map
Ok(map)
}
/// Read a sequence of packed RLE data.
@ -51,12 +51,12 @@ fn read_rle_data<F, N>(cmp: bool, len: usize, o: &mut Vec<u8>, mut rd: F)
}
/// Read run-length encoded data.
fn read_rle(b: &[u8], pt: usize, ln: bool) -> Result<(Vec<u8>, usize), &str>
fn read_rle(b: &[u8], pt: usize, ln: bool) -> ResultS<(Vec<u8>, usize)>
{
let mut p = 0;
let mut o = Vec::with_capacity(pt);
let sz = if pt > 250 {(b_u16b(&b[0..2]) as usize + 2, p += 2).0}
else {( b[0] as usize + 1, p += 1).0};
let sz = if pt > 250 {(b.c_u16b(0)? as usize + 2, p += 2).0}
else {(b[0] as usize + 1, p += 1).0};
while p < sz
{
@ -75,7 +75,7 @@ fn read_rle(b: &[u8], pt: usize, ln: bool) -> Result<(Vec<u8>, usize), &str>
}
/// Expand packed pixel data based on bit depth.
fn expand_data(b: Vec<u8>, depth: u16) -> Result<Vec<u8>, &'static str>
fn expand_data(b: Vec<u8>, depth: u16) -> ResultS<Vec<u8>>
{
let mut o = Vec::with_capacity(match depth {
4 => b.len() * 2,
@ -98,7 +98,7 @@ fn expand_data(b: Vec<u8>, depth: u16) -> Result<Vec<u8>, &'static str>
}
/// Process a CopyBits operation.
fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result<Image, &str>
fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS<Image>
{
const SIZE_HEAD: usize = 46;
@ -106,38 +106,40 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result
let (w, h) = (im.w(), im.h());
let pf = b_u16b(&b[p ..p+2]); // rowBytes
let yb = b_u16b(&b[p+ 2..p+ 4]) as usize; // Bounds
let xb = b_u16b(&b[p+ 4..p+ 6]) as usize; // 〃
let ye = b_u16b(&b[p+ 6..p+ 8]) as usize; // 〃
let xe = b_u16b(&b[p+ 8..p+10]) as usize; // 〃
// = b_u16b(&b[p+10..p+12]); pmVersion
let pack = b_u16b(&b[p+12..p+14]); // packType
// = b_u32b(&b[p+14..p+18]); packSize
// = b_u32b(&b[p+18..p+22]); hRes
// = b_u32b(&b[p+22..p+26]); vRes
// = b_u16b(&b[p+26..p+28]); pixelType
let dept = b_u16b(&b[p+28..p+30]); // pixelSize
// = b_u16b(&b[p+30..p+32]); cmpCount
// = b_u16b(&b[p+32..p+34]); cmpSize
// = b_u32b(&b[p+34..p+38]); planeBytes
// = b_u32b(&b[p+38..p+42]); pmTable
// = b_u32b(&b[p+42..p+46]); pmReserved
let pf = b.c_u16b(p )?; // rowBytes
let yb = b.c_u16b(p+ 2)? as usize; // Bounds
let xb = b.c_u16b(p+ 4)? as usize; // 〃
let ye = b.c_u16b(p+ 6)? as usize; // 〃
let xe = b.c_u16b(p+ 8)? as usize; // 〃
// = b.c_u16b(p+10)?; pmVersion
let pack = b.c_u16b(p+12)?; // packType
// = b.c_u32b(p+14)?; packSize
// = b.c_u32b(p+18)?; hRes
// = b.c_u32b(p+22)?; vRes
// = b.c_u16b(p+26)?; pixelType
let dept = b.c_u16b(p+28)?; // pixelSize
// = b.c_u16b(p+30)?; cmpCount
// = b.c_u16b(p+32)?; cmpSize
// = b.c_u32b(p+34)?; planeBytes
// = b.c_u32b(p+38)?; pmTable
// = b.c_u32b(p+42)?; pmReserved
p += SIZE_HEAD;
if pf & 0x8000 == 0 {return Err("PICT1 not supported")}
if xe - xb != w || ye - yb != h {return Err("image bounds are incorrect")}
let map = if packed {get_clut(&b[p..])} else {Vec::new()};
let pt = (pf & 0x3fff) as usize;
let map = if packed {get_clut(&b[p..])?} else {Vec::new()};
let rle = pack == PACK_DEFAULT ||
(pack == PACK_RLE16 && dept == 16) ||
(pack == PACK_RLE32 && dept == 32);
let pt = (pf & 0x3fff) as usize;
p += 18 + if packed {8 + map.len() * 8} else {0}; // srcRect, dstRect, mode
if clip {let sz = b_u16b(&b[p..p+2]) as usize; p += sz} // maskRgn
if clip {let sz = b.c_u16b(p)? as usize; p += sz} // maskRgn
match dept {
1 | 2 | 4 | 8 =>
@ -176,7 +178,7 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result
{
for _ in 0..h {
for _ in 0..w
{im.cr.push(Color::from_r5g5b5(b_u16b((&b[p..p+2], p += 2).0)))}
{im.cr.push(Color::from_r5g5b5(b.c_u16b((p, p += 2).0)?))}
}
Ok(im)
@ -192,7 +194,7 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result
p += pp;
for x in 0..w
{im.cr.push(Color::from_r5g5b5(b_u16b(&d[x*2..x*2+2])))}
{im.cr.push(Color::from_r5g5b5(d.c_u16b(x*2)?))}
}
Ok(im)
@ -244,25 +246,25 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result
}
/// Process a CompressedQuickTime operation.
fn read_quicktime_c(_im: Image, _b: &[u8]) -> Result<Image, &str>
fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS<Image>
{Err("compressed quicktime format not implemented")}
/// Load a PICT image.
pub fn load_pict(b: &[u8]) -> Result<Image, &str>
pub fn load_pict(b: &[u8]) -> ResultS<Image>
{
const SIZE_HEAD: usize = 10;
// size = b_u16b(&b[0.. 2]);
// top = b_u16b(&b[2.. 4]);
// left = b_u16b(&b[4.. 6]);
let h = b_u16b(&b[6.. 8]) as usize;
let w = b_u16b(&b[8..10]) as usize;
// size = b.c_u16b(0)?;
// top = b.c_u16b(2)?;
// left = b.c_u16b(4)?;
let h = b.c_u16b(6)? as usize;
let w = b.c_u16b(8)? as usize;
let im = Image::new(w, h);
let mut p = SIZE_HEAD;
while p < b.len()
{
let op = b_u16b((&b[p..p+2], p += 2).0);
let op = b.c_u16b((p, p += 2).0)?;
match op {
0x0098 => return read_bitmap_area(im, &b[p..], true, false), // PackBitsRect
@ -316,8 +318,8 @@ pub fn load_pict(b: &[u8]) -> Result<Image, &str>
0x0034 => p += 8, // FillRect
0x002d => p += 10, // LineJustify
0x0c00 => p += 24, // HeaderOp
0x0001 => p += (b_u16b(&b[p ..p+2]) & !1) as usize, // Clip
0x00a1 => p += (b_u16b(&b[p+2..p+4]) & !1) as usize + 2, // LongComment
0x0001 => p += (b.c_u16b(p )? & !1) as usize, // Clip
0x00a1 => p += (b.c_u16b(p+2)? & !1) as usize + 2, // LongComment
0x100..=
0x7fff => p += (op >> 8) as usize * 2, // Reserved
_ => return Err("invalid op in PICT")

View File

@ -37,7 +37,7 @@ fn main() -> io::Result<()>
let arg = env::args().nth(1).expect("need at least 1 argument");
let fp = fs::File::open(arg)?;
let mm = unsafe{Mmap::map(&fp)?};
let wad = wad::Wad::new(&mm);
let wad = wad::Wad::new(&mm).unwrap();
println!("{:#?}", wad);
@ -45,8 +45,7 @@ fn main() -> io::Result<()>
{
if let Some(b) = ent.map.get(b"PICT")
{
let im = load_pict(b);
match im {
match load_pict(b) {
Ok(im) => {
println!("entry {} has PICT {}x{}", id, im.w(), im.h());
write_ppm(&format!("out_{}.ppm", id), &im)?;

View File

@ -5,17 +5,17 @@ use durandal::bin::*;
impl Terminal
{
pub fn read(b: &[u8]) -> (usize, Terminal)
pub fn read(b: &[u8]) -> ResultS<(usize, Terminal)>
{
const SIZE_HEAD : usize = 10;
const SIZE_GROUP: usize = 12;
const SIZE_FACE : usize = 6;
let len = b_u16b(&b[ .. 2]) as usize;
let enc = b_u16b(&b[2.. 4]) & 1 != 0;
let lns = b_u16b(&b[4.. 6]);
let gnu = b_u16b(&b[6.. 8]) as usize;
let fnu = b_u16b(&b[8..10]) as usize;
let len = b.c_u16b(0)? as usize;
let enc = b.c_u16b(2)? & 1 != 0;
let lns = b.c_u16b(4)?;
let gnu = b.c_u16b(6)? as usize;
let fnu = b.c_u16b(8)? as usize;
let mut grp = Vec::with_capacity(gnu);
let mut fcs = Vec::with_capacity(fnu);
@ -25,12 +25,12 @@ impl Terminal
for _ in 0..gnu
{
// flg = b_u16b(&b[p ..p+ 2]);
let typ = b_u16b(&b[p+ 2..p+ 4]).into();
let per = b_i16b(&b[p+ 4..p+ 6]);
let beg = b_u16b(&b[p+ 6..p+ 8]) as usize;
let len = b_u16b(&b[p+ 8..p+10]) as usize;
let lns = b_u16b(&b[p+10..p+12]);
// flg = b.c_u16b(p )?;
let typ = b.c_u16b(p+ 2)?.into();
let per = b.c_i16b(p+ 4)?;
let beg = b.c_u16b(p+ 6)? as usize;
let len = b.c_u16b(p+ 8)? as usize;
let lns = b.c_u16b(p+10)?;
let sta = end + beg;
@ -45,19 +45,19 @@ impl Terminal
for _ in 0..fnu
{
let ind = b_u16b(&b[p ..p+2]);
let fce = b_u16b(&b[p+2..p+4]);
let col = b_u16b(&b[p+4..p+6]);
let ind = b.c_u16b(p )?;
let fce = b.c_u16b(p+2)?;
let col = b.c_u16b(p+4)?;
fcs.push(Face{ind, fce, col});
p += SIZE_FACE;
}
(len, Terminal{lns, grp, fcs})
Ok((len, Terminal{lns, grp, fcs}))
}
pub fn chunk(b: &[u8]) -> Vec<Terminal>
pub fn chunk(b: &[u8]) -> ResultS<Vec<Terminal>>
{
let mut v = Vec::new();
let mut p = 0;
@ -66,14 +66,14 @@ impl Terminal
{
let sta = p;
let (len, trm) = Terminal::read(&b[p..]);
let (len, trm) = Terminal::read(&b[p..])?;
v.push(trm);
p = sta + len;
}
v
Ok(v)
}
}
@ -160,7 +160,7 @@ impl fmt::Debug for Group
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
write!(f, "Group{{{:?} {} {}", self.typ, self.per, self.lns)?;
if self.txt.len() != 0 {write!(f, "\n{}\n", self.txt)?}
if self.txt.len() != 0 {write!(f, ";\n{}\n", self.txt)?}
write!(f, "}}")
}
}

View File

@ -48,29 +48,31 @@ impl<'a> fmt::Debug for Entry<'a>
impl<'a> Wad<'a>
{
pub fn new(b: &[u8]) -> Wad
pub fn new(b: &[u8]) -> ResultS<Wad>
{
const SIZE_ENTRY_NEW: usize = 10;
const SIZE_ENTRY_OLD: usize = 8;
let b = &b[try_mac_header(b)..];
let ver = b_u16b(&b[ .. 2]);
let dvr = b_u16b(&b[ 2.. 4]);
let nam = &b[ 4..68];
// crc = b_u32b(&b[68..72]);
let dir = b_u32b(&b[72..76]) as usize;
let num = b_u16b(&b[76..78]) as usize;
let ext = b_u16b(&b[78..80]) as usize;
// hdr = b_u16b(&b[80..82]);
// bsz = b_u16b(&b[82..84]);
// pck = b_u32b(&b[84..88]);
let b = &b[try_mac_header(b)..];
if b.len() < 128 {return Err("not enough data for header")}
let ver = b.c_u16b( 0)?;
let dvr = b.c_u16b( 2)?;
let nam = &b[4..68];
// crc = b.c_u32b(68)?;
let dir = b.c_u32b(72)? as usize;
let num = b.c_u16b(76)? as usize;
let ext = b.c_u16b(78)? as usize;
// hdr = b.c_u16b(80)?;
// bsz = b.c_u16b(82)?;
// pck = b.c_u32b(84)?;
let ver = match ver {
4 => Ver::MI,
2 => Ver::M2,
1 => Ver::M1Dir,
0 => Ver::M1,
_ => panic!("invalid wad version {}", ver),
_ => return Err("invalid wad version"),
};
let mut map = EntryMap::new();
@ -80,11 +82,14 @@ impl<'a> Wad<'a>
for i in 0..num
{
let ofs = b_u32b(&b[p ..p+ 4]) as usize;
let len = b_u32b(&b[p+4..p+ 8]) as usize;
let ofs = b.c_u32b(p )? as usize;
let len = b.c_u32b(p+4)? as usize;
let ind = if o {i as u16}
else {b_u16b(&b[p+8..p+10])};
let ent = Entry{map: get_chunks(&b[ofs..ofs+len], o),
else {b.c_u16b(p+8)?};
if ofs + len > b.len() {return Err("not enough data for entry")}
let ent = Entry{map: get_chunks(&b[ofs..ofs+len], o)?,
ext: &b[p+h..p+h+ext]};
map.insert(ind, ent);
@ -92,13 +97,13 @@ impl<'a> Wad<'a>
p += h + ext;
}
Wad{ver, dvr, ext,
nam: mac_roman_conv(nam),
ent: map}
Ok(Wad{ver, dvr, ext,
nam: mac_roman_conv(nam),
ent: map})
}
}
fn get_chunks(b: &[u8], o: bool) -> ChunkMap
fn get_chunks(b: &[u8], o: bool) -> ResultS<ChunkMap>
{
const SIZE_CHUNK_NEW: usize = 16;
const SIZE_CHUNK_OLD: usize = 12;
@ -109,15 +114,15 @@ fn get_chunks(b: &[u8], o: bool) -> ChunkMap
while p < b.len()
{
let k = b_iden(&b[p ..p+ 4]);
// nx = b_u32b(&b[p+ 4..p+ 8]);
let l = b_u32b(&b[p+ 8..p+12]) as usize;
// o = b_u32b(&b[p+12..p+16]);
let k = b.c_iden(p )?;
// nx = b.c_u32b(p+ 4)?;
let l = b.c_u32b(p+ 8)? as usize;
// o = b.c_u32b(p+12)?;
map.insert(k, &b[p+h ..p+h+l]);
p += l + h;
}
map
Ok(map)
}
// EOF