From ddc135e09a95884881e574d0efbbff62e2edcc44 Mon Sep 17 00:00:00 2001 From: Jared Roberts Date: Tue, 1 Nov 2016 03:14:49 -0700 Subject: [PATCH] read lzw .EGA files --- Cargo.toml | 1 + src/ega.rs | 177 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 152 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index feadbb6..fef9553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ gl = "*" gfx = "*" gfx_device_gl = "*" image = "*" +lzw = "*" nalgebra = "*" num-traits = "*" openvr = { git = "https://github.com/rust-openvr/rust-openvr" } diff --git a/src/ega.rs b/src/ega.rs index d4041cf..8882108 100644 --- a/src/ega.rs +++ b/src/ega.rs @@ -1,19 +1,25 @@ -static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0x00], - [0x00, 0x00, 0xAA, 0x00], - [0x00, 0xAA, 0x00, 0x00], - [0x00, 0xAA, 0xAA, 0x00], - [0xAA, 0x00, 0x00, 0x00], - [0xAA, 0x00, 0xAA, 0x00], - [0xAA, 0x55, 0x00, 0x00], - [0xAA, 0xAA, 0xAA, 0x00], - [0x55, 0x55, 0x55, 0x00], - [0x55, 0x55, 0xFF, 0x00], - [0x55, 0xFF, 0x55, 0x00], - [0x55, 0xFF, 0xFF, 0x00], - [0xFF, 0x55, 0x55, 0x00], - [0xFF, 0x55, 0xFF, 0x00], - [0xFF, 0xFF, 0x55, 0x00], - [0xFF, 0xFF, 0xFF, 0x00]]; +extern crate lzw; + +use std::cell::RefCell; + +use self::lzw::BitReader; + +static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0xFF], + [0x00, 0x00, 0xAA, 0xFF], + [0x00, 0xAA, 0x00, 0xFF], + [0x00, 0xAA, 0xAA, 0xFF], + [0xAA, 0x00, 0x00, 0xFF], + [0xAA, 0x00, 0xAA, 0xFF], + [0xAA, 0x55, 0x00, 0xFF], + [0xAA, 0xAA, 0xAA, 0xFF], + [0x55, 0x55, 0x55, 0xFF], + [0x55, 0x55, 0xFF, 0xFF], + [0x55, 0xFF, 0x55, 0xFF], + [0x55, 0xFF, 0xFF, 0xFF], + [0xFF, 0x55, 0x55, 0xFF], + [0xFF, 0x55, 0xFF, 0xFF], + [0xFF, 0xFF, 0x55, 0xFF], + [0xFF, 0xFF, 0xFF, 0xFF]]; pub enum Compression { Uncompressed, @@ -98,16 +104,12 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling) let out: Vec; out = match compression { - Compression::Uncompressed => buf.iter() - .flat_map(|tile_byte| { - EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize] - .into_iter() - .chain(EGA_PALETTE[(tile_byte & 0xF) as usize] - .into_iter()) - }) - .map(|x| *x) - .collect(), - _ => unimplemented!() + Compression::Uncompressed => decode_uncompressed(buf), + Compression::Rle => unimplemented!(), + Compression::Lzw => { + let mut decoder = U4Lzw::new(); + decode_uncompressed(&decoder.decode(buf)) + }, }; let dim = match tiling { Tiling::Tiled(tiledim) => tiledim as usize, @@ -115,3 +117,126 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling) }; EgaPage { data: out, dim: dim} } + +fn decode_uncompressed(buf: &[u8]) -> Vec { + buf.iter() + .flat_map(|tile_byte| { + EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize] + .into_iter() + .chain(EGA_PALETTE[(tile_byte & 0xF) as usize] + .into_iter()) + }) + .map(|x| *x) + .collect() +} + +struct U4Lzw { + table: Vec>>, + load: u16, +} + +const U4_CODE_SIZE: u8 = 12; +impl U4Lzw { + fn new() -> U4Lzw { + let mut out = U4Lzw { + table: vec![RefCell::new(Vec::new()); 1 << U4_CODE_SIZE], + load: 0 + }; + for (i, entry) in out.table.iter_mut().take(256).enumerate() { + entry.borrow_mut().push(i as u8) + } + out + } + + fn reset(&mut self) { + for i in 256..(1 << U4_CODE_SIZE) { + self.table[i].borrow_mut().clear(); + } + self.load = 0; + } + + fn decode(&mut self, bytes: &[u8]) -> Vec { + let mut out = Vec::new(); + let mut iter = CodeIter::new(bytes); + + let mut prev = iter.next().expect("empty lzw"); + let mut ch = prev as u8; + out.push(ch); + + for code in iter { + { + let mut new; + let mut seq = &*self.table[code as usize].borrow(); + if seq.is_empty() { + new = self.table[prev as usize].borrow().clone(); + new.push(ch); + seq = &new; + }; + out.extend(seq.iter()); + ch = seq[0]; + } + let hash = self.hash(ch, prev); + { + let old = self.table[prev as usize].borrow(); + let mut entry = self.table[hash].borrow_mut(); + entry.extend(old.iter()); + entry.push(ch); + } + prev = code; + + self.load += 1; + if self.load > 0xccd { self.reset(); } + } + out + } + + fn hash(&self, root: u8, code: u16) -> usize { + let hash = Self::hash_primary(root, code); + if self.table[hash].borrow().is_empty() { return hash; } + + let mut hash = Self::hash_secondary(root, code); + while !self.table[hash].borrow().is_empty() { + hash = (hash + 509) & 0xfff + } + + hash + } + + fn hash_primary(root: u8, code: u16) -> usize { + ((root as (u16) << 4) ^ code) as usize + } + + fn hash_secondary(root: u8, code: u16) -> usize { + let base = (((root as (u16) << 1) + code) | 0x800) as u32; + let squared = base * base; + ((squared & 0x0003ffc0) >> 6) as usize + } +} + +struct CodeIter<'a> { + pos: usize, + bytes: &'a [u8], + reader: lzw::MsbReader, +} + +impl<'a> CodeIter<'a> { + fn new(bytes: &[u8]) -> CodeIter { + CodeIter { pos: 0, bytes: bytes, reader: lzw::MsbReader::new() } + } +} + +impl<'a> Iterator for CodeIter<'a> { + type Item = u16; + fn next(&mut self) -> Option { + match self.reader.read_bits(&self.bytes[self.pos..], U4_CODE_SIZE) { + lzw::Bits::Some(used, code) => { + self.pos += used; + Some(code) + }, + lzw::Bits::None(used) => { + self.pos += used; + None + } + } + } +}