summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/defs.rs95
-rw-r--r--src/fmt.rs114
-rw-r--r--src/main.rs329
5 files changed, 453 insertions, 93 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 300988d..47c9b8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,7 +8,14 @@ name = "rebuild"
version = "0.1.0"
dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "simple-error 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "simple-error"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[metadata]
"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
+"checksum simple-error 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "7779d1977a9e1e50bebb430a57114acc64bc4c40d6d8efb3e57893531d5fd895"
diff --git a/Cargo.toml b/Cargo.toml
index 7c2ba50..c3fdc4d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,3 +5,4 @@ authors = ["Jakob L. Kreuze <jakob@memeware.net>"]
[dependencies]
byteorder = "1"
+simple-error = "0.1"
diff --git a/src/defs.rs b/src/defs.rs
new file mode 100644
index 0000000..37dd5a1
--- /dev/null
+++ b/src/defs.rs
@@ -0,0 +1,95 @@
+// Straight ripped from DUKE3D.H
+
+pub struct UserDefs {
+ god: bool,
+ warp_on: bool,
+ cashman: bool,
+ eog: bool,
+ showallmap: bool,
+ show_help: bool,
+ scrollmode: bool,
+ clipping: bool,
+ overhead_on: bool,
+ last_overhead: bool,
+ showweapons: bool,
+
+ // Vec is MAXPLAYERS in size, names are 32 characters.
+ user_name: Vec<String>,
+
+ // Vec is 10 in size, strings are 40 characters.
+ ridecule: Vec<String>,
+
+ // Vec is 10 in size, strings are 22 characters.
+ savegame: Vec<String>,
+
+ // Constrained to 128 characters.
+ pwlockout: String,
+
+ // Constrained to 128 characters.
+ rtsname: String,
+
+ pause_on: i16, // Maybe a bool?
+ from_bonus: i16, // ??
+ camerasprite: i16, //??
+ last_camsprite: i16, // ??
+ last_level: i16, // ??
+ secretlevel: i16,
+
+ // These names are pretty shitty.
+ const_visibility: i32,
+ uw_framerate: i32,
+ camera_time: i32,
+ folfvel: i32,
+ folavel: i32,
+ folx: i32,
+ foly: i32,
+ fola: i32,
+ reccnt: i32,
+
+ entered_name: i32,
+ screen_tilting: i32,
+ shadows: i32,
+ fta_on: i32,
+ executions: i32,
+ auto_run: i32,
+
+ coords: i32,
+ tickrate: i32,
+ m_coop: i32,
+ coop: i32,
+ screen_size: i32,
+ lockout: i32,
+ crosshair: i32,
+
+ // [MAXPLAYERS][MAX_WEAPONS]
+ wchoice: Vec<Vec<i32>>,
+ playerai: i32,
+
+ respawn_monsters: i32,
+ respawn_items: i32,
+ respawn_inventory: i32,
+ recstat: i32,
+ monsters_off: i32,
+ brightness: i32,
+
+ m_respawn_items: i32,
+ m_respawn_monsters: i32,
+ m_respawn_inventory: i32,
+ m_recstat: i32,
+ m_monsters_off: i32,
+ detail: i32,
+
+ m_ffire: i32,
+ ffire: i32,
+ m_player_skill: i32,
+ m_level_number: i32,
+ m_volume_number: i32,
+ multimode: i32,
+
+ player_skill: i32,
+ level_number: i32,
+ volume_number: i32,
+ m_marker: i32,
+ marker: i32,
+ mouseflip: i32,
+}
diff --git a/src/fmt.rs b/src/fmt.rs
index 4a747a7..61f53eb 100644
--- a/src/fmt.rs
+++ b/src/fmt.rs
@@ -1,33 +1,13 @@
extern crate byteorder;
+extern crate simple_error;
-use std::fmt;
+use std::collections::HashMap;
use std::error::Error;
+use std::fs::File;
+use std::io::Read;
use self::byteorder::{ByteOrder, LittleEndian};
-#[derive(Debug)]
-pub struct FormatError {
- details: &'static str,
-}
-
-impl FormatError {
- fn new(details: &'static str) -> FormatError {
- FormatError { details }
- }
-}
-
-impl fmt::Display for FormatError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.details)
- }
-}
-
-impl Error for FormatError {
- fn description(&self) -> &str {
- self.details
- }
-}
-
// The ".grp" file format is just a collection of a lot of files stored into 1 big
// one. I tried to make the format as simple as possible: The first 12 bytes
@@ -38,75 +18,77 @@ impl Error for FormatError {
// after the other in the same order as the list of files.
#[derive(Debug)]
-pub struct GroupEntry {
- pub name: String,
- pub data: Vec<u8>,
+pub struct GroupManager {
+ files: HashMap<String, Vec<u8>>,
}
-#[derive(Debug)]
-pub struct Group {
- pub file_count: usize,
-
- data: Vec<u8>,
-
- iter_index: usize,
- data_off: usize,
-}
+impl GroupManager {
+ pub fn new() -> GroupManager {
+ GroupManager { files: HashMap::new() }
+ }
-impl Group {
- pub fn new(data: &[u8]) -> Result<Group, Box<Error>> {
+ pub fn load_from_slice(&mut self, data: &[u8]) -> Result<(), Box<Error>> {
let len = data.len();
if len < 16 {
- let details = "'data' is too small to contain the GRP header.";
- return Err(Box::new(FormatError::new(details)));
+ bail!("'data' is too small to contain the GRP header.");
}
let header = String::from_utf8(data[..12].to_vec())?;
if header.as_str() != "KenSilverman" {
- let details = "Invalid GRP header.";
- return Err(Box::new(FormatError::new(details)));
+ bail!("Invalid GRP header.");
}
let file_count = LittleEndian::read_u32(&data[12..16]) as usize;
- // 16 bytes for the header, and 16 bytes for each file entry. The raw
+ // 16 bytes for the header, and 16 bytes for each table entry. The raw
// data will follow.
- let data_off = 16 * (file_count + 1) as usize;
+ let data_start = 16 * (file_count + 1) as usize;
- if data_off >= len {
- let details = "Invalid number of files.";
- return Err(Box::new(FormatError::new(details)));
+ if data_start >= len {
+ bail!("Invalid number of files.");
}
- Ok(Group { file_count, data: data.clone().to_vec(), iter_index: 0, data_off })
- }
-}
+ let mut data_off = data_start;
+
+ for i in 0..file_count {
+ // Similar to how 'data_start' was calculated - 16 bytes for the
+ // header, and 16 bytes for each table entry.
+ let table_off = 16 * (i + 1);
+
+ let name = &data[table_off..table_off+12];
+ let name = String::from_utf8(name.to_vec())?;
+ let name = if let Some(j) = name.find('\x00') {
+ String::from(&name[..j])
+ } else {
+ name
+ };
-impl Iterator for Group {
- type Item = GroupEntry;
+ let size = &data[table_off+12..table_off+16];
+ let size = LittleEndian::read_u32(size) as usize;
- fn next(&mut self) -> Option<GroupEntry> {
- if self.iter_index >= self.file_count {
- return None;
+ let data = data[data_off..data_off+size].to_vec();
+ data_off += size;
+
+ self.files.insert(name, data);
}
- let table_off = 16 * (1 + self.iter_index);
+ Ok(())
+ }
- // Raising an error would be more ideal than a sentinel filename.
- let size = LittleEndian::read_u32(&self.data[table_off+12..table_off+16]) as usize;
- let name = match String::from_utf8(self.data[table_off..table_off+12].to_vec()) {
- Ok(name) => name,
- Err(_) => String::from("ERRORFNAMEAA"),
- };
+ pub fn load_from_file(&mut self, filename: &str) -> Result<(), Box<Error>> {
+ let mut file = File::open(filename)?;
+ let mut bytes: Vec<u8> = Vec::new();
- let result = Some(GroupEntry { name, data: self.data[self.data_off..self.data_off+size].to_vec() });
+ file.read_to_end(&mut bytes)?;
+ self.load_from_slice(&bytes)?;
- self.iter_index += 1;
- self.data_off += size;
+ Ok(())
+ }
- result
+ pub fn get(&self, filename: &str) -> Option<&[u8]> {
+ Some(&self.files.get(filename)?)
}
}
diff --git a/src/main.rs b/src/main.rs
index 29c3e1e..0c48e53 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,39 +1,314 @@
+#[macro_use]
+extern crate simple_error;
+
+use std::process;
+
mod fmt;
-use std::fs::File;
-use std::io::Read;
-use std::io::Write; // Temporary
+// initgroupfile(char *filename)
+// {
+// char buf[16];
+// long i, j, k;
+
+// if (numgroupfiles >= MAXGROUPFILES) return(-1);
+
+// groupfil[numgroupfiles] = open(filename,O_BINARY|O_RDWR,S_IREAD);
+// if (groupfil[numgroupfiles] != -1)
+// {
+// groupfilpos[numgroupfiles] = 0;
+// read(groupfil[numgroupfiles],buf,16);
+// if ((buf[0] != 'K') || (buf[1] != 'e') || (buf[2] != 'n') ||
+// (buf[3] != 'S') || (buf[4] != 'i') || (buf[5] != 'l') ||
+// (buf[6] != 'v') || (buf[7] != 'e') || (buf[8] != 'r') ||
+// (buf[9] != 'm') || (buf[10] != 'a') || (buf[11] != 'n'))
+// {
+// close(groupfil[numgroupfiles]);
+// groupfil[numgroupfiles] = -1;
+// return(-1);
+// }
+// gnumfiles[numgroupfiles] = *((long *)&buf[12]);
+
+// if ((gfilelist[numgroupfiles] = (char *)kmalloc(gnumfiles[numgroupfiles]<<4)) == 0)
+// { printf("Not enough memory for file grouping system\n"); exit(0); }
+// if ((gfileoffs[numgroupfiles] = (long *)kmalloc((gnumfiles[numgroupfiles]+1)<<2)) == 0)
+// { printf("Not enough memory for file grouping system\n"); exit(0); }
+
+// read(groupfil[numgroupfiles],gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
+
+// j = 0;
+// for(i=0;i<gnumfiles[numgroupfiles];i++)
+// {
+// k = *((long *)&gfilelist[numgroupfiles][(i<<4)+12]);
+// gfilelist[numgroupfiles][(i<<4)+12] = 0;
+// gfileoffs[numgroupfiles][i] = j;
+// j += k;
+// }
+// gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
+// }
+// numgroupfiles++;
+// return(groupfil[numgroupfiles-1]);
+// }
+
+
+// High-level description:
+// - Caches group files into an array with a rolling index.
+// - Keeps track of group file positions (pointer?) starts as 0.
+// - Keeps track of number of files in another array (gnumfiles)
+//
+// - Check header for "KenSilverman", invalidate entry in cache if bad.
+// - kmalloc 16 * number of files (for storing table)
+// - kmalloc 4 * (number of files + 1) (for storing individual file offsets)
+// - Read in table
+// - For every entry, read in the file size, and invalidate the entry.
+
+
+
+
+// -------
+
+// void printstr(short x, short y, char string[81], char attribute)
+// {
+// char character;
+// short i, pos;
+
+// pos = (y*80+x)<<1;
+// i = 0;
+// while (string[i] != 0)
+// {
+// character = string[i];
+// printchrasm(0xb8000+(long)pos,1L,((long)attribute<<8)+(long)character);
+// i++;
+// pos+=2;
+// }
+// }
+
+// static char todd[] = "Duke Nukem 3D(tm) Copyright 1989, 1996 Todd Replogle and 3D Realms Entertainment";
+// static char trees[] = "I want to make a game with trees";
+// static char sixteen[] = "16 Possible Dukes";
fn main() {
- let filename = "/tmp/DUKE3D.GRP";
+ // #define VERSION "1.4"
+ // #define HEAD2 "Duke Nukem 3D v"VERSION" - Atomic Edition"
+ // printstr(40-(strlen(HEAD2)>>1),0,HEAD2,79);
+
+ // ud.multimode = 1;
+ // printstr(0,1," Copyright (c) 1996 3D Realms Entertainment ",79);
- let mut file = match File::open(filename) {
- Ok(file) => file,
- Err(err) => {
- panic!("Couldn't open {}", filename);
- }
- };
+ // initgroupfile("duke3d.grp");
- let mut bytes: Vec<u8> = Vec::new();
+ let filename = "DUKE3D.GRP";
+ let mut group_manager = fmt::GroupManager::new();
- match file.read_to_end(&mut bytes) {
- Ok(count) => println!("Read {} bytes", count),
- Err(err) => panic!("Couldn't read."),
+ if let Err(e) = group_manager.load_from_file(filename) {
+ println!("Couldn't open {}: {}", filename, e);
+ process::exit(1);
}
- let grp = fmt::Group::new(&bytes).unwrap();
+ println!("DOGWHINE.VOC: {} bytes", group_manager.get("DOGWHINE.VOC").unwrap().len());
- println!("Files: {}", grp.file_count);
+ // checkcommandline(argc,argv);
- for entry in grp {
- println!("{}", entry.name);
- if entry.name.contains("MID") {
- let mut filename = String::from("/tmp/");
- filename.push_str(&entry.name);
- filename = filename[..filename.find('\x00').unwrap()].to_string();
-
- let mut file = File::create(filename).unwrap();
- file.write_all(&entry.data).unwrap();
- }
- }
+ println!("You don't have enough free memory to run Duke Nukem 3D.");
+ println!("The DOS \"mem\" command should report 6,800K (or 6.8 megs)");
+ println!("of \"total memory free\".");
+ println!("");
+ println!("Duke Nukem 3D requires {} more bytes to run.", 3162000 - 350000);
+ process::exit(1);
+
+ // Considering that most of this can be implemented with Drop, I
+ // don't think it's necessary.
+
+ // RegisterShutdownFunction( ShutDown );
+ // void ShutDown( void )
+ // {
+ // SoundShutdown();
+ // MusicShutdown();
+ // uninittimer();
+ // uninitengine();
+ // CONTROL_Shutdown();
+ // CONFIG_WriteSetup();
+ // KB_Shutdown();
+ // }
+
+ // Startup();
+
+
+ // if(numplayers > 1)
+ // {
+ // ud.multimode = numplayers;
+ // sendlogon();
+ // }
+ // else if(boardfilename[0] != 0)
+ // {
+ // ud.m_level_number = 7;
+ // ud.m_volume_number = 0;
+ // ud.warp_on = 1;
+ // }
+
+ // getnames();
+
+ // if(ud.multimode > 1)
+ // {
+ // playerswhenstarted = ud.multimode;
+
+ // if(ud.warp_on == 0)
+ // {
+ // ud.m_monsters_off = 1;
+ // ud.m_player_skill = 0;
+ // }
+ // }
+
+ // ud.last_level = -1;
+
+ // RTS_Init(ud.rtsname);
+ // if(numlumps) printf("Using .RTS file:%s\n",ud.rtsname);
+
+ // if( setgamemode(ScreenMode,ScreenWidth,ScreenHeight) < 0 )
+ // {
+ // printf("\nVESA driver for ( %i * %i ) not found/supported!\n",xdim,ydim);
+ // ScreenMode = 2;
+ // ScreenWidth = 320;
+ // ScreenHeight = 200;
+ // setgamemode(ScreenMode,ScreenWidth,ScreenHeight);
+ // }
+
+ // // CTW END - MODIFICATION
+
+ // genspriteremaps();
+
+// #ifdef VOLUMEONE
+// if(numplayers > 4 || ud.multimode > 4)
+// gameexit(" The full version of Duke Nukem 3D supports 5 or more players.");
+// #endif
+
+ // setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);
+
+ // ESCESCAPE;
+
+ // FX_StopAllSounds();
+ // clearsoundlocks();
+
+ // if(ud.warp_on > 1 && ud.multimode < 2)
+ // {
+ // clearview(0L);
+ // ps[myconnectindex].palette = palette;
+ // palto(0,0,0,0);
+ // rotatesprite(320<<15,200<<15,65536L,0,LOADSCREEN,0,0,2+8+64,0,0,xdim-1,ydim-1);
+ // menutext(160,105,0,0,"LOADING SAVED GAME...");
+ // nextpage();
+
+ // j = loadplayer(ud.warp_on-2);
+ // if(j)
+ // ud.warp_on = 0;
+ // }
+
+ // // getpackets();
+
+// MAIN_LOOP_RESTART:
+
+// if(ud.warp_on == 0)
+// Logo();
+// else if(ud.warp_on == 1)
+// {
+// newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);
+// enterlevel(MODE_GAME);
+// }
+// else vscrn();
+
+// tempautorun = ud.auto_run;
+
+// if( ud.warp_on == 0 && playback() )
+// {
+// FX_StopAllSounds();
+// clearsoundlocks();
+// nomorelogohack = 1;
+// goto MAIN_LOOP_RESTART;
+// }
+
+// ud.auto_run = tempautorun;
+
+// ud.warp_on = 0;
+
+// while ( !(ps[myconnectindex].gm&MODE_END) ) //The whole loop!!!!!!!!!!!!!!!!!!
+// {
+// if( ud.recstat == 2 || ud.multimode > 1 || ( ud.show_help == 0 && (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU ) )
+// if( ps[myconnectindex].gm&MODE_GAME )
+// if( moveloop() ) continue;
+
+// if( ps[myconnectindex].gm&MODE_EOL || ps[myconnectindex].gm&MODE_RESTART )
+// {
+// if( ps[myconnectindex].gm&MODE_EOL )
+// {
+// #ifdef ONELEVELDEMO
+// gameexit(" ");
+// #endif
+// closedemowrite();
+
+// ready2send = 0;
+
+// i = ud.screen_size;
+// ud.screen_size = 0;
+// vscrn();
+// ud.screen_size = i;
+// dobonus(0);
+
+// if(ud.eog)
+// {
+// ud.eog = 0;
+// if(ud.multimode < 2)
+// {
+// #ifndef VOLUMEALL
+// doorders();
+// #endif
+// ps[myconnectindex].gm = MODE_MENU;
+// cmenu(0);
+// probey = 0;
+// goto MAIN_LOOP_RESTART;
+// }
+// else
+// {
+// ud.m_level_number = 0;
+// ud.level_number = 0;
+// }
+// }
+// }
+
+// ready2send = 0;
+// if(numplayers > 1) ps[myconnectindex].gm = MODE_GAME;
+// enterlevel(ps[myconnectindex].gm);
+// continue;
+// }
+
+// cheats();
+// nonsharedkeys();
+
+// if( (ud.show_help == 0 && ud.multimode < 2 && !(ps[myconnectindex].gm&MODE_MENU) ) || ud.multimode > 1 || ud.recstat == 2)
+// i = min(max((totalclock-ototalclock)*(65536L/TICSPERFRAME),0),65536);
+// else
+// i = 65536;
+
+// displayrooms(screenpeek,i);
+// displayrest(i);
+
+// // if( KB_KeyPressed(sc_F) )
+// // {
+// // KB_ClearKeyDown(sc_F);
+// // addplayer();
+// // }
+
+// if(ps[myconnectindex].gm&MODE_DEMO)
+// goto MAIN_LOOP_RESTART;
+
+// if(debug_on) caches();
+
+// checksync();
+
+// #ifdef VOLUMEONE
+// if(ud.show_help == 0 && show_shareware > 0 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
+// rotatesprite((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128,0,0,xdim-1,ydim-1);
+// #endif
+// nextpage();
+// }
+
+// gameexit(" ");
}