From 6420e4a6e3ca0edd174c229499cfdbee9fe3f71c Mon Sep 17 00:00:00 2001 From: Marrub Date: Tue, 11 Sep 2018 15:07:42 -0400 Subject: [PATCH] Improve error handling and syntax --- src/durandal/bin.rs | 60 +++++++++++++++++++++----- src/durandal/machead.rs | 27 ++++++------ src/durandal/pict.rs | 94 +++++++++++++++++++++-------------------- src/main.rs | 5 +-- src/marathon/term.rs | 40 +++++++++--------- src/marathon/wad.rs | 57 +++++++++++++------------ 6 files changed, 163 insertions(+), 120 deletions(-) diff --git a/src/durandal/bin.rs b/src/durandal/bin.rs index 849a4b6..b2822d4 100644 --- a/src/durandal/bin.rs +++ b/src/durandal/bin.rs @@ -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 = Result; -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; + fn c_u32b(&self, i: usize) -> ResultS; + fn c_u16b(&self, i: usize) -> ResultS; + + fn c_i32b(&self, i: usize) -> ResultS + {match self.c_u32b(i) {Ok(n) => Ok(n as i32), Err(e) => Err(e)}} + fn c_i16b(&self, i: usize) -> ResultS + {match self.c_u16b(i) {Ok(n) => Ok(n as i16), Err(e) => Err(e)}} + + // Optional + fn o_iden(&self, i: usize) -> Option {self.c_iden(i).ok()} + fn o_u32b(&self, i: usize) -> Option {self.c_u32b(i).ok()} + fn o_u16b(&self, i: usize) -> Option {self.c_u16b(i).ok()} + fn o_i32b(&self, i: usize) -> Option {self.c_i32b(i).ok()} + fn o_i16b(&self, i: usize) -> Option {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 + { + 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 + { + 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 + { + 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]} diff --git a/src/durandal/machead.rs b/src/durandal/machead.rs index 9e60889..fb3f039 100644 --- a/src/durandal/machead.rs +++ b/src/durandal/machead.rs @@ -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 { - 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 { - 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 diff --git a/src/durandal/pict.rs b/src/durandal/pict.rs index ca0f878..3afb7dc 100644 --- a/src/durandal/pict.rs +++ b/src/durandal/pict.rs @@ -12,11 +12,11 @@ const PACK_RLE16 : u16 = 3; const PACK_RLE32 : u16 = 4; /// Read a colorTable structure. -fn get_clut(b: &[u8]) -> Vec +fn get_clut(b: &[u8]) -> ResultS> { - // = 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 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(cmp: bool, len: usize, o: &mut Vec, mut rd: F) } /// Read run-length encoded data. -fn read_rle(b: &[u8], pt: usize, ln: bool) -> Result<(Vec, usize), &str> +fn read_rle(b: &[u8], pt: usize, ln: bool) -> ResultS<(Vec, 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, usize), &str> } /// Expand packed pixel data based on bit depth. -fn expand_data(b: Vec, depth: u16) -> Result, &'static str> +fn expand_data(b: Vec, depth: u16) -> ResultS> { let mut o = Vec::with_capacity(match depth { 4 => b.len() * 2, @@ -98,7 +98,7 @@ fn expand_data(b: Vec, depth: u16) -> Result, &'static str> } /// Process a CopyBits operation. -fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result +fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS { 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 +fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS {Err("compressed quicktime format not implemented")} /// Load a PICT image. -pub fn load_pict(b: &[u8]) -> Result +pub fn load_pict(b: &[u8]) -> ResultS { 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 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") diff --git a/src/main.rs b/src/main.rs index e68928c..6072f6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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)?; diff --git a/src/marathon/term.rs b/src/marathon/term.rs index 3c73419..7ea867e 100644 --- a/src/marathon/term.rs +++ b/src/marathon/term.rs @@ -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 + pub fn chunk(b: &[u8]) -> ResultS> { 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, "}}") } } diff --git a/src/marathon/wad.rs b/src/marathon/wad.rs index 2c7b6fa..f8a73f9 100644 --- a/src/marathon/wad.rs +++ b/src/marathon/wad.rs @@ -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 { 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 { 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