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
|
||||
|
||||
[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)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
pub use crate::navmesh::NavArea;
|
||||
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]
|
||||
fn test() {
|
||||
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