diff options
| author | Jakob L. Kreuze <jakob@memeware.net> | 2018-06-06 17:48:25 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze <jakob@memeware.net> | 2018-06-06 17:48:25 -0400 |
| commit | 9bb619edf01a897dd531c4c4df03ff60c7b5c4ad (patch) | |
| tree | 95a09f4126d99afad14fd8154a50663f0844a6ec | |
| parent | 8d5cbda448d439deafceb9ecc54ce04a01689999 (diff) | |
Began to implement a MAP parser
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | src/art.rs | 120 | ||||
| -rw-r--r-- | src/defs.rs | 111 | ||||
| -rw-r--r-- | src/game_vars.rs | 34 | ||||
| -rw-r--r-- | src/grp.rs (renamed from src/fmt.rs) | 101 | ||||
| -rw-r--r-- | src/main.rs | 19 | ||||
| -rw-r--r-- | src/world.rs | 446 |
7 files changed, 574 insertions, 261 deletions
@@ -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 { } - } -} @@ -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,§or[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), +} |