initial port

This commit is contained in:
Robin Appelman 2020-04-30 19:59:21 +02:00
commit b4b32dd6fa
5 changed files with 302 additions and 5 deletions

View file

@ -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

Binary file not shown.

View file

@ -1,7 +1,149 @@
#[cfg(test)] pub use crate::navmesh::NavArea;
mod tests { use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian};
#[test] use err_derive::Error;
fn it_works() {
assert_eq!(2 + 2, 4); 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
View 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
View file