mirror of
https://codeberg.org/icewind/sourcenav.git
synced 2026-06-03 18:24:10 +02:00
update
This commit is contained in:
parent
f2a4e1ad80
commit
89ab56a12c
5 changed files with 115 additions and 175 deletions
|
|
@ -4,8 +4,9 @@ parsing of SourceEngine `.nav` files
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
This library is currently focused on getting the z-height from an x/y coordinate in a map and the api is tailored towards
|
This library is currently focused on getting the z-height from an x/y coordinate
|
||||||
that usage. For other usages the raw navigation areas are exposed.
|
in a map and the api is tailored towards that usage. For other usages the raw
|
||||||
|
navigation areas are exposed.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use sourcenav::get_quad_tree;
|
use sourcenav::get_quad_tree;
|
||||||
|
|
@ -23,5 +24,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
## Credits and Licence
|
## Credits and Licence
|
||||||
|
|
||||||
This library is largely based on [gonav](https://github.com/mrazza/gonav), a parser for `.nav` files written in Go
|
This library is largely based on [gonav](https://github.com/mrazza/gonav), a
|
||||||
and is licenced under AGPL-3.0.
|
parser for `.nav` files written in Go and is licenced under AGPL-3.0.
|
||||||
|
|
|
||||||
13
src/lib.rs
13
src/lib.rs
|
|
@ -6,7 +6,7 @@ pub use crate::navmesh::{
|
||||||
use crate::parser::read_quads;
|
use crate::parser::read_quads;
|
||||||
pub use crate::parser::{read_areas, NavArea, ParseError};
|
pub use crate::parser::{read_areas, NavArea, ParseError};
|
||||||
use aabb_quadtree::{ItemId, QuadTree};
|
use aabb_quadtree::{ItemId, QuadTree};
|
||||||
use bitbuffer::{BitReadStream, LittleEndian};
|
use binread::io::{Read, Seek};
|
||||||
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
|
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
|
||||||
|
|
||||||
mod navmesh;
|
mod navmesh;
|
||||||
|
|
@ -30,10 +30,8 @@ pub struct NavQuadTree(QuadTree<NavQuad, HammerUnit, [(ItemId, Rect); 4]>);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_quad_tree(
|
pub fn get_quad_tree<R: Read + Seek>(data: &mut R) -> Result<NavQuadTree, ParseError> {
|
||||||
data: impl Into<BitReadStream<LittleEndian>>,
|
let areas = read_quads(data)?;
|
||||||
) -> Result<NavQuadTree, ParseError> {
|
|
||||||
let areas = read_quads(data.into())?;
|
|
||||||
|
|
||||||
let (min_x, min_y, max_x, max_y) = areas.iter().fold(
|
let (min_x, min_y, max_x, max_y) = areas.iter().fold(
|
||||||
(f32::MAX, f32::MAX, f32::MIN, f32::MIN),
|
(f32::MAX, f32::MAX, f32::MIN, f32::MIN),
|
||||||
|
|
@ -142,8 +140,11 @@ impl NavQuadTree {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tree() {
|
fn test_tree() {
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
||||||
let tree = get_quad_tree(file).unwrap();
|
let mut cursor = Cursor::new(&file);
|
||||||
|
let tree = get_quad_tree(&mut cursor).unwrap();
|
||||||
|
|
||||||
// single flat plane
|
// single flat plane
|
||||||
let point1 = (1600.0, -1300.0);
|
let point1 = (1600.0, -1300.0);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
use crate::Rect;
|
use crate::Rect;
|
||||||
use aabb_quadtree::Spatial;
|
use aabb_quadtree::Spatial;
|
||||||
use bitbuffer::{BitRead, BitReadStream, Endianness, ReadError};
|
use binread::io::{Read, Seek};
|
||||||
|
use binread::{BinRead, BinReaderExt, BinResult, ReadOptions};
|
||||||
use euclid::{TypedPoint2D, TypedSize2D};
|
use euclid::{TypedPoint2D, TypedSize2D};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
/// A 3 dimensional coordinate
|
/// A 3 dimensional coordinate
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct Vector3(pub f32, pub f32, pub f32);
|
pub struct Vector3(pub f32, pub f32, pub f32);
|
||||||
|
|
||||||
/// A unique identifier for a navigation area
|
/// A unique identifier for a navigation area
|
||||||
#[derive(Debug, BitRead, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, BinRead, Clone, Copy, Eq, PartialEq)]
|
||||||
pub struct NavAreaId(u32);
|
pub struct NavAreaId(u32);
|
||||||
|
|
||||||
impl fmt::Display for NavAreaId {
|
impl fmt::Display for NavAreaId {
|
||||||
|
|
@ -21,22 +22,35 @@ impl fmt::Display for NavAreaId {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A navigation area from the nav file
|
/// A navigation area from the nav file
|
||||||
#[derive(Debug)]
|
#[derive(Debug, BinRead)]
|
||||||
|
#[br(import(major_version: u32))]
|
||||||
pub struct NavArea {
|
pub struct NavArea {
|
||||||
pub id: NavAreaId,
|
pub id: NavAreaId,
|
||||||
pub quad: NavQuad,
|
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
|
pub quad: NavQuad,
|
||||||
pub connections: Connections,
|
pub connections: Connections,
|
||||||
|
pub hiding_spots_count: u8,
|
||||||
|
#[br(count = hiding_spots_count)]
|
||||||
pub hiding_spots: Vec<NavHidingSpot>,
|
pub hiding_spots: Vec<NavHidingSpot>,
|
||||||
|
#[br(if(major_version < 15))]
|
||||||
|
pub approach_areas_count: u8,
|
||||||
|
#[br(count = approach_areas_count)]
|
||||||
pub approach_areas: Vec<ApproachArea>,
|
pub approach_areas: Vec<ApproachArea>,
|
||||||
|
pub encounter_paths_count: u32,
|
||||||
|
#[br(count = encounter_paths_count)]
|
||||||
pub encounter_paths: Vec<EncounterPath>,
|
pub encounter_paths: Vec<EncounterPath>,
|
||||||
pub place: u16,
|
pub place: u16,
|
||||||
pub light_intensity: LightIntensity,
|
|
||||||
pub ladder_connections: LadderConnections,
|
pub ladder_connections: LadderConnections,
|
||||||
pub earliest_occupy_first_team: f32,
|
pub earliest_occupy_first_team: f32,
|
||||||
pub earliest_occupy_second_team: f32,
|
pub earliest_occupy_second_team: f32,
|
||||||
|
#[br(if(major_version >= 11))]
|
||||||
|
pub light_intensity: LightIntensity,
|
||||||
|
#[br(if(major_version >= 16))]
|
||||||
|
pub visible_areas_count: u32,
|
||||||
|
#[br(count = visible_areas_count)]
|
||||||
pub visible_areas: Vec<VisibleArea>,
|
pub visible_areas: Vec<VisibleArea>,
|
||||||
pub inherit_visibility_from_area_id: u32,
|
pub inherit_visibility_from_area_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct HammerUnit;
|
pub(crate) struct HammerUnit;
|
||||||
|
|
@ -62,28 +76,26 @@ pub(crate) struct HammerUnit;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Connections([Vec<NavAreaId>; 4]);
|
pub struct Connections([Vec<NavAreaId>; 4]);
|
||||||
|
|
||||||
impl<E: Endianness> BitRead<E> for Connections {
|
impl BinRead for Connections {
|
||||||
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
|
type Args = ();
|
||||||
|
|
||||||
|
fn read_options<R: Read + Seek>(
|
||||||
|
reader: &mut R,
|
||||||
|
options: &ReadOptions,
|
||||||
|
args: Self::Args,
|
||||||
|
) -> BinResult<Self> {
|
||||||
let mut connections = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
|
let mut connections = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
|
||||||
|
|
||||||
for direction in connections.iter_mut() {
|
for direction in connections.iter_mut() {
|
||||||
let connection_count: u32 = stream.read()?;
|
let connection_count: u32 = reader.read_le()?;
|
||||||
direction.reserve(connection_count as usize);
|
direction.reserve(connection_count as usize);
|
||||||
for _ in 0..connection_count {
|
for _ in 0..connection_count {
|
||||||
direction.push(stream.read()?);
|
direction.push(reader.read_le()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Connections(connections))
|
Ok(Connections(connections))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip(stream: &mut BitReadStream<E>) -> Result<(), ReadError> {
|
|
||||||
for _ in 0..4 {
|
|
||||||
let connection_count: u32 = stream.read()?;
|
|
||||||
stream.skip_bits(connection_count as usize * 32)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<NavDirection> for Connections {
|
impl Index<NavDirection> for Connections {
|
||||||
|
|
@ -115,28 +127,26 @@ impl Index<NavDirection> for Connections {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct LadderConnections([Vec<NavAreaId>; 2]);
|
pub struct LadderConnections([Vec<NavAreaId>; 2]);
|
||||||
|
|
||||||
impl<E: Endianness> BitRead<E> for LadderConnections {
|
impl BinRead for LadderConnections {
|
||||||
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
|
type Args = ();
|
||||||
|
|
||||||
|
fn read_options<R: Read + Seek>(
|
||||||
|
reader: &mut R,
|
||||||
|
options: &ReadOptions,
|
||||||
|
args: Self::Args,
|
||||||
|
) -> BinResult<Self> {
|
||||||
let mut connections = [Vec::new(), Vec::new()];
|
let mut connections = [Vec::new(), Vec::new()];
|
||||||
|
|
||||||
for direction in connections.iter_mut() {
|
for direction in connections.iter_mut() {
|
||||||
let connection_count: u32 = stream.read()?;
|
let connection_count: u32 = reader.read_le()?;
|
||||||
direction.reserve(connection_count as usize);
|
direction.reserve(connection_count as usize);
|
||||||
for _ in 0..connection_count {
|
for _ in 0..connection_count {
|
||||||
direction.push(stream.read()?);
|
direction.push(reader.read_le()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(LadderConnections(connections))
|
Ok(LadderConnections(connections))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip(stream: &mut BitReadStream<E>) -> Result<(), ReadError> {
|
|
||||||
for _ in 0..2 {
|
|
||||||
let connection_count: u32 = stream.read()?;
|
|
||||||
stream.skip_bits(connection_count as usize * 32)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<LadderDirection> for LadderConnections {
|
impl Index<LadderDirection> for LadderConnections {
|
||||||
|
|
@ -148,9 +158,8 @@ impl Index<LadderDirection> for LadderConnections {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The directions in which two areas can be connected
|
/// The directions in which two areas can be connected
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[discriminant_bits = 8]
|
|
||||||
pub enum NavDirection {
|
pub enum NavDirection {
|
||||||
North,
|
North,
|
||||||
East,
|
East,
|
||||||
|
|
@ -159,16 +168,15 @@ pub enum NavDirection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The directions in which two areas can be connected by ladder
|
/// The directions in which two areas can be connected by ladder
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[discriminant_bits = 8]
|
|
||||||
pub enum LadderDirection {
|
pub enum LadderDirection {
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A hiding spot within an area
|
/// A hiding spot within an area
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct NavHidingSpot {
|
pub struct NavHidingSpot {
|
||||||
id: u32,
|
id: u32,
|
||||||
location: Vector3,
|
location: Vector3,
|
||||||
|
|
@ -176,7 +184,7 @@ pub struct NavHidingSpot {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An area that can be used for approach, no longer used in newer nav files
|
/// An area that can be used for approach, no longer used in newer nav files
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct ApproachArea {
|
pub struct ApproachArea {
|
||||||
approach_here: u32,
|
approach_here: u32,
|
||||||
approach_pre: u32,
|
approach_pre: u32,
|
||||||
|
|
@ -186,24 +194,25 @@ pub struct ApproachArea {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A path that can be used to approach an area
|
/// A path that can be used to approach an area
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct EncounterPath {
|
pub struct EncounterPath {
|
||||||
from_area_id: NavAreaId,
|
from_area_id: NavAreaId,
|
||||||
from_direction: u8,
|
from_direction: u8,
|
||||||
to_area_id: NavAreaId,
|
to_area_id: NavAreaId,
|
||||||
to_direction: u8,
|
to_direction: u8,
|
||||||
#[size_bits = 8]
|
spot_count: u8,
|
||||||
|
#[br(count = spot_count)]
|
||||||
spots: Vec<EncounterSpot>,
|
spots: Vec<EncounterSpot>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct EncounterSpot {
|
pub struct EncounterSpot {
|
||||||
order: u32,
|
order: u32,
|
||||||
distance: u8, // divide by 255
|
distance: u8, // divide by 255
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The light intensity at the four corners of an area
|
/// The light intensity at the four corners of an area
|
||||||
#[derive(Debug, BitRead, Default)]
|
#[derive(Debug, BinRead, Default)]
|
||||||
pub struct LightIntensity {
|
pub struct LightIntensity {
|
||||||
pub north_west: f32,
|
pub north_west: f32,
|
||||||
pub north_east: f32,
|
pub north_east: f32,
|
||||||
|
|
@ -212,7 +221,7 @@ pub struct LightIntensity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An area that is visible
|
/// An area that is visible
|
||||||
#[derive(Debug, BitRead)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct VisibleArea {
|
pub struct VisibleArea {
|
||||||
id: u32,
|
id: u32,
|
||||||
attributes: u8,
|
attributes: u8,
|
||||||
|
|
@ -225,7 +234,7 @@ pub struct NavPlace {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A navigation area from the nav file
|
/// A navigation area from the nav file
|
||||||
#[derive(Debug)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct NavQuad {
|
pub struct NavQuad {
|
||||||
pub north_west: Vector3,
|
pub north_west: Vector3,
|
||||||
pub south_east: Vector3,
|
pub south_east: Vector3,
|
||||||
|
|
|
||||||
174
src/parser.rs
174
src/parser.rs
|
|
@ -1,15 +1,17 @@
|
||||||
pub use crate::navmesh::NavArea;
|
pub use crate::navmesh::NavArea;
|
||||||
use crate::navmesh::NavQuad;
|
use crate::navmesh::NavQuad;
|
||||||
use crate::{Connections, EncounterPath, LadderConnections, NavHidingSpot, VisibleArea};
|
use crate::{Connections, EncounterPath, LadderConnections, NavHidingSpot, VisibleArea};
|
||||||
use bitbuffer::{BitRead, BitReadStream, LittleEndian};
|
use binread::io::{Read, Seek, SeekFrom};
|
||||||
|
use binread::{BinRead, BinReaderExt, ReadOptions};
|
||||||
use err_derive::Error;
|
use err_derive::Error;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
/// Errors that can occur when parsing the binary nav file
|
/// Errors that can occur when parsing the binary nav file
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
/// An error ocured when reading from the source binary data
|
/// An error ocured when reading from the source binary data
|
||||||
#[error(display = "Error while reading from data: {}", _0)]
|
#[error(display = "Error while reading from data: {}", _0)]
|
||||||
ReadError(#[error(source)] bitbuffer::ReadError),
|
ReadError(#[error(source)] binread::Error),
|
||||||
#[error(
|
#[error(
|
||||||
display = "Invalid magic number ({:#8X}), not a nav file or corrupted",
|
display = "Invalid magic number ({:#8X}), not a nav file or corrupted",
|
||||||
_0
|
_0
|
||||||
|
|
@ -34,127 +36,53 @@ pub enum ParseError {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn read_areas(
|
pub fn read_areas<R: Read + Seek>(data: &mut R) -> Result<Vec<NavArea>, ParseError> {
|
||||||
data: impl Into<BitReadStream<LittleEndian>>,
|
let magic = data.read_le()?;
|
||||||
) -> Result<Vec<NavArea>, ParseError> {
|
|
||||||
let mut data = data.into();
|
|
||||||
let magic = data.read()?;
|
|
||||||
if magic != 0xFEED_FACE {
|
if magic != 0xFEED_FACE {
|
||||||
return Err(ParseError::InvalidMagicNumber(magic));
|
return Err(ParseError::InvalidMagicNumber(magic));
|
||||||
}
|
}
|
||||||
|
|
||||||
let major_version: u32 = data.read()?;
|
let major_version: u32 = data.read_le()?;
|
||||||
|
|
||||||
if major_version < 6 || major_version > 16 {
|
if major_version < 6 || major_version > 16 {
|
||||||
return Err(ParseError::UnsupportedVersion(major_version));
|
return Err(ParseError::UnsupportedVersion(major_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _minor_version: u32 = if major_version >= 10 { data.read()? } else { 0 };
|
let _minor_version: u32 = if major_version >= 10 {
|
||||||
|
data.read_le()?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let _size: u32 = data.read()?;
|
let _size: u32 = data.read_le()?;
|
||||||
|
|
||||||
let _is_analysed = if major_version >= 14 {
|
let _is_analysed = if major_version >= 14 {
|
||||||
data.read_int::<u8>(8)? == 1
|
data.read_le::<u8>(8)? == 1
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let place_count: u16 = data.read()?;
|
let place_count: u16 = data.read_le()?;
|
||||||
|
|
||||||
// let places = Vec::with_capacity(place_count as usize);
|
// let places = Vec::with_capacity(place_count as usize);
|
||||||
for _id in 1..=place_count {
|
for _id in 1..=place_count {
|
||||||
let name_length: u16 = data.read()?;
|
let name_length: u16 = data.read_le()?;
|
||||||
let _name = data.read_string(Some(name_length as usize))?;
|
let _name = data.read_string(Some(name_length as usize))?;
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
let _has_unnamed_areas = if major_version >= 12 {
|
let _has_unnamed_areas = if major_version >= 12 {
|
||||||
data.read_int::<u8>(8)? == 1
|
data.read_le::<u8>(8)? == 1
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let area_count: u32 = data.read()?;
|
let area_count: u32 = data.read_le()?;
|
||||||
|
|
||||||
let mut areas = Vec::with_capacity(area_count as usize);
|
let mut areas = Vec::with_capacity(area_count as usize);
|
||||||
|
|
||||||
for _ in 0..area_count {
|
for _ in 0..area_count {
|
||||||
let id = data.read()?;
|
areas.push(data.read_le()?);
|
||||||
|
|
||||||
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,
|
|
||||||
quad: NavQuad {
|
|
||||||
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);
|
debug_assert!(data.bits_left() <= 32);
|
||||||
|
|
@ -162,83 +90,83 @@ pub fn read_areas(
|
||||||
Ok(areas)
|
Ok(areas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_quads(
|
pub(crate) fn read_quads<R: Read + Seek>(data: &mut R) -> Result<Vec<NavQuad>, ParseError> {
|
||||||
mut data: BitReadStream<LittleEndian>,
|
let magic = data.read_le()?;
|
||||||
) -> Result<Vec<NavQuad>, ParseError> {
|
|
||||||
let magic = data.read()?;
|
|
||||||
if magic != 0xFEED_FACE {
|
if magic != 0xFEED_FACE {
|
||||||
return Err(ParseError::InvalidMagicNumber(magic));
|
return Err(ParseError::InvalidMagicNumber(magic));
|
||||||
}
|
}
|
||||||
|
|
||||||
let major_version: u32 = data.read()?;
|
let major_version: u32 = data.read_le()?;
|
||||||
|
|
||||||
if major_version != 16 {
|
if major_version != 16 {
|
||||||
return Err(ParseError::UnsupportedVersion(major_version));
|
return Err(ParseError::UnsupportedVersion(major_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _minor_version: u32 = if major_version >= 10 { data.read()? } else { 0 };
|
let _minor_version: u32 = if major_version >= 10 {
|
||||||
|
data.read_le()?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let _size: u32 = data.read()?;
|
let _size: u32 = data.read_le()?;
|
||||||
|
|
||||||
let _is_analysed = if major_version >= 14 {
|
let _is_analysed = if major_version >= 14 {
|
||||||
data.read_int::<u8>(8)? == 1
|
data.read_le::<u8>(8)? == 1
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let place_count: u16 = data.read()?;
|
let place_count: u16 = data.read_le()?;
|
||||||
|
|
||||||
// let places = Vec::with_capacity(place_count as usize);
|
// let places = Vec::with_capacity(place_count as usize);
|
||||||
for _id in 1..=place_count {
|
for _id in 1..=place_count {
|
||||||
let name_length: u16 = data.read()?;
|
let name_length: u16 = data.read_le()?;
|
||||||
let _name = data.read_string(Some(name_length as usize))?;
|
let _name = data.read_string(Some(name_length as usize))?;
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
let _has_unnamed_areas = if major_version >= 12 {
|
let _has_unnamed_areas = if major_version >= 12 {
|
||||||
data.read_int::<u8>(8)? == 1
|
data.read_le::<u8>(8)? == 1
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let area_count: u32 = data.read()?;
|
let area_count: u32 = data.read_le()?;
|
||||||
|
|
||||||
let mut areas = Vec::with_capacity(area_count as usize);
|
let mut areas = Vec::with_capacity(area_count as usize);
|
||||||
|
|
||||||
for _ in 0..area_count {
|
for _ in 0..area_count {
|
||||||
data.skip_bits(32 * 2)?; // id and flags
|
data.seek(SeekFrom::Current(4 * 2))?; // id and flags
|
||||||
|
|
||||||
let north_west = data.read()?;
|
let north_west = data.read_le()?;
|
||||||
let south_east = data.read()?;
|
let south_east = data.read_le()?;
|
||||||
let north_east_z = data.read()?;
|
let north_east_z = data.read_le()?;
|
||||||
let south_west_z = data.read()?;
|
let south_west_z = data.read_le()?;
|
||||||
|
|
||||||
Connections::skip(&mut data)?;
|
Connections::skip(&mut data)?;
|
||||||
|
|
||||||
let hiding_spots_count: u8 = data.read()?;
|
let hiding_spots_count: u8 = data.read_le()?;
|
||||||
data.skip_bits(
|
data.seek(SeekFrom::Current(
|
||||||
<NavHidingSpot as BitRead<LittleEndian>>::bit_size().unwrap()
|
size_of::<NavHidingSpot>() as i64 * hiding_spots_count as i64,
|
||||||
* hiding_spots_count as usize,
|
))?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let encounter_paths_count: u32 = data.read()?;
|
let encounter_paths_count: u32 = data.read_le()?;
|
||||||
for _ in 0..encounter_paths_count {
|
for _ in 0..encounter_paths_count {
|
||||||
EncounterPath::skip(&mut data)?;
|
EncounterPath::skip(&mut data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.skip_bits(16)?; // place
|
data.seek(SeekFrom::Current(2))?;
|
||||||
|
|
||||||
LadderConnections::skip(&mut data)?;
|
let _: LadderConnections = data.read_le()?;
|
||||||
|
|
||||||
data.skip_bits((32 * 2) + (32 * 4))?; // occupy time, light intensity
|
data.seek(SeekFrom::Current((4 * 2) + (4 * 4)))?; // occupy time, light intensity
|
||||||
|
|
||||||
let visible_areas_count: u32 = data.read()?;
|
let visible_areas_count: u32 = data.read_le()?;
|
||||||
data.skip_bits(
|
data.seek(SeekFrom::Current(
|
||||||
<VisibleArea as BitRead<LittleEndian>>::bit_size().unwrap()
|
size_of::<VisibleArea>() as i64 * visible_areas_count as i64,
|
||||||
* visible_areas_count as usize,
|
))?;
|
||||||
)?;
|
|
||||||
|
|
||||||
data.skip_bits(32 * 2)?; // inherit visible, garbage
|
data.seek(SeekFrom::Current(4 * 2))?; // inherit visible, garbage
|
||||||
|
|
||||||
areas.push(NavQuad {
|
areas.push(NavQuad {
|
||||||
north_west,
|
north_west,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue