mirror of
https://codeberg.org/icewind/sourcenav.git
synced 2026-06-03 10:14:11 +02:00
initial port
This commit is contained in:
parent
f7713d6252
commit
b4b32dd6fa
5 changed files with 302 additions and 5 deletions
|
|
@ -7,3 +7,5 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitbuffer = "0.7.1"
|
||||||
|
err-derive = "0.2.4"
|
||||||
BIN
data/pl_badwater.nav
Normal file
BIN
data/pl_badwater.nav
Normal file
Binary file not shown.
152
src/lib.rs
152
src/lib.rs
|
|
@ -1,7 +1,149 @@
|
||||||
#[cfg(test)]
|
pub use crate::navmesh::NavArea;
|
||||||
mod tests {
|
use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian};
|
||||||
|
use err_derive::Error;
|
||||||
|
|
||||||
|
mod navmesh;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ParseError {
|
||||||
|
#[error(display = "Error while reading from data: {}", _0)]
|
||||||
|
ReadError(#[error(source)] bitbuffer::ReadError),
|
||||||
|
#[error(
|
||||||
|
display = "Invalid magic number ({:#8X}), not a nav file or corrupted",
|
||||||
|
_0
|
||||||
|
)]
|
||||||
|
InvalidMagicNumber(u32),
|
||||||
|
#[error(display = "The major version for this nav ({}), is not supported", _0)]
|
||||||
|
UnsupportedVersion(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_areas(data: Vec<u8>) -> Result<Vec<NavArea>, ParseError> {
|
||||||
|
let mut data = BitReadStream::new(BitReadBuffer::new(data, LittleEndian));
|
||||||
|
let magic = data.read()?;
|
||||||
|
if magic != 0xFEEDFACE {
|
||||||
|
return Err(ParseError::InvalidMagicNumber(magic));
|
||||||
|
}
|
||||||
|
|
||||||
|
let major_version: u32 = data.read()?;
|
||||||
|
|
||||||
|
if major_version < 6 || major_version > 16 {
|
||||||
|
return Err(ParseError::UnsupportedVersion(major_version));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _minor_version: u32 = if major_version >= 10 { data.read()? } else { 0 };
|
||||||
|
|
||||||
|
let _size: u32 = data.read()?;
|
||||||
|
|
||||||
|
let _is_analysed = if major_version >= 14 {
|
||||||
|
data.read_int::<u8>(8)? == 1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let place_count: u16 = data.read()?;
|
||||||
|
|
||||||
|
// let places = Vec::with_capacity(place_count as usize);
|
||||||
|
for _id in 1..=place_count {
|
||||||
|
let name_length: u16 = data.read()?;
|
||||||
|
let _name = data.read_string(Some(name_length as usize))?;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
let _has_unnamed_areas = if major_version >= 12 {
|
||||||
|
data.read_int::<u8>(8)? == 1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let area_count: u32 = data.read()?;
|
||||||
|
|
||||||
|
let mut areas = Vec::with_capacity(area_count as usize);
|
||||||
|
|
||||||
|
for _ in 0..area_count {
|
||||||
|
let id = data.read()?;
|
||||||
|
|
||||||
|
let flags = if major_version <= 8 {
|
||||||
|
data.read_int(8)?
|
||||||
|
} else if major_version <= 12 {
|
||||||
|
data.read_int(16)?
|
||||||
|
} else {
|
||||||
|
data.read_int(32)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let north_west = data.read()?;
|
||||||
|
let south_east = data.read()?;
|
||||||
|
let north_east_z = data.read()?;
|
||||||
|
let south_west_z = data.read()?;
|
||||||
|
|
||||||
|
let connections = data.read()?;
|
||||||
|
|
||||||
|
let hiding_spots_count: u8 = data.read()?;
|
||||||
|
let hiding_spots = data.read_sized(hiding_spots_count as usize)?;
|
||||||
|
|
||||||
|
let approach_areas = if major_version < 15 {
|
||||||
|
let approach_area_count: u8 = data.read()?;
|
||||||
|
|
||||||
|
data.read_sized(approach_area_count as usize)?
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let encounter_paths_count: u32 = data.read()?;
|
||||||
|
let encounter_paths = data.read_sized(encounter_paths_count as usize)?;
|
||||||
|
|
||||||
|
let place = data.read()?;
|
||||||
|
|
||||||
|
let ladder_connections = data.read()?;
|
||||||
|
|
||||||
|
let earliest_occupy_first_team = data.read()?;
|
||||||
|
let earliest_occupy_second_team = data.read()?;
|
||||||
|
|
||||||
|
let light_intensity = if major_version >= 11 {
|
||||||
|
data.read()?
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let visible_areas = if major_version >= 16 {
|
||||||
|
let visible_areas_count: u32 = data.read()?;
|
||||||
|
data.read_sized(visible_areas_count as usize)?
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let inherit_visibility_from_area_id = data.read()?;
|
||||||
|
|
||||||
|
data.skip_bits(32)?;
|
||||||
|
|
||||||
|
areas.push(NavArea {
|
||||||
|
id,
|
||||||
|
north_west,
|
||||||
|
south_east,
|
||||||
|
north_east_z,
|
||||||
|
south_west_z,
|
||||||
|
flags,
|
||||||
|
connections,
|
||||||
|
hiding_spots,
|
||||||
|
approach_areas,
|
||||||
|
encounter_paths,
|
||||||
|
place,
|
||||||
|
ladder_connections,
|
||||||
|
earliest_occupy_first_team,
|
||||||
|
earliest_occupy_second_team,
|
||||||
|
light_intensity,
|
||||||
|
visible_areas,
|
||||||
|
inherit_visibility_from_area_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(data.bits_left() <= 32);
|
||||||
|
|
||||||
|
Ok(areas)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn test() {
|
||||||
assert_eq!(2 + 2, 4);
|
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
||||||
}
|
let areas = read_areas(file).unwrap();
|
||||||
|
assert_eq!(1930, areas.len());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
153
src/navmesh.rs
Normal file
153
src/navmesh.rs
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
use bitbuffer::{BitRead, BitReadStream, Endianness, ReadError};
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct Vector3(pub f32, pub f32, pub f32);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NavArea {
|
||||||
|
pub id: u32,
|
||||||
|
pub north_west: Vector3,
|
||||||
|
pub south_east: Vector3,
|
||||||
|
pub north_east_z: f32,
|
||||||
|
pub south_west_z: f32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub connections: Connections,
|
||||||
|
pub hiding_spots: Vec<NavHidingSpot>,
|
||||||
|
pub approach_areas: Vec<ApproachArea>,
|
||||||
|
pub encounter_paths: Vec<EncounterPath>,
|
||||||
|
pub place: u16,
|
||||||
|
pub light_intensity: LightIntensity,
|
||||||
|
pub ladder_connections: LadderConnections,
|
||||||
|
pub earliest_occupy_first_team: f32,
|
||||||
|
pub earliest_occupy_second_team: f32,
|
||||||
|
pub visible_areas: Vec<VisibleArea>,
|
||||||
|
pub inherit_visibility_from_area_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Connections([Vec<u32>; 4]);
|
||||||
|
|
||||||
|
impl<E: Endianness> BitRead<E> for Connections {
|
||||||
|
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
|
||||||
|
let mut connections = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
|
||||||
|
|
||||||
|
for direction in 0..4 {
|
||||||
|
let connection_count: u32 = stream.read()?;
|
||||||
|
connections[direction] = Vec::with_capacity(connection_count as usize);
|
||||||
|
for _ in 0..connection_count {
|
||||||
|
connections[direction].push(stream.read()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Connections(connections))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<NavDirection> for Connections {
|
||||||
|
type Output = Vec<u32>;
|
||||||
|
|
||||||
|
fn index(&self, index: NavDirection) -> &Self::Output {
|
||||||
|
&self.0[index as u8 as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LadderConnections([Vec<u32>; 2]);
|
||||||
|
|
||||||
|
impl<E: Endianness> BitRead<E> for LadderConnections {
|
||||||
|
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
|
||||||
|
let mut connections = [Vec::new(), Vec::new()];
|
||||||
|
|
||||||
|
for direction in 0..2 {
|
||||||
|
let connection_count: u32 = stream.read()?;
|
||||||
|
connections[direction] = Vec::with_capacity(connection_count as usize);
|
||||||
|
for _ in 0..connection_count {
|
||||||
|
connections[direction].push(stream.read()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LadderConnections(connections))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<LadderDirection> for LadderConnections {
|
||||||
|
type Output = Vec<u32>;
|
||||||
|
|
||||||
|
fn index(&self, index: LadderDirection) -> &Self::Output {
|
||||||
|
&self.0[index as u8 as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[discriminant_bits = 8]
|
||||||
|
pub enum NavDirection {
|
||||||
|
North,
|
||||||
|
East,
|
||||||
|
South,
|
||||||
|
West,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[discriminant_bits = 8]
|
||||||
|
pub enum LadderDirection {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct NavHidingSpot {
|
||||||
|
id: u32,
|
||||||
|
location: Vector3,
|
||||||
|
flags: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct ApproachArea {
|
||||||
|
approach_here: u32,
|
||||||
|
approach_pre: u32,
|
||||||
|
approach_type: u8,
|
||||||
|
approach_next: u32,
|
||||||
|
approach_how: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct EncounterPath {
|
||||||
|
from_area_id: u32,
|
||||||
|
from_direction: u8,
|
||||||
|
to_area_id: u32,
|
||||||
|
to_direction: u8,
|
||||||
|
#[size_bits = 8]
|
||||||
|
spots: Vec<EncounterSpot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct EncounterSpot {
|
||||||
|
order: u32,
|
||||||
|
distance: u8, // divide by 255
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead, Default)]
|
||||||
|
pub struct LightIntensity {
|
||||||
|
pub north_west: f32,
|
||||||
|
pub north_east: f32,
|
||||||
|
pub south_west: f32,
|
||||||
|
pub south_east: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, BitRead)]
|
||||||
|
pub struct VisibleArea {
|
||||||
|
id: u32,
|
||||||
|
attributes: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NavPlace {
|
||||||
|
id: u32,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NavMesh {}
|
||||||
0
src/read.rs
Normal file
0
src/read.rs
Normal file
Loading…
Add table
Add a link
Reference in a new issue