mirror of
https://codeberg.org/icewind/sourcenav.git
synced 2026-06-03 18:24:10 +02:00
allow parsing just the nav quads
This commit is contained in:
parent
d3765674d4
commit
99e24bc9b8
5 changed files with 239 additions and 96 deletions
|
|
@ -8,11 +8,11 @@ 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.
|
that usage. For other usages the raw navigation areas are exposed.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use sourcenav::get_area_tree;
|
use sourcenav::get_quad_tree;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let file = std::fs::read("data/pl_badwater.nav")?;
|
let file = std::fs::read("data/pl_badwater.nav")?;
|
||||||
let tree = get_area_tree(file)?;
|
let tree = get_quad_tree(file)?;
|
||||||
|
|
||||||
assert_eq!(220.83125, tree.find_best_height(320.0, -1030.0, 0.0));
|
assert_eq!(220.83125, tree.find_best_height(320.0, -1030.0, 0.0));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,26 @@ extern crate test;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian};
|
use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian};
|
||||||
use sourcenav::get_area_tree;
|
use sourcenav::{get_quad_tree, read_areas};
|
||||||
use std::fs::read;
|
use std::fs::read;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_badwater(b: &mut Bencher) {
|
fn bench_badwater_areas(b: &mut Bencher) {
|
||||||
let file = read("data/pl_badwater.nav").unwrap();
|
let file = read("data/pl_badwater.nav").unwrap();
|
||||||
let data = BitReadStream::new(BitReadBuffer::new(file, LittleEndian));
|
let data = BitReadStream::new(BitReadBuffer::new(file, LittleEndian));
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
test::black_box(get_area_tree(data.clone()));
|
test::black_box(read_areas(data.clone()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_badwater_quads(b: &mut Bencher) {
|
||||||
|
let file = read("data/pl_badwater.nav").unwrap();
|
||||||
|
let data = BitReadStream::new(BitReadBuffer::new(file, LittleEndian));
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
test::black_box(get_quad_tree(data.clone()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
src/lib.rs
51
src/lib.rs
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::navmesh::HammerUnit;
|
use crate::navmesh::HammerUnit;
|
||||||
pub use crate::navmesh::{
|
pub use crate::navmesh::{
|
||||||
ApproachArea, Connections, EncounterPath, LadderConnections, LadderDirection, LightIntensity,
|
ApproachArea, Connections, EncounterPath, LadderConnections, LadderDirection, LightIntensity,
|
||||||
NavDirection, NavHidingSpot, Vector3, VisibleArea,
|
NavDirection, NavHidingSpot, NavQuad, Vector3, VisibleArea,
|
||||||
};
|
};
|
||||||
use crate::parser::read_areas;
|
use crate::parser::read_quads;
|
||||||
pub use crate::parser::{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 bitbuffer::{BitReadStream, LittleEndian};
|
||||||
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
|
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
|
||||||
|
|
@ -15,23 +15,25 @@ mod parser;
|
||||||
type Rect = TypedRect<f32, HammerUnit>;
|
type Rect = TypedRect<f32, HammerUnit>;
|
||||||
|
|
||||||
/// A tree of all navigation areas
|
/// A tree of all navigation areas
|
||||||
pub struct NavTree(QuadTree<NavArea, HammerUnit, [(ItemId, Rect); 4]>);
|
pub struct NavQuadTree(QuadTree<NavQuad, HammerUnit, [(ItemId, Rect); 4]>);
|
||||||
|
|
||||||
/// Parse all navigation areas from a nav file
|
/// Parse all navigation quads from a nav file
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use sourcenav::get_area_tree;
|
/// use sourcenav::get_quad_tree;
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let file = std::fs::read("path/to/navfile.nav")?;
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
/// let tree = get_area_tree(file)?;
|
/// let tree = get_quad_tree(file)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_area_tree(data: impl Into<BitReadStream<LittleEndian>>) -> Result<NavTree, ParseError> {
|
pub fn get_quad_tree(
|
||||||
let areas = read_areas(data.into())?;
|
data: impl Into<BitReadStream<LittleEndian>>,
|
||||||
|
) -> 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),
|
||||||
|
|
@ -57,25 +59,25 @@ pub fn get_area_tree(data: impl Into<BitReadStream<LittleEndian>>) -> Result<Nav
|
||||||
tree.insert(area);
|
tree.insert(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(NavTree(tree))
|
Ok(NavQuadTree(tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavTree {
|
impl NavQuadTree {
|
||||||
/// Find the navigation areas at a x/y cooordinate
|
/// Find the navigation areas at a x/y cooordinate
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use sourcenav::get_area_tree;
|
/// use sourcenav::get_quad_tree;
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let file = std::fs::read("path/to/navfile.nav")?;
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
/// let tree = get_area_tree(file)?;
|
/// let tree = get_quad_tree(file)?;
|
||||||
/// let areas = tree.query(150.0, -312.0);
|
/// let areas = tree.query(150.0, -312.0);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn query(&self, x: f32, y: f32) -> impl Iterator<Item = &NavArea> {
|
pub fn query(&self, x: f32, y: f32) -> impl Iterator<Item = &NavQuad> {
|
||||||
let query_box = Rect::new(TypedPoint2D::new(x, y), TypedSize2D::new(1.0, 1.0));
|
let query_box = Rect::new(TypedPoint2D::new(x, y), TypedSize2D::new(1.0, 1.0));
|
||||||
|
|
||||||
self.0.query(query_box).into_iter().map(|(area, ..)| area)
|
self.0.query(query_box).into_iter().map(|(area, ..)| area)
|
||||||
|
|
@ -88,11 +90,11 @@ impl NavTree {
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use sourcenav::get_area_tree;
|
/// use sourcenav::get_quad_tree;
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let file = std::fs::read("path/to/navfile.nav")?;
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
/// let tree = get_area_tree(file)?;
|
/// let tree = get_quad_tree(file)?;
|
||||||
/// let heights = tree.find_z_height(150.0, -312.0);
|
/// let heights = tree.find_z_height(150.0, -312.0);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
|
@ -122,18 +124,18 @@ impl NavTree {
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use sourcenav::get_area_tree;
|
/// use sourcenav::get_quad_tree;
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let file = std::fs::read("path/to/navfile.nav")?;
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
/// let tree = get_area_tree(file)?;
|
/// let tree = get_quad_tree(file)?;
|
||||||
/// for area in tree.areas() {
|
/// for quad in tree.quads() {
|
||||||
/// println!("area: {}", area.id)
|
/// println!("area: {:?}", quad)
|
||||||
/// }
|
/// }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn areas(&self) -> impl Iterator<Item = &NavArea> {
|
pub fn quads(&self) -> impl Iterator<Item = &NavQuad> {
|
||||||
self.0.iter().map(|(_, (area, _))| area)
|
self.0.iter().map(|(_, (area, _))| area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +143,7 @@ impl NavTree {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tree() {
|
fn test_tree() {
|
||||||
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
||||||
let tree = get_area_tree(file).unwrap();
|
let tree = get_quad_tree(file).unwrap();
|
||||||
|
|
||||||
// single flat plane
|
// single flat plane
|
||||||
let point1 = (1600.0, -1300.0);
|
let point1 = (1600.0, -1300.0);
|
||||||
|
|
@ -174,11 +176,6 @@ fn test_tree() {
|
||||||
vec![147.23126],
|
vec![147.23126],
|
||||||
tree.find_z_height(point4.0, point4.1).collect::<Vec<f32>>()
|
tree.find_z_height(point4.0, point4.1).collect::<Vec<f32>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
tree.query(point3.0, point3.1).next().map(|area| area.id),
|
|
||||||
tree.query(point4.0, point4.1).next().map(|area| area.id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(doctest)]
|
#[cfg(doctest)]
|
||||||
|
|
|
||||||
133
src/navmesh.rs
133
src/navmesh.rs
|
|
@ -24,10 +24,7 @@ impl fmt::Display for NavAreaId {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NavArea {
|
pub struct NavArea {
|
||||||
pub id: NavAreaId,
|
pub id: NavAreaId,
|
||||||
pub north_west: Vector3,
|
pub quad: NavQuad,
|
||||||
pub south_east: Vector3,
|
|
||||||
pub north_east_z: f32,
|
|
||||||
pub south_west_z: f32,
|
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
pub connections: Connections,
|
pub connections: Connections,
|
||||||
pub hiding_spots: Vec<NavHidingSpot>,
|
pub hiding_spots: Vec<NavHidingSpot>,
|
||||||
|
|
@ -42,60 +39,8 @@ pub struct NavArea {
|
||||||
pub inherit_visibility_from_area_id: u32,
|
pub inherit_visibility_from_area_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavArea {
|
|
||||||
pub fn width(&self) -> f32 {
|
|
||||||
self.south_east.0 - self.north_west.0
|
|
||||||
}
|
|
||||||
pub fn height(&self) -> f32 {
|
|
||||||
self.south_east.1 - self.north_west.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the z height of a x/y point inside the navigation area
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use sourcenav::get_area_tree;
|
|
||||||
///
|
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let file = std::fs::read("path/to/navfile.nav")?;
|
|
||||||
/// let tree = get_area_tree(file)?;
|
|
||||||
/// let area = tree.query(150.0, -312.0).next().unwrap();
|
|
||||||
///
|
|
||||||
/// let height = area.get_z_height(150.0, -312.0);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn get_z_height(&self, x: f32, y: f32) -> f32 {
|
|
||||||
let from_east = self.south_east.0 - x;
|
|
||||||
let from_south = self.south_east.0 - y;
|
|
||||||
|
|
||||||
let north_slope = (self.north_west.2 - self.north_east_z) / self.width();
|
|
||||||
let south_slope = (self.south_west_z - self.south_east.2) / self.width();
|
|
||||||
|
|
||||||
let north_z = self.north_east_z + north_slope * from_east;
|
|
||||||
let south_z = self.south_east.2 + south_slope * from_east;
|
|
||||||
|
|
||||||
let final_slope = (north_z - south_z) / self.height();
|
|
||||||
|
|
||||||
south_z + final_slope * from_south
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct HammerUnit;
|
pub(crate) struct HammerUnit;
|
||||||
|
|
||||||
impl Spatial<HammerUnit> for NavArea {
|
|
||||||
fn aabb(&self) -> Rect {
|
|
||||||
Rect {
|
|
||||||
origin: TypedPoint2D::new(self.north_west.0, self.north_west.1),
|
|
||||||
size: TypedSize2D::new(
|
|
||||||
self.south_east.0 - self.north_west.0,
|
|
||||||
self.south_east.1 - self.north_west.1,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The connections from a navigation area into it's neighbours
|
/// The connections from a navigation area into it's neighbours
|
||||||
///
|
///
|
||||||
/// Contains a list of area id's for every [`NavDirection`]
|
/// Contains a list of area id's for every [`NavDirection`]
|
||||||
|
|
@ -131,6 +76,14 @@ impl<E: Endianness> BitRead<E> for Connections {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -176,6 +129,14 @@ impl<E: Endianness> BitRead<E> for LadderConnections {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -263,5 +224,63 @@ pub struct NavPlace {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A navigation area from the nav file
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NavMesh {}
|
pub struct NavQuad {
|
||||||
|
pub north_west: Vector3,
|
||||||
|
pub south_east: Vector3,
|
||||||
|
pub north_east_z: f32,
|
||||||
|
pub south_west_z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavQuad {
|
||||||
|
pub fn width(&self) -> f32 {
|
||||||
|
self.south_east.0 - self.north_west.0
|
||||||
|
}
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
self.south_east.1 - self.north_west.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the z height of a x/y point inside the navigation area
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use sourcenav::get_quad_tree;
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
|
/// let tree = get_quad_tree(file)?;
|
||||||
|
/// let area = tree.query(150.0, -312.0).next().unwrap();
|
||||||
|
///
|
||||||
|
/// let height = area.get_z_height(150.0, -312.0);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn get_z_height(&self, x: f32, y: f32) -> f32 {
|
||||||
|
let from_east = self.south_east.0 - x;
|
||||||
|
let from_south = self.south_east.0 - y;
|
||||||
|
|
||||||
|
let north_slope = (self.north_west.2 - self.north_east_z) / self.width();
|
||||||
|
let south_slope = (self.south_west_z - self.south_east.2) / self.width();
|
||||||
|
|
||||||
|
let north_z = self.north_east_z + north_slope * from_east;
|
||||||
|
let south_z = self.south_east.2 + south_slope * from_east;
|
||||||
|
|
||||||
|
let final_slope = (north_z - south_z) / self.height();
|
||||||
|
|
||||||
|
south_z + final_slope * from_south
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spatial<HammerUnit> for NavQuad {
|
||||||
|
fn aabb(&self) -> Rect {
|
||||||
|
Rect {
|
||||||
|
origin: TypedPoint2D::new(self.north_west.0, self.north_west.1),
|
||||||
|
size: TypedSize2D::new(
|
||||||
|
self.south_east.0 - self.north_west.0,
|
||||||
|
self.south_east.1 - self.north_west.1,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
123
src/parser.rs
123
src/parser.rs
|
|
@ -1,5 +1,7 @@
|
||||||
pub use crate::navmesh::NavArea;
|
pub use crate::navmesh::NavArea;
|
||||||
use bitbuffer::{BitReadStream, LittleEndian};
|
use crate::navmesh::NavQuad;
|
||||||
|
use crate::{Connections, EncounterPath, LadderConnections, NavHidingSpot, VisibleArea};
|
||||||
|
use bitbuffer::{BitRead, BitReadStream, LittleEndian};
|
||||||
use err_derive::Error;
|
use err_derive::Error;
|
||||||
|
|
||||||
/// Errors that can occur when parsing the binary nav file
|
/// Errors that can occur when parsing the binary nav file
|
||||||
|
|
@ -19,9 +21,23 @@ pub enum ParseError {
|
||||||
UnsupportedVersion(u32),
|
UnsupportedVersion(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_areas(
|
/// Parse all navigation areas from a nav file
|
||||||
mut data: BitReadStream<LittleEndian>,
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use sourcenav::read_areas;
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let file = std::fs::read("path/to/navfile.nav")?;
|
||||||
|
/// let tree = read_areas(file)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn read_areas(
|
||||||
|
data: impl Into<BitReadStream<LittleEndian>>,
|
||||||
) -> Result<Vec<NavArea>, ParseError> {
|
) -> Result<Vec<NavArea>, ParseError> {
|
||||||
|
let mut data = data.into();
|
||||||
let magic = data.read()?;
|
let magic = data.read()?;
|
||||||
if magic != 0xFEED_FACE {
|
if magic != 0xFEED_FACE {
|
||||||
return Err(ParseError::InvalidMagicNumber(magic));
|
return Err(ParseError::InvalidMagicNumber(magic));
|
||||||
|
|
@ -120,10 +136,12 @@ pub(crate) fn read_areas(
|
||||||
|
|
||||||
areas.push(NavArea {
|
areas.push(NavArea {
|
||||||
id,
|
id,
|
||||||
|
quad: NavQuad {
|
||||||
north_west,
|
north_west,
|
||||||
south_east,
|
south_east,
|
||||||
north_east_z,
|
north_east_z,
|
||||||
south_west_z,
|
south_west_z,
|
||||||
|
},
|
||||||
flags,
|
flags,
|
||||||
connections,
|
connections,
|
||||||
hiding_spots,
|
hiding_spots,
|
||||||
|
|
@ -144,6 +162,97 @@ pub(crate) fn read_areas(
|
||||||
Ok(areas)
|
Ok(areas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_quads(
|
||||||
|
mut data: BitReadStream<LittleEndian>,
|
||||||
|
) -> Result<Vec<NavQuad>, ParseError> {
|
||||||
|
let magic = data.read()?;
|
||||||
|
if magic != 0xFEED_FACE {
|
||||||
|
return Err(ParseError::InvalidMagicNumber(magic));
|
||||||
|
}
|
||||||
|
|
||||||
|
let major_version: u32 = data.read()?;
|
||||||
|
|
||||||
|
if 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 {
|
||||||
|
data.skip_bits(32 * 2)?; // id and flags
|
||||||
|
|
||||||
|
let north_west = data.read()?;
|
||||||
|
let south_east = data.read()?;
|
||||||
|
let north_east_z = data.read()?;
|
||||||
|
let south_west_z = data.read()?;
|
||||||
|
|
||||||
|
Connections::skip(&mut data)?;
|
||||||
|
|
||||||
|
let hiding_spots_count: u8 = data.read()?;
|
||||||
|
data.skip_bits(
|
||||||
|
<NavHidingSpot as BitRead<LittleEndian>>::bit_size().unwrap()
|
||||||
|
* hiding_spots_count as usize,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let encounter_paths_count: u32 = data.read()?;
|
||||||
|
for _ in 0..encounter_paths_count {
|
||||||
|
EncounterPath::skip(&mut data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.skip_bits(16)?; // place
|
||||||
|
|
||||||
|
LadderConnections::skip(&mut data)?;
|
||||||
|
|
||||||
|
data.skip_bits((32 * 2) + (32 * 4))?; // occupy time, light intensity
|
||||||
|
|
||||||
|
let visible_areas_count: u32 = data.read()?;
|
||||||
|
data.skip_bits(
|
||||||
|
<VisibleArea as BitRead<LittleEndian>>::bit_size().unwrap()
|
||||||
|
* visible_areas_count as usize,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
data.skip_bits(32 * 2)?; // inherit visible, garbage
|
||||||
|
|
||||||
|
areas.push(NavQuad {
|
||||||
|
north_west,
|
||||||
|
south_east,
|
||||||
|
north_east_z,
|
||||||
|
south_west_z,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(data.bits_left() <= 32);
|
||||||
|
|
||||||
|
Ok(areas)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
||||||
|
|
@ -151,3 +260,11 @@ fn test() {
|
||||||
let areas = read_areas(data).unwrap();
|
let areas = read_areas(data).unwrap();
|
||||||
assert_eq!(1930, areas.len());
|
assert_eq!(1930, areas.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quads() {
|
||||||
|
let file = std::fs::read("data/pl_badwater.nav").unwrap();
|
||||||
|
let data = BitReadStream::new(bitbuffer::BitReadBuffer::new(file, LittleEndian));
|
||||||
|
let quads = read_quads(data).unwrap();
|
||||||
|
assert_eq!(1930, quads.len());
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue