summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fmt.rs118
-rw-r--r--src/main.rs15
-rw-r--r--src/timer.rs33
3 files changed, 151 insertions, 15 deletions
diff --git a/src/fmt.rs b/src/fmt.rs
index b237a31..a838de6 100644
--- a/src/fmt.rs
+++ b/src/fmt.rs
@@ -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;
+ }
+ }
+}