diff --git a/.gitignore b/.gitignore index 3636ff0..34de845 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /target /WADS/* +!/WADS/DOOM1.wad \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 9e833a1..406f49b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,7 @@ } }, "args": [ - "./WADS/DOOM.WAD", "-dev", "@configs.txt", "-nomonsters", "-file", "test.WAD", "test2.WAD", "-skill", "3" + "./WADS/DOOM.WAD", "-dev", "-nomonsters", "-skill", "3" ], "cwd": "${workspaceFolder}" }, diff --git a/WADS/doom1.wad b/WADS/doom1.wad new file mode 100644 index 0000000..1a58f66 Binary files /dev/null and b/WADS/doom1.wad differ diff --git a/src/d_main/mod.rs b/src/d_main/mod.rs index d77fa75..b6c2e37 100644 --- a/src/d_main/mod.rs +++ b/src/d_main/mod.rs @@ -9,6 +9,8 @@ use crate::m_argv::M_CheckParm; use crate::m_argv::M_GetOptionalArgumentValueByArgument; use crate::m_argv::PARM_NOT_FOUND; +use crate::w_wad::{W_CheckNumForName, W_InitMultipleFiles}; + use crate::doomdef::{ VERSION, // D_DEVSTR, @@ -407,10 +409,10 @@ pub fn D_DoomMain() { println!("Z_init: Init zone memory allocation daemon."); Z_Init(); - - println!("W_Init: Init WADfiles"); - W_InitMultipleFiles(wadfiles); + */ + println!("W_Init: Init WADfiles"); + W_InitMultipleFiles(DOOMGLOBALS::with_ref(|g| g.wadfiles.clone())); // we are loading so how cares about a borrow let is_modified = DOOMGLOBALS::with_ref(|g| g.modifiedgame); @@ -426,12 +428,13 @@ pub fn D_DoomMain() { } if g_mode == GameMode::Registered { - for lump in iwads_lumps_to_check.iter().take(24) { - // TODO: Implement check - // for (i = 0;i < 23; i++) - // if (W_CheckNumForName(name[i])<0) - // I_Error("\nThis is not the registered version."); - } + let missing_registered_lump = iwads_lumps_to_check + .iter() + .any(|lump_name| W_CheckNumForName(lump_name) == -1); + if missing_registered_lump { + // TODO: Implement I_Error + // I_Error("\nThis is not the registered version."); + } } } @@ -541,7 +544,7 @@ pub fn D_DoomMain() { } - let g_action = DOOMGLOBALS::with_ref(|g| g.gameaction); + // let g_action = DOOMGLOBALS::with_ref(|g| g.gameaction); // TODO: Impement Game actions // if g_action != ga_loadgame { diff --git a/src/doomdef.rs b/src/doomdef.rs index 5374cc9..84a07d7 100644 --- a/src/doomdef.rs +++ b/src/doomdef.rs @@ -1,10 +1,9 @@ use std::cell::RefCell; -use std::sync::{Mutex, MutexGuard, OnceLock}; - pub const VERSION:i32 = 110; pub const D_DEVSTR: &str = "Development mode ON."; + #[derive(Debug, Clone, Copy, PartialEq)] pub enum GameMode { Shareware, diff --git a/src/main.rs b/src/main.rs index 903d705..f678a46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod d_main; -mod m_argv; mod doomdef; +mod m_argv; +mod w_wad; mod math; diff --git a/src/w_wad/mod.rs b/src/w_wad/mod.rs new file mode 100644 index 0000000..228911d --- /dev/null +++ b/src/w_wad/mod.rs @@ -0,0 +1,235 @@ +use std::fs::File; +use std::io::{Read, Seek, SeekFrom}; +use std::mem::size_of; +use std::os::windows::io::AsRawHandle; +use std::path::Path; + +#[allow(non_upper_case_globals)] +static mut numlumps: i32 = 0; + +#[allow(non_upper_case_globals)] +static mut lumpinfo: Vec = Vec::new(); + +#[allow(non_upper_case_globals)] +static mut reloadlump: i32 = 0; + +#[allow(non_upper_case_globals)] +static mut reloadname: String = String::new(); + +/// Struct of the header of a WAD file +/// All integers are 4 bytes long in x86-style little-endian order. Their values can never exceed 231-1 +#[repr(C)] +pub struct wadinfo_t { + /// ASCII identifer, either IWAD or PWAD + pub identification: [u8; 4], + + /// The number of lumps in the WAD + pub numlumps: i32, + + /// Pointer to the location of the directory + pub infotableofs: i32, +} + +/// Struct of fileumps of a WAD file +#[repr(C)] +#[derive(Clone)] +pub struct filelump_t { + + /// Pointer to the start of the lumps data in the WAD file + pub filepos: i32, + + /// The size of the lump in bytes + pub size: i32, + + /// ASCII string identifying the lumps name + /// must be null terminated if less than 8 characters long + /// For safety, padd till end with null chars + pub name: [u8; 8], +} + +/// TODO: RD-2: fill in if it is even needed for this port?? +#[repr(C)] +#[derive(Clone)] +pub struct lumpinfo_t { + + pub name: [u8; 8], + + pub handle: isize, + + pub position: i32, + + pub size: i32 +} + +#[allow(non_snake_case)] +fn W_AddFile(file: &str) { + // Get reference to static vvariables to be used locally + let (lumpinfo_ref, reloadname_ref) = unsafe { + ( + &mut *std::ptr::addr_of_mut!(lumpinfo), + &mut *std::ptr::addr_of_mut!(reloadname) + ) + }; + + if file.as_bytes()[0] == '~' as u8 { + unsafe { + reloadname = file.chars().skip(1).collect(); + reloadlump = numlumps; + } + } + + let mut fileinfo: Vec = Vec::new(); + + let mut handle: File = match std::fs::File::open(&file) { + Ok(file) => file, + Err(_) => { + println!(" couldn't open {}", file); + return; + } + }; + println!("adding {}",file); + + if !file.to_ascii_lowercase().ends_with(".wad") { + // process single lump + let file_meta = handle.metadata().unwrap(); + // Single lump file + let mut singleinfo = filelump_t { + filepos: 0, + size: file_meta.len() as i32, + name: [0; 8] + }; + let name = Path::new(file) + .file_name() + .and_then(|os_str| os_str.to_str()).unwrap(); + for (i, byte) in name.as_bytes().iter().take(8).enumerate() { + singleinfo.name[i] = *byte; + } + fileinfo.push(singleinfo); + unsafe {numlumps += 1}; + } else { + // WAD file + let mut header: wadinfo_t = wadinfo_t { + identification: [0; 4], + numlumps: 0, + infotableofs: 0 + }; + handle.read_exact(&mut header.identification).unwrap(); + let mut int_buf:[u8; 4] = [0u8; 4]; + handle.read_exact(&mut int_buf).unwrap(); + header.numlumps = i32::from_le_bytes(int_buf); + handle.read_exact(&mut int_buf).unwrap(); + header.infotableofs = i32::from_le_bytes(int_buf); + + if !header.identification.eq_ignore_ascii_case(b"IWAD") { + if !header.identification.eq_ignore_ascii_case(b"PWAD") { + // TODO: Implement I_Error + // I_Error ("Wad file %s doesn't have IWAD or PWAD id\n", file); + } + } + + let _ = handle.seek(SeekFrom::Start(header.infotableofs as u64)); + fileinfo.resize(header.numlumps as usize, filelump_t { + filepos: 0, + size: 0, + name: [0; 8], + }); + let dirinfo_bytes = header.numlumps as usize * size_of::(); + unsafe { + let fileinfo_slice = std::slice::from_raw_parts_mut( + fileinfo.as_mut_ptr() as *mut u8, + dirinfo_bytes, + ); + handle.read_exact(fileinfo_slice).unwrap(); + } + + // enusre LE-ness of the ints + for fl in &mut fileinfo { + fl.filepos = i32::from_le(fl.filepos); + fl.size = i32::from_le(fl.size); + } + + unsafe{numlumps += header.numlumps} + }; + + let is_reloaded = !reloadname_ref.is_empty(); + let storehandle: isize = if is_reloaded { + -1 // Original C code closes the file immediately if it's a reloaded file + } else { + // get raw Windows HANDLE as an integer pointer + let raw_handle = handle.as_raw_handle() as isize; + // Tells rust to "trust us" and keeps the handle open after this function scope + std::mem::forget(handle); + raw_handle + }; + + + lumpinfo_ref.reserve(fileinfo.len()); + for info in fileinfo { + lumpinfo_ref.push( lumpinfo_t { + handle: storehandle, + position: info.filepos, + size: info.size, + name: info.name + }) + } +} + + +#[allow(non_snake_case)] +pub fn W_InitMultipleFiles(filenames: Vec) { + + for file in filenames { + W_AddFile(&file); + } + + if unsafe{numlumps == 0} { + // TODO: Implement I_Error + // I_Error("W_InitFiles: no files found") + } + + // set up lump cache +} + +#[allow(non_snake_case)] +pub fn W_Reload() { + +} + +#[allow(non_snake_case)] +pub fn W_CheckNumForName(name: &str) -> i32 { + let lumpinfo_ref = unsafe { &*std::ptr::addr_of!(lumpinfo) }; + + let mut search_name = [0u8; 8]; + for (i, byte) in name.as_bytes().iter().take(8).enumerate() { + search_name[i] = byte.to_ascii_uppercase(); + } + + // 2. Scan backwards so PWAD patches take precedence over IWAD assets + lumpinfo_ref + .iter() + .enumerate() + .rev() // Iterates backward from the end + .find(|(_, lump)| lump.name == search_name) + .map(|(index, _)| index as i32) // If found, return the index + .unwrap_or(-1) // "TFB. Not found." -> return -1 +} + +#[allow(non_snake_case)] +pub fn W_LumpLength() { + +} + +#[allow(non_snake_case)] +pub fn W_ReadLump() { + +} + +#[allow(non_snake_case)] +pub fn W_CacheLumpNum() { + +} + +#[allow(non_snake_case)] +pub fn W_CacheLumpName() { + +} \ No newline at end of file