Compare commits

...

8 Commits

Author SHA1 Message Date
728599e033 Merge pull request 'Seg Reader' (#8) from segs-reader into main
Reviewed-on: #8
2025-04-08 19:49:24 -04:00
2e2c500155 Seg Reader
Added the ability to read Seg lumps from the WAD file.
2025-04-08 19:47:15 -04:00
81fd194938 Merge pull request 'Thing Reader' (#7) from thing-reader into main
Reviewed-on: #7
2025-04-07 19:14:11 -04:00
569265f414 Thing Reader
Reads things from the WAD file.
2025-04-07 19:09:33 -04:00
76358caaff Merge pull request 'Sidedef Reader' (#6) from sidedef-reader into main
Reviewed-on: #6
2025-03-31 20:24:19 -04:00
fdb68ce0ff Sidedef Reader
Added the ability to read sidedef lumps from the wadfile.
2025-03-31 20:20:52 -04:00
a4fc4b89f8 Merge pull request 'WAD Refactor' (#5) from wad-refactor into main
Reviewed-on: #5
2025-03-31 19:33:15 -04:00
fb802a8d2b WAD Refactor
Refactored some of the code for the wadfile reading. Moved out the
readers into their own separate file, and moved some utilities around to
create wad utilities for more modularity.
2025-03-31 19:30:26 -04:00
17 changed files with 417 additions and 89 deletions

View File

@@ -11,4 +11,7 @@ pub enum LumpType {
Unknown,
Vertex,
Linedef,
Sidedef,
Thing,
Seg,
}

View File

@@ -1,8 +1,13 @@
mod linedef;
mod lump;
mod seg;
mod sidedef;
mod thing;
mod vertex;
pub use linedef::LinedefLump;
pub use lump::Lump;
pub use lump::LumpType;
pub use lump::{Lump, LumpType};
pub use seg::SegLump;
pub use sidedef::SidedefLump;
pub use thing::ThingLump;
pub use vertex::VertexLump;

19
src/lumps/seg.rs Normal file
View File

@@ -0,0 +1,19 @@
use crate::types::Seg;
pub struct SegLump {
pub segs: Vec<Seg>,
}
impl SegLump {
pub fn get_num_segs(&self) -> usize {
self.segs.len()
}
pub fn get_all_segs(&self) -> Vec<Seg> {
self.segs.to_vec()
}
pub fn get_seg(&self, pos: usize) -> Seg {
self.segs[pos].to_owned()
}
}

19
src/lumps/sidedef.rs Normal file
View File

@@ -0,0 +1,19 @@
use crate::types::Sidedef;
pub struct SidedefLump {
pub sidedefs: Vec<Sidedef>,
}
impl SidedefLump {
pub fn get_num_sidedefs(&self) -> usize {
self.sidedefs.len()
}
pub fn get_all_sidedefs(&self) -> Vec<Sidedef> {
self.sidedefs.to_vec()
}
pub fn get_sidedef(&self, pos: usize) -> Sidedef {
self.sidedefs[pos].to_owned()
}
}

19
src/lumps/thing.rs Normal file
View File

@@ -0,0 +1,19 @@
use crate::types::Thing;
pub struct ThingLump {
pub things: Vec<Thing>,
}
impl ThingLump {
pub fn get_num_things(&self) -> usize {
self.things.len()
}
pub fn get_all_things(&self) -> Vec<Thing> {
self.things.to_vec()
}
pub fn get_thing(&self, pos: usize) -> Thing {
self.things[pos].to_owned()
}
}

View File

@@ -1,4 +1,4 @@
use crate::utils::*;
use crate::wad::wadfile::validate_wad;
#[test]
pub fn valid_wad() {

View File

@@ -147,3 +147,140 @@ pub fn get_num_linedefs() {
assert_eq!(linedef_lump.get_num_linedefs(), 475);
}
#[test]
pub fn read_first_sidedef() {
use crate::types::Sidedef;
let wad_file = get_wad();
let sidedef_lump = wad_file.get_sidedef_lump(&wad_file.directory[9]);
let correct_sidedef = Sidedef {
x_offset: 0,
y_offset: 0,
upper_texture: String::from("-"),
lower_texture: String::from("-"),
middle_texture: String::from("DOOR3"),
sector: 40,
};
assert_eq!(sidedef_lump.get_sidedef(0), correct_sidedef);
}
#[test]
pub fn get_num_sidedef_lumps() {
use crate::lumps::{Lump, LumpType};
let mut sidedef_lumps: Vec<Lump> = Vec::new();
let wad_file = get_wad();
for entry in 0..wad_file.num_lumps as usize {
if let LumpType::Sidedef = wad_file.directory[entry].lump_type {
sidedef_lumps.push(wad_file.directory[entry].to_owned());
}
}
assert_eq!(sidedef_lumps.len(), 9);
}
#[test]
pub fn get_num_sidedefs() {
let wad_file = get_wad();
let sidedef_lump = wad_file.get_sidedef_lump(&wad_file.directory[9]);
assert_eq!(sidedef_lump.get_num_sidedefs(), 648);
}
#[test]
pub fn read_first_thing() {
use crate::types::Thing;
let wad_file = get_wad();
let thing_lump = wad_file.get_thing_lump(&wad_file.directory[7]);
let correct_thing = Thing {
x_position: 1056,
y_position: -3616,
angle: 90,
thing_type: 1,
flags: 7,
};
assert_eq!(thing_lump.get_thing(0), correct_thing);
}
#[test]
pub fn get_num_thing_lumps() {
use crate::lumps::{Lump, LumpType};
let mut thing_lumps: Vec<Lump> = Vec::new();
let wad_file = get_wad();
for entry in 0..wad_file.num_lumps as usize {
if let LumpType::Thing = wad_file.directory[entry].lump_type {
thing_lumps.push(wad_file.directory[entry].to_owned());
}
}
assert_eq!(thing_lumps.len(), 9);
}
#[test]
pub fn get_num_things() {
let wad_file = get_wad();
let thing_lump = wad_file.get_thing_lump(&wad_file.directory[7]);
assert_eq!(thing_lump.get_num_things(), 138);
}
#[test]
pub fn read_first_seg() {
use crate::types::Seg;
let wad_file = get_wad();
let seg_lump = wad_file.get_seg_lump(&wad_file.directory[11]);
let correct_seg = Seg {
start_vertex: 123,
end_vertex: 124,
angle: 16384,
linedef: 152,
direction: 0,
offset: 0,
};
assert_eq!(seg_lump.get_seg(0), correct_seg);
}
#[test]
pub fn get_num_seg_lumps() {
use crate::lumps::{Lump, LumpType};
let mut seg_lumps: Vec<Lump> = Vec::new();
let wad_file = get_wad();
for entry in 0..wad_file.num_lumps as usize {
if let LumpType::Seg = wad_file.directory[entry].lump_type {
seg_lumps.push(wad_file.directory[entry].to_owned());
}
}
assert_eq!(seg_lumps.len(), 9);
}
#[test]
pub fn get_num_segs() {
let wad_file = get_wad();
let thing_lump = wad_file.get_seg_lump(&wad_file.directory[11]);
assert_eq!(thing_lump.get_num_segs(), 732);
}

View File

@@ -1,5 +1,11 @@
mod linedef;
mod seg;
mod sidedef;
mod thing;
mod vertex;
pub use linedef::Linedef;
pub use seg::Seg;
pub use sidedef::Sidedef;
pub use thing::Thing;
pub use vertex::Vertex;

9
src/types/seg.rs Normal file
View File

@@ -0,0 +1,9 @@
#[derive(Clone, Debug, PartialEq)]
pub struct Seg {
pub start_vertex: i16,
pub end_vertex: i16,
pub angle: i16,
pub linedef: i16,
pub direction: i16,
pub offset: i16,
}

9
src/types/sidedef.rs Normal file
View File

@@ -0,0 +1,9 @@
#[derive(Clone, Debug, PartialEq)]
pub struct Sidedef {
pub x_offset: i16,
pub y_offset: i16,
pub upper_texture: String,
pub lower_texture: String,
pub middle_texture: String,
pub sector: i16,
}

8
src/types/thing.rs Normal file
View File

@@ -0,0 +1,8 @@
#[derive(Clone, Debug, PartialEq)]
pub struct Thing {
pub x_position: i16,
pub y_position: i16,
pub angle: i16,
pub thing_type: i16,
pub flags: i16,
}

View File

@@ -1,32 +1,3 @@
use std::{
fs::File,
io::{self, Read},
path::Path,
};
/// Validates a WAD file to make sure that it is a legitimate file
///
/// Parameters:
/// - path: &str - Path to the WAD to validate
pub fn validate_wad(path: &str) -> io::Result<bool> {
let wad_file = Path::new(path);
// Check to see if the WAD exists
if !(wad_file.exists()) {
// Return back false because we didn't pass a valid file
return Ok(false);
}
// If the file exists open it and read the first 4 bytes
// of the file and see if we get "IWAD" or "PWAD"
let mut file = File::open(wad_file)?;
let mut magic = [0u8; 4];
file.read_exact(&mut magic)?;
// Now we return based on what we found
Ok(magic == *b"IWAD" || magic == *b"PWAD")
}
pub fn read_ascii(bytes: &[u8]) -> String {
std::str::from_utf8(&bytes[..bytes.len()])
.unwrap_or("")

View File

@@ -4,4 +4,3 @@ mod helpers;
pub use helpers::read_ascii;
pub use helpers::read_i16_le;
pub use helpers::read_u32_le;
pub use helpers::validate_wad;

View File

@@ -1,4 +1,4 @@
mod header;
mod wadfile;
pub mod wadfile;
pub use wadfile::WADFile;

View File

@@ -1,9 +1,13 @@
mod readers;
mod utils;
pub use utils::validate_wad;
use crate::lumps::{Lump, LumpType};
use std::{fs::File, io::Read, path::Path};
use super::header::Header;
use crate::lumps::{LinedefLump, Lump, LumpType, VertexLump};
use crate::types::{Linedef, Vertex};
use crate::utils::{read_ascii, read_i16_le, read_u32_le, validate_wad};
use crate::utils::{read_ascii, read_i16_le, read_u32_le};
pub struct WADFile {
pub path: String,
@@ -40,6 +44,9 @@ impl WADFile {
let lump_type = match name.as_str() {
"VERTEXES" => LumpType::Vertex,
"LINEDEFS" => LumpType::Linedef,
"SIDEDEFS" => LumpType::Sidedef,
"THINGS" => LumpType::Thing,
"SEGS" => LumpType::Seg,
_ => LumpType::Unknown,
};
lump_dir.push(Lump {
@@ -59,56 +66,4 @@ impl WADFile {
data: file_buffer,
}
}
pub fn get_vertex_lump(&self, lump: &Lump) -> VertexLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entries: usize = lump_size / 4;
let mut vertexes: Vec<Vertex> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = entry * 4;
let vertex_x = read_i16_le(&lump_data[startpos..startpos + 2]);
let vertex_y = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
vertexes.push(Vertex {
x: vertex_x,
y: vertex_y,
});
}
VertexLump { vertexes }
}
pub fn get_linedef_lump(&self, lump: &Lump) -> LinedefLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entries = lump_size / 14;
let mut linedefs: Vec<Linedef> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = entry * 14;
let vertex1 = read_i16_le(&lump_data[startpos..startpos + 2]);
let vertex2 = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
let flags = read_i16_le(&lump_data[startpos + 4..startpos + 6]);
let special = read_i16_le(&lump_data[startpos + 6..startpos + 8]);
let tag = read_i16_le(&lump_data[startpos + 8..startpos + 10]);
let front_sidedef = read_i16_le(&lump_data[startpos + 10..startpos + 12]);
let back_sidedef = read_i16_le(&lump_data[startpos + 12..startpos + 14]);
linedefs.push(Linedef {
vertex1,
vertex2,
flags,
special,
tag,
front_sidedef,
back_sidedef,
});
}
LinedefLump { linedefs }
}
}

143
src/wad/wadfile/readers.rs Normal file
View File

@@ -0,0 +1,143 @@
use crate::lumps::{LinedefLump, Lump, SegLump, SidedefLump, ThingLump, VertexLump};
use crate::types::{Linedef, Seg, Sidedef, Thing, Vertex};
use crate::utils::read_ascii;
use crate::wad::WADFile;
use crate::wad::wadfile::read_i16_le;
impl WADFile {
pub fn get_vertex_lump(&self, lump: &Lump) -> VertexLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entries: usize = lump_size / 4;
let mut vertexes: Vec<Vertex> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = entry * 4;
let vertex_x = read_i16_le(&lump_data[startpos..startpos + 2]);
let vertex_y = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
vertexes.push(Vertex {
x: vertex_x,
y: vertex_y,
});
}
VertexLump { vertexes }
}
pub fn get_linedef_lump(&self, lump: &Lump) -> LinedefLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entries = lump_size / 14;
let mut linedefs: Vec<Linedef> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = entry * 14;
let vertex1 = read_i16_le(&lump_data[startpos..startpos + 2]);
let vertex2 = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
let flags = read_i16_le(&lump_data[startpos + 4..startpos + 6]);
let special = read_i16_le(&lump_data[startpos + 6..startpos + 8]);
let tag = read_i16_le(&lump_data[startpos + 8..startpos + 10]);
let front_sidedef = read_i16_le(&lump_data[startpos + 10..startpos + 12]);
let back_sidedef = read_i16_le(&lump_data[startpos + 12..startpos + 14]);
linedefs.push(Linedef {
vertex1,
vertex2,
flags,
special,
tag,
front_sidedef,
back_sidedef,
});
}
LinedefLump { linedefs }
}
pub fn get_sidedef_lump(&self, lump: &Lump) -> SidedefLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entires = lump_size / 30;
let mut sidedefs: Vec<Sidedef> = Vec::with_capacity(lump_entires);
for entry in 0..lump_entires {
let startpos = entry * 30;
let x_offset = read_i16_le(&lump_data[startpos..startpos + 2]);
let y_offset = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
let upper_texture = read_ascii(&lump_data[startpos + 4..startpos + 12]);
let lower_texture = read_ascii(&lump_data[startpos + 12..startpos + 20]);
let middle_texture = read_ascii(&lump_data[startpos + 20..startpos + 28]);
let sector = read_i16_le(&lump_data[startpos + 28..startpos + 30]);
sidedefs.push(Sidedef {
x_offset,
y_offset,
upper_texture,
lower_texture,
middle_texture,
sector,
});
}
SidedefLump { sidedefs }
}
pub fn get_thing_lump(&self, lump: &Lump) -> ThingLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_data = &self.data[lump_offset..lump_offset + lump_size];
let lump_entries = lump_size / 10;
let mut things: Vec<Thing> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = entry * 10;
let x_position = read_i16_le(&lump_data[startpos..startpos + 2]);
let y_position = read_i16_le(&lump_data[startpos + 2..startpos + 4]);
let angle = read_i16_le(&lump_data[startpos + 4..startpos + 6]);
let thing_type = read_i16_le(&lump_data[startpos + 6..startpos + 8]);
let flags = read_i16_le(&lump_data[startpos + 8..startpos + 10]);
things.push(Thing {
x_position,
y_position,
angle,
thing_type,
flags,
});
}
ThingLump { things }
}
pub fn get_seg_lump(&self, lump: &Lump) -> SegLump {
let lump_offset = lump.offset as usize;
let lump_size = lump.size as usize;
let lump_entries = lump_size / 12;
let mut segs: Vec<Seg> = Vec::with_capacity(lump_entries);
for entry in 0..lump_entries {
let startpos = (entry * 12) + lump_offset;
let start_vertex = read_i16_le(&self.data[startpos..startpos + 2]);
let end_vertex = read_i16_le(&self.data[startpos + 2..startpos + 4]);
let angle = read_i16_le(&self.data[startpos + 4..startpos + 6]);
let linedef = read_i16_le(&self.data[startpos + 6..startpos + 8]);
let direction = read_i16_le(&self.data[startpos + 8..startpos + 10]);
let offset = read_i16_le(&self.data[startpos + 10..startpos + 12]);
segs.push(Seg {
start_vertex,
end_vertex,
angle,
linedef,
direction,
offset,
});
}
SegLump { segs }
}
}

26
src/wad/wadfile/utils.rs Normal file
View File

@@ -0,0 +1,26 @@
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
/// Validates a WAD file to make sure that it is a legitimate file
///
/// Parameters:
/// - path: &str - Path to the WAD to validate
pub fn validate_wad(path: &str) -> io::Result<bool> {
let wad_file = Path::new(path);
// Check to see if the WAD exists
if !(wad_file.exists()) {
// Return back false because we didn't pass a valid file
return Ok(false);
}
// If the file exists open it and read the first 4 bytes
// of the file and see if we get "IWAD" or "PWAD"
let mut file = File::open(wad_file)?;
let mut magic = [0u8; 4];
file.read_exact(&mut magic)?;
// Now we return based on what we found
Ok(magic == *b"IWAD" || magic == *b"PWAD")
}