diff options
| author | Jakob L. Kreuze <jakob@memeware.net> | 2018-05-30 16:30:03 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze <jakob@memeware.net> | 2018-05-30 16:30:03 -0400 |
| commit | 20c791ebe6526eec175cf83ec1f60d9c4eaa2e67 (patch) | |
| tree | ef650039ffdd42039b1e125701cd636171ce1982 /src | |
| parent | ef870e5b8c9809592dfc57e956c5ec60e56e4ae5 (diff) | |
Began to implement a timer systen and a parser for PALETTE.DAT
Diffstat (limited to 'src')
| -rw-r--r-- | src/fmt.rs | 118 | ||||
| -rw-r--r-- | src/main.rs | 15 | ||||
| -rw-r--r-- | src/timer.rs | 33 |
3 files changed, 151 insertions, 15 deletions
@@ -120,7 +120,7 @@ impl GroupManager { } #[cfg(test)] -mod tests { +mod grp_tests { use super::*; #[test] @@ -192,9 +192,8 @@ mod tests { let path_manager = PathManager::new(); let mut group_manager = GroupManager::new(path_manager); - match group_manager.load_data(&data) { - Ok(_) => panic!("Accepted incomplete header."), - Err(_) => (), + if let Ok(_) = group_manager.load_data(&data) { + panic!("Accepted invalid header."); } } @@ -213,9 +212,8 @@ mod tests { let path_manager = PathManager::new(); let mut group_manager = GroupManager::new(path_manager); - match group_manager.load_data(&data) { - Ok(_) => panic!("Accepted invalid header."), - Err(_) => (), + if let Ok(_) = group_manager.load_data(&data) { + panic!("Accepted invalid header."); } } @@ -235,9 +233,8 @@ mod tests { let path_manager = PathManager::new(); let mut group_manager = GroupManager::new(path_manager); - match group_manager.load_data(&data) { - Ok(_) => panic!("Accepted invalid header."), - Err(_) => (), + if let Ok(_) = group_manager.load_data(&data) { + panic!("Accepted invalid header."); } } @@ -256,9 +253,104 @@ mod tests { let path_manager = PathManager::new(); let mut group_manager = GroupManager::new(path_manager); - match group_manager.load_data(&data) { - Ok(_) => panic!("Accepted invalid header."), - Err(_) => (), + if let Ok(_) = group_manager.load_data(&data) { + panic!("Accepted invalid header."); + } + } +} + +// What's the PALETTE.DAT format? +// +// char palette[768], palookup[numpalookups][256], transluc[256][256]; +// short numpalookups; +// +// fil = open("PALETTE.DAT",...); +// read(fil,palette,768); +// read(fil,&numpalookups,2); +// read(fil,palookup,numpalookups*256); +// read(fil,transluc,65536); +// close(fil); +// +// PALETTE: This 768 byte array is exactly the palette you want. The format is: +// Red0, Green0, Blue0, Red1, Green1, Blue1, ..., Blue255 +// The colors are based on the VGA 262,144 color palette. The values range from +// 0-63, so if you want to convert it to a windows palette you will have to +// multiply each byte by 4. +// +// NUMPALOOKUPS: The number of shading tables used. Usually this number is 32, +// but 16 or 64 have also been used. Each of the 256 colors of the VGA palette +// can take on any of "numpalookups" number of shades. +// +// PALOOKUP: The shading table. If numpalookups = 32, then this table is: +// (32 shades) * (256 colors) = 8192 bytes (8K). The shade tables are often made +// to go from normal brightness (shade #0) down to pitch black (shade #31) So +// the first 256 bytes of the table would be for shade #0, etc... +// +// TRANSLUC: 64K translucent lookup table. Given any 2 colors of the palette, +// this lookup table gives the best match of the 2 colors when mixed together. +// +// Here's a funny story: I noticed that Duke3D's PALETTE.DAT file is 8K longer +// than it should be. Any PALETTE.DAT file with 32 shades and translucent table +// should be 74,498 bytes. Duke3D's palette is 82,690 bytes, but it only has 32 +// shades! The reason is that at one time, Duke3D had 64 shades in their +// "palookup" table. Then when we noticed that this extra memory overhead slowed +// down the frame rate of the game noticably, it was converted back to 32 +// shades. The problem is that my palette conversion program never truncated off +// the end of the file. So the last 8K of Duke3D's PALETTE.DAT is the last 8K of +// a translucent table that was based on an older version of their palette. + +/// Parser for PALETTE.DAT, the file specifying the color format. +pub struct Palette { + colors: Vec<u8>, +} + +impl Palette { + /// Parse the contents of a PALETTE.DAT + pub fn new(data: &[u8]) -> Result<Palette, Box<Error>> { + let len = data.len(); + + // FIXME: This only takes into account the actual palette. PALETTE.DAT + // should also contain some lookup tables. + if len < 770 { + bail!("Too small to contain palette."); + } + + let colors = data[0..768].to_vec(); + + // FIXME: Not loading the lookup table yet because.. well, I don't know + // if we really need it yet? I suppose we'll need to get the values for + // TRANSLUC, but we're not on DOS anymore and I think a lookup table + // would be overkill. My plan is to convert ART files into bitmaps ahead + // of time, anyway. + + // let _lookup_count = LittleEndian::read_u16(size) as usize; + + Ok(Palette { colors }) + } +} + +#[cfg(test)] +mod palette_tests { + use super::*; + + #[test] + fn test_load_slice() { + // Considering the size of PALETTE.DAT, it would be absurd embed as a + // blob in this file. We'll just generate dummy data. I'm leaving the + // number of 'pa' lookups as 0 intentionally. + let data = [0; 0x10301]; + + if let Err(e) = Palette::new(&data) { + panic!("Valid PALETTE errored out with '{}'", e); + } + } + + #[test] + fn test_not_enough_data() { + let data = [0; 1]; + + if let Ok(_) = Palette::new(&data) { + panic!("Accepted incomplete header."); } } } diff --git a/src/main.rs b/src/main.rs index ac6a87a..b60030e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,24 @@ #[macro_use] extern crate simple_error; +use std::fs::File; // Temporary, used for 'extract'. +use std::io::Write; // Temporary, used for 'extract'. use std::process; mod fmt; mod path; +// This function is strictly temporary and exists only to help me get samples of +// binary files to inspect in radare. +fn extract(name: &str, group_manager: &fmt::GroupManager) { + let output_path = format!("/tmp/{}", name); + let mut file = File::create(output_path).unwrap(); + file.write_all(group_manager.get(name).unwrap()).ok(); +} + fn main() { let path_manager = path::PathManager::new(); - + let filename = "DUKE3D.GRP"; let mut group_manager = fmt::GroupManager::new(path_manager); @@ -17,5 +27,6 @@ fn main() { process::exit(1); } - println!("DOGWHINE.VOC: {} bytes", group_manager.get("DOGWHINE.VOC").unwrap().len()); + extract("PALETTE.DAT", &group_manager); + // println!("DOGWHINE.VOC: {} bytes", group_manager.get("DOGWHINE.VOC").unwrap().len()); } diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..f34df8d --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,33 @@ +extern crate sdl2; + +use sdl2::TimerSubsystem; + +static const FREQUENCY = 1000; + +/// Implementation of a timer for managing game ticks. +pub struct Timer { + last_sample: u32, + ticks_per_second: u32, + ms_per_u64_tick: f64, + + sdl_timer: TimerSubsystem, +} + +impl Timer { + /// Create a new timer with the given frequency. + pub fn new(ticks_per_second: u32, sdl_timer: TimerSubsystem) -> Timer { + let last_sample = sdl_timer.ticks() * tics_per_second / FREQUENCY; + let ms_per_u64_tick = 1000.0 / sdl_timer.performance_frequency(); + Timer { last_sample, ticks_per_second, ms_per_u64_tick, sdl_timer } + } + + // TODO: Document this. + pub fn update(&mut self) { + let ms = self.sdl_timer.ticks(); + let ticks = ms * self.ticks_per_second / FREQUENCY - self.last_sample; + + if ticks > 0 { + self.last_sample += ticks; + } + } +} |