summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakob L. Kreuze <jakob@memeware.net>2018-05-29 14:20:24 -0400
committerJakob L. Kreuze <jakob@memeware.net>2018-05-29 14:20:24 -0400
commitb1b852e6a648956170b25d90cb16c79d9fa50ff9 (patch)
tree8fbe002431d00c1cd75318ad95e41de78b5bc05a /src
parent8f109356234421cbf22c14976be363ef3d08c4ee (diff)
Moved path resolution into GroupManager
Diffstat (limited to 'src')
-rw-r--r--src/fmt.rs220
-rw-r--r--src/main.rs235
2 files changed, 104 insertions, 351 deletions
diff --git a/src/fmt.rs b/src/fmt.rs
index 5b38f85..67b3fbf 100644
--- a/src/fmt.rs
+++ b/src/fmt.rs
@@ -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(" ");
}