summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--src/art.rs120
-rw-r--r--src/defs.rs111
-rw-r--r--src/game_vars.rs34
-rw-r--r--src/grp.rs (renamed from src/fmt.rs)101
-rw-r--r--src/main.rs19
-rw-r--r--src/world.rs446
7 files changed, 574 insertions, 261 deletions
diff --git a/README.md b/README.md
index 178b89e..f7509e4 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,9 @@ lack of foresight.
# Thanks
- To the 3D Realms teams for releasing the source code for Duke Nukem 3D.
+- To Fabien Sanglard for his [detailed analysis of the Duke Nukem 3D codebase][3], and his work on Chocolate Duke3D.
- To Richard Gobeille et al. for their work on EDuke32.
-- To Mathieu Olivier for releasing the source code to his various BUILD format parsers.
+- To Mathieu Olivier et al. for their work on Transfusion - specifically their open-source parsing code for the BUILD file formats.
# Roadmap
@@ -65,3 +66,4 @@ lack of foresight.
[1]: https://blood-wiki.org/index.php/BloodGDX
[2]: http://eduke32.com/
+[3]: http://fabiensanglard.net/duke3d/index.php
diff --git a/src/art.rs b/src/art.rs
new file mode 100644
index 0000000..2e7e116
--- /dev/null
+++ b/src/art.rs
@@ -0,0 +1,120 @@
+// Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+//
+// This file is part of rebuild.
+//
+// rebuild is free software: you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// rebuild is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// rebuild. If not, see <http://www.gnu.org/licenses/>.
+
+extern crate simple_error;
+
+use std::error::Error;
+
+// What's the PALETTE.DAT format?
+//
+// char palette[768], palookup[numpalookups][256], transluc[256][256];
+// short numpalookups;
+//
+// fil = open("PALETTE.DAT",...);
+// read(fil,palette,768);
+// read(fil,&numpalookups,2);
+// read(fil,palookup,numpalookups*256);
+// read(fil,transluc,65536);
+// close(fil);
+//
+// PALETTE: This 768 byte array is exactly the palette you want. The format is:
+// Red0, Green0, Blue0, Red1, Green1, Blue1, ..., Blue255
+// The colors are based on the VGA 262,144 color palette. The values range from
+// 0-63, so if you want to convert it to a windows palette you will have to
+// multiply each byte by 4.
+//
+// NUMPALOOKUPS: The number of shading tables used. Usually this number is 32,
+// but 16 or 64 have also been used. Each of the 256 colors of the VGA palette
+// can take on any of "numpalookups" number of shades.
+//
+// PALOOKUP: The shading table. If numpalookups = 32, then this table is:
+// (32 shades) * (256 colors) = 8192 bytes (8K). The shade tables are often made
+// to go from normal brightness (shade #0) down to pitch black (shade #31) So
+// the first 256 bytes of the table would be for shade #0, etc...
+//
+// TRANSLUC: 64K translucent lookup table. Given any 2 colors of the palette,
+// this lookup table gives the best match of the 2 colors when mixed together.
+//
+// Here's a funny story: I noticed that Duke3D's PALETTE.DAT file is 8K longer
+// than it should be. Any PALETTE.DAT file with 32 shades and translucent table
+// should be 74,498 bytes. Duke3D's palette is 82,690 bytes, but it only has 32
+// shades! The reason is that at one time, Duke3D had 64 shades in their
+// "palookup" table. Then when we noticed that this extra memory overhead slowed
+// down the frame rate of the game noticably, it was converted back to 32
+// shades. The problem is that my palette conversion program never truncated off
+// the end of the file. So the last 8K of Duke3D's PALETTE.DAT is the last 8K of
+// a translucent table that was based on an older version of their palette.
+//
+//
+// For canonical parsers, see:
+// - 'paletteLoadFromDisk' in EDuke's 'build/src/palette.cpp'
+// - 'loadpalette' in Build's 'ENGINE.C'
+
+/// Parser for PALETTE.DAT, the file specifying the color format.
+pub struct Palette {
+ colors: Vec<u8>,
+}
+
+impl Palette {
+ /// Parse the contents of a PALETTE.DAT
+ pub fn new(data: &[u8]) -> Result<Palette, Box<Error>> {
+ let len = data.len();
+
+ // FIXME: This only takes into account the actual palette. PALETTE.DAT
+ // should also contain some lookup tables.
+ if len < 770 {
+ bail!("Too small to contain palette.");
+ }
+
+ let colors = data[0..768].to_vec();
+
+ // FIXME: Not loading the lookup table yet because.. well, I don't know
+ // if we really need it yet? I suppose we'll need to get the values for
+ // TRANSLUC, but we're not on DOS anymore and I think a lookup table
+ // would be overkill. My plan is to convert ART files into bitmaps ahead
+ // of time, anyway.
+
+ // let _lookup_count = LittleEndian::read_u16(size) as usize;
+
+ Ok(Palette { colors })
+ }
+}
+
+#[cfg(test)]
+mod palette_tests {
+ use super::*;
+
+ #[test]
+ fn test_load_slice() {
+ // Considering the size of PALETTE.DAT, it would be absurd embed as a
+ // blob in this file. We'll just generate dummy data. I'm leaving the
+ // number of 'pa' lookups as 0 intentionally.
+ let data = [0; 0x10301];
+
+ if let Err(e) = Palette::new(&data) {
+ panic!("Valid PALETTE errored out with '{}'", e);
+ }
+ }
+
+ #[test]
+ fn test_not_enough_data() {
+ let data = [0; 1];
+
+ if let Ok(_) = Palette::new(&data) {
+ panic!("Accepted incomplete header.");
+ }
+ }
+}
diff --git a/src/defs.rs b/src/defs.rs
deleted file mode 100644
index 47000eb..0000000
--- a/src/defs.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
-//
-// This file is part of rebuild.
-//
-// rebuild is free software: you can redistribute it and/or modify it under the
-// terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// rebuild is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// rebuild. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/game_vars.rs b/src/game_vars.rs
deleted file mode 100644
index a4538d7..0000000
--- a/src/game_vars.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
-//
-// This file is part of rebuild.
-//
-// rebuild is free software: you can redistribute it and/or modify it under the
-// terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// rebuild is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// rebuild. If not, see <http://www.gnu.org/licenses/>.
-
-use std::collections::HashMap;
-
-pub enum GameVar {
- String(string),
-}
-
-// TODO: Document this.
-// TODO: Rename?
-pub struct GameVarManager {
- vars: HashMap<String,
-}
-
-impl GameVars {
- // TODO: Document this.
- pub fn new() -> GameVars {
- GameVars { }
- }
-}
diff --git a/src/fmt.rs b/src/grp.rs
index bd34962..3bed2fc 100644
--- a/src/fmt.rs
+++ b/src/grp.rs
@@ -274,104 +274,3 @@ mod grp_tests {
}
}
}
-
-// What's the PALETTE.DAT format?
-//
-// char palette[768], palookup[numpalookups][256], transluc[256][256];
-// short numpalookups;
-//
-// fil = open("PALETTE.DAT",...);
-// read(fil,palette,768);
-// read(fil,&numpalookups,2);
-// read(fil,palookup,numpalookups*256);
-// read(fil,transluc,65536);
-// close(fil);
-//
-// PALETTE: This 768 byte array is exactly the palette you want. The format is:
-// Red0, Green0, Blue0, Red1, Green1, Blue1, ..., Blue255
-// The colors are based on the VGA 262,144 color palette. The values range from
-// 0-63, so if you want to convert it to a windows palette you will have to
-// multiply each byte by 4.
-//
-// NUMPALOOKUPS: The number of shading tables used. Usually this number is 32,
-// but 16 or 64 have also been used. Each of the 256 colors of the VGA palette
-// can take on any of "numpalookups" number of shades.
-//
-// PALOOKUP: The shading table. If numpalookups = 32, then this table is:
-// (32 shades) * (256 colors) = 8192 bytes (8K). The shade tables are often made
-// to go from normal brightness (shade #0) down to pitch black (shade #31) So
-// the first 256 bytes of the table would be for shade #0, etc...
-//
-// TRANSLUC: 64K translucent lookup table. Given any 2 colors of the palette,
-// this lookup table gives the best match of the 2 colors when mixed together.
-//
-// Here's a funny story: I noticed that Duke3D's PALETTE.DAT file is 8K longer
-// than it should be. Any PALETTE.DAT file with 32 shades and translucent table
-// should be 74,498 bytes. Duke3D's palette is 82,690 bytes, but it only has 32
-// shades! The reason is that at one time, Duke3D had 64 shades in their
-// "palookup" table. Then when we noticed that this extra memory overhead slowed
-// down the frame rate of the game noticably, it was converted back to 32
-// shades. The problem is that my palette conversion program never truncated off
-// the end of the file. So the last 8K of Duke3D's PALETTE.DAT is the last 8K of
-// a translucent table that was based on an older version of their palette.
-//
-//
-// For canonical parsers, see:
-// - 'paletteLoadFromDisk' in EDuke's 'build/src/palette.cpp'
-// - 'loadpalette' in Build's 'ENGINE.C'
-
-/// Parser for PALETTE.DAT, the file specifying the color format.
-pub struct Palette {
- colors: Vec<u8>,
-}
-
-impl Palette {
- /// Parse the contents of a PALETTE.DAT
- pub fn new(data: &[u8]) -> Result<Palette, Box<Error>> {
- let len = data.len();
-
- // FIXME: This only takes into account the actual palette. PALETTE.DAT
- // should also contain some lookup tables.
- if len < 770 {
- bail!("Too small to contain palette.");
- }
-
- let colors = data[0..768].to_vec();
-
- // FIXME: Not loading the lookup table yet because.. well, I don't know
- // if we really need it yet? I suppose we'll need to get the values for
- // TRANSLUC, but we're not on DOS anymore and I think a lookup table
- // would be overkill. My plan is to convert ART files into bitmaps ahead
- // of time, anyway.
-
- // let _lookup_count = LittleEndian::read_u16(size) as usize;
-
- Ok(Palette { colors })
- }
-}
-
-#[cfg(test)]
-mod palette_tests {
- use super::*;
-
- #[test]
- fn test_load_slice() {
- // Considering the size of PALETTE.DAT, it would be absurd embed as a
- // blob in this file. We'll just generate dummy data. I'm leaving the
- // number of 'pa' lookups as 0 intentionally.
- let data = [0; 0x10301];
-
- if let Err(e) = Palette::new(&data) {
- panic!("Valid PALETTE errored out with '{}'", e);
- }
- }
-
- #[test]
- fn test_not_enough_data() {
- let data = [0; 1];
-
- if let Ok(_) = Palette::new(&data) {
- panic!("Accepted incomplete header.");
- }
- }
-}
diff --git a/src/main.rs b/src/main.rs
index d54bfea..f12759c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,32 +17,23 @@
#[macro_use]
extern crate simple_error;
-use std::fs::File; // Temporary, used for 'extract'.
-use std::io::Write; // Temporary, used for 'extract'.
use std::process;
-mod fmt;
+mod grp;
mod path;
-
-// This function is strictly temporary and exists only to help me get samples of
-// binary files to inspect in radare.
-fn extract(name: &str, group_manager: &fmt::GroupManager) {
- let output_path = format!("/tmp/{}", name);
- let mut file = File::create(output_path).unwrap();
- file.write_all(group_manager.get(name).unwrap()).ok();
-}
+mod world;
fn main() {
let path_manager = path::PathManager::new();
let filename = "DUKE3D.GRP";
- let mut group_manager = fmt::GroupManager::new(path_manager);
+ let mut group_manager = grp::GroupManager::new(path_manager);
if let Err(e) = group_manager.load_file(filename) {
println!("Couldn't open {}: {}", filename, e);
process::exit(1);
}
- extract("PALETTE.DAT", &group_manager);
- // println!("DOGWHINE.VOC: {} bytes", group_manager.get("DOGWHINE.VOC").unwrap().len());
+ let map = group_manager.get("E1L1.MAP").unwrap();
+ let world = world::World::from_map(map);
}
diff --git a/src/world.rs b/src/world.rs
new file mode 100644
index 0000000..34a11f4
--- /dev/null
+++ b/src/world.rs
@@ -0,0 +1,446 @@
+// Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+//
+// This file is part of rebuild.
+//
+// rebuild is free software: you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// rebuild is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// rebuild. If not, see <http://www.gnu.org/licenses/>.
+
+// FIXME: Signedness of 'char' is ambiguous from documentation.
+
+extern crate byteorder;
+extern crate simple_error;
+
+use std::error::Error;
+use std::io::Cursor;
+
+use self::byteorder::{LE, ReadBytesExt};
+
+/// Maintains the current state of the game world - the map geometry and
+/// everything contained within it.
+#[derive(Debug)]
+pub struct World {
+ sectors: Vec<Sector>,
+ walls: Vec<Wall>,
+}
+
+impl World {
+ // TODO: Document this.
+ // FIXME: Doesn't do any sort of sanity checks on length.
+ pub fn from_map(data: &[u8]) -> Result<World, Box<Error>> {
+ let mut data = Cursor::new(data);
+
+ // From BUILDINF.TXT
+ //
+ // Here is Ken's documentation on the COMPLETE BUILD map format:
+ // BUILD engine and editor programmed completely by Ken Silverman
+ //
+ // Here's how you should read a BUILD map file:
+ // {
+ // fil = open(???);
+ //
+ // // Load map version number (current version is 7L)
+ // read(fil,&mapversion,4);
+ //
+ // // Load starting position
+ // read(fil,posx,4);
+ // read(fil,posy,4);
+ // read(fil,posz,4); // Note: Z coordinates are all shifted up 4
+ // read(fil,ang,2); // All angles are from 0-2047, clockwise
+ // read(fil,cursectnum,2); // Sector of starting point
+ //
+ // // Load all sectors (see sector structure described below)
+ // read(fil,&numsectors,2);
+ // read(fil,&sector[0],sizeof(sectortype)*numsectors);
+ //
+ // // Load all walls (see wall structure described below)
+ // read(fil,&numwalls,2);
+ // read(fil,&wall[0],sizeof(walltype)*numwalls);
+ //
+ // // Load all sprites (see sprite structure described below)
+ // read(fil,&numsprites,2);
+ // read(fil,&sprite[0],sizeof(spritetype)*numsprites);
+ //
+ // close(fil);
+ // }
+
+ let version = data.read_u32::<LE>()?;
+
+ if version != 7 {
+ bail!("Unsupported MAP version.");
+ }
+
+ // TODO: Use this to position the world's player (?)
+ let _start_x = data.read_i32::<LE>()?;
+ let _start_y = data.read_i32::<LE>()?;
+ let _start_z = data.read_i32::<LE>()?;
+ let _start_angle = data.read_i16::<LE>()? & 0x7ff;
+ let _start_sector = data.read_i16::<LE>()?;
+
+ // From BUILDINF.TXT:
+ //
+ // -------------------------------------------------------------
+ // | @@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@ |
+ // | @@ @@ @@ @@ @@ @@ @@ @@@ @@ |
+ // | @@@@@@@ @@@@@ @@ @@ @@ @@ @@@@@@@ @@@@@@@ |
+ // | @@ @@ @@ @@ @@ @@ @@ @@@ @@ |
+ // | @@@@@@@ @@@@@@@ @@@@@@@ @@ @@@@@@@ @@ @@ @@@@@@@ |
+ // -------------------------------------------------------------
+ //
+ // // sizeof(sectortype) = 40
+ // typedef struct
+ // {
+ // short wallptr, wallnum;
+ // long ceilingz, floorz;
+ // short ceilingstat, floorstat;
+ // short ceilingpicnum, ceilingheinum;
+ // signed char ceilingshade;
+ // char ceilingpal, ceilingxpanning, ceilingypanning;
+ // short floorpicnum, floorheinum;
+ // signed char floorshade;
+ // char floorpal, floorxpanning, floorypanning;
+ // char visibility, filler;
+ // short lotag, hitag, extra;
+ // } sectortype;
+ // sectortype sector[1024];
+ //
+ // wallptr - index to first wall of sector
+ // wallnum - number of walls in sector
+ // z's - z coordinate (height) at first point of sector
+ //
+ // stat's
+ // bit 0: 1 = parallaxing, 0 = not "P"
+ // bit 1: 1 = sloped, 0 = not
+ // bit 2: 1 = swap x&y, 0 = not "F"
+ // bit 3: 1 = double smooshiness "E"
+ // bit 4: 1 = x-flip "F"
+ // bit 5: 1 = y-flip "F"
+ // bit 6: 1 = Align texture to first wall of sector "R"
+ // bits 7-15: reserved
+ //
+ // picnum's - texture index into art file
+ // heinum's - slope value (0-parallel to floor, 4096-45 degrees)
+ // shade's - shade offset of ceiling/floor
+ // pal's - palette lookup table number (0 - use standard colors)
+ // panning's - used to align textures or to do texture panning
+ // visibility - determines how area changes shade relative to distance
+ // filler - useless byte to make structure aligned
+ // lotag, hitag, extra - These variables used by the programmer only
+
+ let mut sectors = Vec::new();
+ let sector_count = data.read_u16::<LE>()?;
+
+ for _ in 0..sector_count {
+ let first_wall = data.read_i16::<LE>()?;
+ let wall_count = data.read_i16::<LE>()?;
+ let ceiling_height = data.read_i32::<LE>()?;
+ let floor_height = data.read_i32::<LE>()?;
+ let ceiling_status = data.read_i16::<LE>()?;
+ let floor_status = data.read_i16::<LE>()?;
+ let ceiling_bitmap = data.read_i16::<LE>()?;
+ let ceiling_slope = data.read_i16::<LE>()?;
+ let ceiling_shade = data.read_i8()?;
+ let ceiling_palette = data.read_u8()?;
+ let ceiling_panning_x = data.read_u8()?;
+ let ceiling_panning_y = data.read_u8()?;
+ let floor_bitmap = data.read_i16::<LE>()?;
+ let floor_slope = data.read_i16::<LE>()?;
+ let floor_shade = data.read_i8()?;
+ let floor_palette = data.read_u8()?;
+ let floor_panning_x = data.read_u8()?;
+ let floor_panning_y = data.read_u8()?;
+ let visibility = data.read_u8()?;
+ let _padding = data.read_u8()?;
+ let lotag = data.read_i16::<LE>()?;
+ let hitag = data.read_i16::<LE>()?;
+ let extra = data.read_i16::<LE>()?;
+
+ sectors.push(Sector {
+ first_wall,
+ wall_count,
+ visibility,
+ tags: (lotag, hitag, extra),
+
+ ceiling_height,
+ ceiling_slope,
+ ceiling_status,
+ ceiling_bitmap,
+ ceiling_shade,
+ ceiling_palette,
+ ceiling_panning: (ceiling_panning_x, ceiling_panning_y),
+
+ floor_height,
+ floor_slope,
+ floor_status,
+ floor_bitmap,
+ floor_shade,
+ floor_palette,
+ floor_panning: (floor_panning_x, floor_panning_y),
+ });
+ }
+
+ // From BUILDINF.TXT:
+ //
+ // -----------------------------------------------
+ // | @@ @@ @@@@@@@@ @@ @@ @@@@@@@ |
+ // | @@ @@ @@ @@ @@ @@ @@ |
+ // | @@ @@ @@ @@@@@@@@ @@ @@ @@@@@@@ |
+ // | @@ @@@@ @@ @@ @@ @@ @@ @@ |
+ // | @@@ @@@@ @@ @@ @@@@@@@ @@@@@@@ @@@@@@@ |
+ // ----------------------------------------------|
+ //
+ // // sizeof(walltype) = 32
+ // typedef struct
+ // {
+ // long x, y;
+ // short point2, nextwall, nextsector, cstat;
+ // short picnum, overpicnum;
+ // signed char shade;
+ // char pal, xrepeat, yrepeat, xpanning, ypanning;
+ // short lotag, hitag, extra;
+ // } walltype;
+ // walltype wall[8192];
+ //
+ // x, y: Coordinate of left side of wall
+ // point2: Index to next wall on the right (in same sector)
+ // nextwall: Index to wall on other side (-1 if there is no sector)
+ // nextsector: Index to sector on other side (-1 if there is no sector)
+ // cstat:
+ // bit 0: 1 = Blocking wall (use with clipmove, getzrange) "B"
+ // bit 1: 1 = bottoms of invisible walls swapped, 0 = not "2"
+ // bit 2: 1 = align picture on bottom (for doors), 0 = top "O"
+ // bit 3: 1 = x-flipped, 0 = normal "F"
+ // bit 4: 1 = masking wall, 0 = not "M"
+ // bit 5: 1 = 1-way wall, 0 = not "1"
+ // bit 6: 1 = Blocking wall (use with hitscan / cliptype 1) "H"
+ // bit 7: 1 = Transluscence, 0 = not "T"
+ // bit 8: 1 = y-flipped, 0 = normal "F"
+ // bit 9: 1 = Transluscence reversing, 0 = normal "T"
+ // bits 10-15: reserved
+ // picnum - texture index into art file
+ // overpicnum - texture index into art file for masked / 1-way walls
+ // shade - shade offset of wall
+ // pal - palette lookup table number (0 - use standard colors)
+ // repeat's - used to change the size of pixels (stretch textures)
+ // pannings - used to align textures or to do texture panning
+ // lotag, hitag, extra - These variables used by the programmer only
+
+ let mut walls = Vec::new();
+ let wall_count = data.read_u16::<LE>()?;
+
+ for _ in 0..wall_count {
+ let position_x = data.read_i32::<LE>()?;
+ let position_y = data.read_i32::<LE>()?;
+ let adjacent_wall_index = data.read_i16::<LE>()?;
+ let opposite_wall_index = data.read_i16::<LE>()?;
+ let into_sector_index = data.read_i16::<LE>()?;
+ let status = data.read_i16::<LE>()?;
+ let bitmap = data.read_i16::<LE>()?;
+ let bitmap_overlay = data.read_i16::<LE>()?;
+ let shade = data.read_i8()?;
+ let palette = data.read_u8()?;
+ let stretch_x = data.read_u8()?;
+ let stretch_y = data.read_u8()?;
+ let panning_x = data.read_u8()?;
+ let panning_y = data.read_u8()?;
+ let lotag = data.read_i16::<LE>()?;
+ let hitag = data.read_i16::<LE>()?;
+ let extra = data.read_i16::<LE>()?;
+
+ walls.push(Wall {
+ position: (position_x, position_y),
+ adjacent_wall_index,
+ opposite_wall_index,
+ into_sector_index,
+
+ bitmap,
+ bitmap_overlay,
+ shade,
+ palette,
+ stretch: (stretch_x, stretch_y),
+ panning: (panning_x, panning_y),
+
+ status,
+ tags: (lotag, hitag, extra),
+ });
+ }
+
+ // From BUILDINF.TXT:
+ //
+ // -------------------------------------------------------------
+ // | @@@@@@@ @@@@@@@ @@@@@@@ @@@@@@ @@@@@@@@ @@@@@@@ @@@@@@@ |
+ // | @@ @@ @@ @@ @@@ @@ @@ @@ @@ |
+ // | @@@@@@@ @@@@@@@ @@@@@@@ @@ @@ @@@@@ @@@@@@@ |
+ // | @@ @@ @@ @@ @@ @@ @@ @@ |
+ // | @@@@@@@ @@ @@ @@ @@@@@@ @@ @@@@@@@ @@@@@@@ |
+ // -------------------------------------------------------------
+ //
+ // // sizeof(spritetype) = 44
+ // typedef struct
+ // {
+ // long x, y, z;
+ // short cstat, picnum;
+ // signed char shade;
+ // char pal, clipdist, filler;
+ // unsigned char xrepeat, yrepeat;
+ // signed char xoffset, yoffset;
+ // short sectnum, statnum;
+ // short ang, owner, xvel, yvel, zvel;
+ // short lotag, hitag, extra;
+ // } spritetype;
+ // spritetype sprite[4096];
+ //
+ // x, y, z - position of sprite - can be defined at center bottom or center
+ // cstat:
+ // bit 0: 1 = Blocking sprite (use with clipmove, getzrange) "B"
+ // bit 1: 1 = transluscence, 0 = normal "T"
+ // bit 2: 1 = x-flipped, 0 = normal "F"
+ // bit 3: 1 = y-flipped, 0 = normal "F"
+ // bits 5-4: 00 = FACE sprite (default) "R"
+ // 01 = WALL sprite (like masked walls)
+ // 10 = FLOOR sprite (parallel to ceilings&floors)
+ // bit 6: 1 = 1-sided sprite, 0 = normal "1"
+ // bit 7: 1 = Real centered centering, 0 = foot center "C"
+ // bit 8: 1 = Blocking sprite (use with hitscan / cliptype 1) "H"
+ // bit 9: 1 = Transluscence reversing, 0 = normal "T"
+ // bits 10-14: reserved
+ // bit 15: 1 = Invisible sprite, 0 = not invisible
+ // picnum - texture index into art file
+ // shade - shade offset of sprite
+ // pal - palette lookup table number (0 - use standard colors)
+ // clipdist - the size of the movement clipping square (face sprites only)
+ // filler - useless byte to make structure aligned
+ // repeat's - used to change the size of pixels (stretch textures)
+ // offset's - used to center the animation of sprites
+ // sectnum - current sector of sprite
+ // statnum - current status of sprite (inactive/monster/bullet, etc.)
+ //
+ // ang - angle the sprite is facing
+ // owner, xvel, yvel, zvel, lotag, hitag, extra - These variables used by the game programmer only
+
+ let mut sprites = Vec::new();
+ let sprite_count = data.read_u16::<LE>()?;
+
+ for _ in 0..sprite_count {
+ let position_x = data.read_i32::<LE>()?;
+ let position_y = data.read_i32::<LE>()?;
+ let position_z = data.read_i32::<LE>()?;
+ let sprite_status = data.read_i16::<LE>()?;
+ let bitmap = data.read_i16::<LE>()?;
+ let shade = data.read_i8()?;
+ let palette = data.read_u8()?;
+ let clip_distance = data.read_u8()?;
+ let _filler = data.read_u8()?;
+ let stretch_x = data.read_u8()?;
+ let stretch_y = data.read_u8()?;
+ let panning_x = data.read_i8()?;
+ let panning_y = data.read_i8()?;
+ let sector_index = data.read_i16::<LE>()?;
+ let entity_status = data.read_i16::<LE>()?;
+ let angle = data.read_i16::<LE>()?;
+ let owner = data.read_i16::<LE>()?;
+ let velocity_x = data.read_i16::<LE>()?;
+ let velocity_y = data.read_i16::<LE>()?;
+ let velocity_z = data.read_i16::<LE>()?;
+ let lotag = data.read_i16::<LE>()?;
+ let hitag = data.read_i16::<LE>()?;
+ let extra = data.read_i16::<LE>()?;
+
+ sprites.push(Sprite {
+ position: (position_x, position_y, position_z),
+ velocity: (velocity_x, velocity_y, velocity_z),
+ angle,
+
+ sector_index,
+
+ bitmap,
+ clip_distance,
+ shade,
+ palette,
+ stretch: (stretch_x, stretch_y),
+ panning: (panning_x, panning_y),
+
+ sprite_status,
+ entity_status,
+
+ owner,
+ tags: (lotag, hitag, extra),
+ });
+ }
+
+ Ok(World { sectors, walls })
+ }
+}
+
+#[derive(Debug)]
+struct Sector {
+ first_wall: i16,
+ wall_count: i16,
+ visibility: u8,
+ tags: (i16, i16, i16),
+
+ ceiling_height: i32,
+ ceiling_slope: i16,
+ ceiling_status: i16,
+ ceiling_bitmap: i16,
+ ceiling_shade: i8,
+ ceiling_palette: u8,
+ ceiling_panning: (u8, u8),
+
+ floor_height: i32,
+ floor_slope: i16,
+ floor_status: i16,
+ floor_bitmap: i16,
+ floor_shade: i8,
+ floor_palette: u8,
+ floor_panning: (u8, u8),
+}
+
+#[derive(Debug)]
+struct Wall {
+ position: (i32, i32),
+
+ adjacent_wall_index: i16,
+ opposite_wall_index: i16,
+ into_sector_index: i16,
+
+ bitmap: i16,
+ bitmap_overlay: i16,
+ shade: i8,
+ palette: u8,
+ stretch: (u8, u8),
+ panning: (u8, u8),
+
+ status: i16,
+ tags: (i16, i16, i16),
+}
+
+#[derive(Debug)]
+struct Sprite {
+ position: (i32, i32, i32),
+ velocity: (i16, i16, i16),
+ angle: i16,
+
+ sector_index: i16,
+
+ bitmap: i16,
+ clip_distance: u8,
+ shade: i8,
+ palette: u8,
+ stretch: (u8, u8),
+ panning: (i8, i8),
+
+ sprite_status: i16,
+ entity_status: i16,
+
+ owner: i16,
+ tags: (i16, i16, i16),
+}