diff options
| -rw-r--r-- | src/fmt.rs | 220 | ||||
| -rw-r--r-- | src/main.rs | 235 |
2 files changed, 104 insertions, 351 deletions
@@ -10,49 +10,6 @@ use std::path::Path; use self::byteorder::{ByteOrder, LittleEndian}; - -// FIXME: This doesn't follow any non-*NIX conventions. - -/// Means of locating a group file that may be located in any number of file -/// system locations. Begins by searching the current working directory, and -/// then moves onto standard *NIX home directory locations, such as -/// '~/.rebuild'. - -fn find_file(filename: &str) -> Option<String> { - // Initial base paths that don't need to be expanded. - let directories = vec!["./"]; - - let mut directories: Vec<String> = directories.iter() - .map(|s| String::from(*s)) - .collect(); - - match env::home_dir() { - Some(path) => { - match path.to_str() { - Some(path) => { - let mut path = String::from(path); - path.push_str("/.rebuild/"); - directories.push(path); - } - None => (), - } - } - None => (), - } - - for root in directories.iter() { - let mut path = root.clone(); - path.push_str(filename); - - if Path::new(&path).exists() { - return Some(path); - } - } - - None -} - - // 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 // contains my name, "KenSilverman". The next 4 bytes is the number of files that @@ -61,19 +18,25 @@ fn find_file(filename: &str) -> Option<String> { // the file's size. The rest of the group file is just the raw data packed one // after the other in the same order as the list of files. - /// Implementation of a group file "cache", into which the contents of various /// group files are loaded. This is only somewhat reminiscent of the way /// Silverman's original code goes about loading game data. - #[derive(Debug)] pub struct GroupManager { files: HashMap<String, Vec<u8>>, + search: Vec<String>, } impl GroupManager { pub fn new() -> GroupManager { - GroupManager { files: HashMap::new() } + let mut result = GroupManager { + files: HashMap::new(), + search: Vec::new() + }; + + result.init_search_paths(); + + result } /// Loads the contents of an in-memory group file into the cache. @@ -127,29 +90,112 @@ impl GroupManager { Ok(()) } + /// Obtains binary data associated with the given filename from the cache. + pub fn get(&self, filename: &str) -> Option<&[u8]> { + Some(&self.files.get(filename)?) + } + + // FIXME: Documentation needs to be rewritten. + /// Loads the contents of an on-disk group file into the cache. pub fn load_file(&mut self, filename: &str) -> Result<(), Box<Error>> { - let filename = match find_file(filename) { - Some(filename) => filename, - None => bail!("File not found in any search paths."), - }; + for directory in self.search.clone().iter() { + let path = format!("{}/{}", directory, filename); + + if Path::new(&path).exists() { + let mut file = File::open(path)?; + let mut bytes: Vec<u8> = Vec::new(); + + file.read_to_end(&mut bytes)?; + self.load_data(&bytes)?; + + return Ok(()); + } + } - let mut file = File::open(filename)?; - let mut bytes: Vec<u8> = Vec::new(); + bail!("File not found in any search paths.") + } - file.read_to_end(&mut bytes)?; - self.load_data(&bytes)?; + pub fn add_search_path(&mut self, path: &str) -> Result<(), Box<Error>> { + if Path::new(&path).exists() { + self.search.push(String::from(path)); + } else { + bail!("Path does not exist"); + } Ok(()) } - /// Obtains binary data associated with the given filename from the cache. - pub fn get(&self, filename: &str) -> Option<&[u8]> { - Some(&self.files.get(filename)?) + // TODO: Conventional paths for OSX and Windows from EDuke32's + // G_AddSearchPaths. + fn init_search_paths(&mut self) { + // Initial base paths that don't need a $HOME expansion. + let directories = vec![ + ".", + "/usr/share/games/jfduke3d", + "/usr/local/share/games/jfduke3d", + "/usr/share/games/eduke32", + "/usr/local/share/games/eduke32", + "/usr/share/games/rebuild", + "/usr/local/share/games/rebuild", + ]; + + for directory in directories.iter() { + self.add_search_path(directory).ok(); + } + + // TODO: Steam paths. + let directories = vec![ + "$HOME/.rebuild", + ]; + + if let Some(home) = env::home_dir() { + if let Some(home) = home.to_str() { + for path in directories.iter() { + let path = String::from(*path).replace("$HOME", home); + self.add_search_path(&path).ok(); + } + } + } } } +// What's the .MAP / .ART file format? +// +// Go to my Build Source Code Page and download BUILDSRC.ZIP. I have a text file +// in there (BUILDINF.TXT) which describes both formats. + +// TODO: Write documentation +// pub struct Art { + +// } + + +// impl Art { +// pub fn new(data: &[u8]) -> Result<Art, Box<Error>> { +// let len = data.len(); +// let version = LittleEndian::read_u32(&data[0..4]); + +// let _tile_count = LittleEndian::read_u32(&data[4..8]); +// let first_tile = LittleEndian::read_u32(&data[8..12]); +// let last_tile = LittleEndian::read_u32(&data[12..16]); +// let tile_count = last_tile - first_tile + 1; // + 1? + +// let tiles_x: Vec<u16> = Vec::new(); +// let tiles_y: Vec<u16> = Vec::new(); +// let tiles_animation: Vec<u32> = Vec::new(); + +// // short tilesizx[localtileend-localtilestart+1]; +// // short tilesizy[localtileend-localtilestart+1]; + +// if version != 1 { +// bail!("Invalid ART version"); +// } +// } +// } + + #[cfg(test)] mod tests { use super::*; @@ -273,63 +319,5 @@ mod tests { } // TODO: Add checks for data_off and table_off going out of bounds. -} - -// What's the .MAP / .ART file format? -// -// Go to my Build Source Code Page and download BUILDSRC.ZIP. I have a text file -// in there (BUILDINF.TXT) which describes both formats. - - -// What's the PALETTE.DAT format? -// -// See this separate PALETTE.TXT <http://advsys.net/ken/palette.txt> file which -// explains it all. - - -// What's the TABLES.DAT format ? -// -// See this separate TABLES.TXT <http://advsys.net/ken/tables.txt> file which -// explains it all. - - -// What's the .KVX file format? -// -// Go to my Projects Page <http://advsys.net/ken/download.htm?#slab6> and -// download SLAB6.ZIP. I have a text file in there (SLAB6.TXT) which describes -// the format. - - -// What's the .VOX file format? -// -// Both SLABSPRI & SLAB6 support a simpler, uncompressed voxel format using the -// .VOX file extension. (See the documentation that comes with those programs.) -// The .VOX format is simple enough to fit a description of it right here. -// Here's some C pseudocode: -// -// long xsiz, ysiz, zsiz; -// char voxel[xsiz][ysiz][zsiz]; -// char palette[256][3]; -// -// fil = open("?.vox",...); -// read(fil,&xsiz,4); -// read(fil,&ysiz,4); -// read(fil,&zsiz,4); -// read(fil,voxel,xsiz*ysiz*zsiz); -// read(fil,palette,768); -// close(fil); -// -// In the voxel array, use color 255 to define your empty space (air). For -// interior voxels (ones you can never see), do not use color 255, because it -// will prevent SLABSPRI from being able to take advantage of back-face culling. - - -// How does SLABSPRI convert images to voxels? -// -// It starts out with a solid cube. Then it runs through all of the rotations, -// chopping out any voxels that lie behind a transparent pixel (color 255). Once -// this is done, it runs through all the rotations again, this time painting -// colors onto the voxel object. If an individual cube is painted twice, the -// colors get averaged. Voxels that don't get hit by paint get randomly set to a -// nearby color. +}// TODO: Write documentation diff --git a/src/main.rs b/src/main.rs index 95925e0..a25083e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,36 +5,7 @@ use std::process; mod fmt; -// 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() { - // #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); - - // initgroupfile("duke3d.grp"); - let filename = "DUKE3D.GRP"; let mut group_manager = fmt::GroupManager::new(); @@ -44,210 +15,4 @@ fn main() { } println!("DOGWHINE.VOC: {} bytes", group_manager.get("DOGWHINE.VOC").unwrap().len()); - - // checkcommandline(argc,argv); - - 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(" "); } |