Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bin support #6

Merged
merged 63 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
28dcc65
chore: minor cleanup
alanpq Jul 31, 2024
3092db3
chore: tweak nix flake for intellij
alanpq Jul 31, 2024
e91a321
feat: optional serde dependency for wads
alanpq Jul 31, 2024
0370c27
fix: ignore wad source buffer in serde ser
alanpq Jul 31, 2024
2cfb957
fix: serde ignore -> skip typo
alanpq Jul 31, 2024
ec5eba2
feat: optional wasm_bindgen dep
alanpq Jul 31, 2024
7e7d96c
fix: make wad chunk fields public
alanpq Jul 31, 2024
d6c58a2
feat: wasm support for WadChunkCompression
alanpq Jul 31, 2024
47ec2b1
fix: attempt to get wasm working
alanpq Aug 1, 2024
094a808
fix: remove unnecessary expect
alanpq Aug 7, 2024
4200686
feat: add ruzstd support (for wasm)
alanpq Aug 7, 2024
0375572
fix: remove wasm stuff
alanpq Aug 10, 2024
d897d90
chore: remove commented debug junk
alanpq Aug 10, 2024
f72dc66
chore: remove more debug logs
alanpq Aug 10, 2024
35ebd2d
feat: ReaderExt specific Error type
alanpq Aug 12, 2024
ef78c5b
feat: impl len prefixed string, bool + mat4 read helpers
alanpq Aug 12, 2024
830d8ce
feat: serde derives for Color
alanpq Aug 12, 2024
14ad168
feat: bin read support
alanpq Aug 12, 2024
0a4ab31
fix: force PropertyValueEnum to be exhaustive
alanpq Aug 12, 2024
2bca8b2
fix: make PropertyValueEnum variants public
alanpq Aug 12, 2024
131c7d4
chore: lint cleanup
alanpq Aug 12, 2024
68f34a9
feat: store BinTreeObject class_hash
alanpq Aug 12, 2024
a49c010
fix: propagate invalid bin prop kind error
alanpq Aug 12, 2024
4a31fe1
fix: remove debug logs
alanpq Aug 12, 2024
818de32
feat: u8/f32 color distinction (+ fix bin colors)
alanpq Aug 12, 2024
da262e0
feat: do bin object legacy read fallback hack
alanpq Aug 12, 2024
3cc6cc6
chore: add cargo-hack to nix flake
alanpq Aug 15, 2024
312a325
Merge branch 'main' into alan/feat/bin
alanpq Aug 15, 2024
617b7ef
Merge branch 'main' of github.com:LeagueToolkit/league-toolkit into a…
alanpq Aug 15, 2024
5a53be9
feat: impl BinProperty::size()
alanpq Oct 22, 2024
482b50b
feat: size check for struct bin value
alanpq Oct 22, 2024
61a00a6
feat: size check for container value
alanpq Oct 22, 2024
ebcf222
fix: rename InvalidPropertyType err -> InvalidPropertyTypePrimitive
alanpq Oct 22, 2024
da2a96c
feat: check for nested containers and non-prim map keys
alanpq Oct 22, 2024
01271c1
chore: remove todo
alanpq Oct 22, 2024
ad86088
feat: basic bin read test
alanpq Oct 22, 2024
0d58d1d
feat: proper size checks + add io::Seek constraint everywhere
alanpq Oct 23, 2024
3a70c04
chore (nix): add cargo-expand + remove duplicated input?
alanpq Oct 23, 2024
902c430
feat: add write helpers + make vec helpers more generic
alanpq Oct 23, 2024
cef0a04
feat: impl AsRef for Color<T>
alanpq Oct 23, 2024
763b717
feat: add WriteProperty trait
alanpq Oct 23, 2024
acf5469
feat: impl to_writer for BinProperty
alanpq Oct 23, 2024
c61abff
feat: impl WriteProperty for primitives
alanpq Oct 23, 2024
fc567ec
feat: impl WriteProperty for OptionalValue
alanpq Oct 23, 2024
146a1bf
feat: impl to_writer and kind for BinPropertyEnum
alanpq Oct 23, 2024
bb67fa9
fix: update bin read test
alanpq Oct 23, 2024
bc3dd21
fix: make all read/write trait bounds the same (+ Seek + ?Sized)
alanpq Oct 23, 2024
8acca90
feat: impl WriteProperty for ContainerValue
alanpq Oct 23, 2024
01b22bc
feat: impl WriteProperty for Struct/EmbeddedValue
alanpq Oct 23, 2024
5e32f0a
chore: better doc comment for measure()
alanpq Oct 23, 2024
ad9cfef
feat: impl WriteProperty for MapValue
alanpq Oct 23, 2024
ab494d4
chore: warning cleanup
alanpq Oct 23, 2024
e0a70e9
feat: impl WriteProperty for StringValue
alanpq Oct 23, 2024
be9d71a
feat: impl WriteProperty for UnorderedContainerValue
alanpq Oct 23, 2024
91ad9fc
fix: explicitly don't allow legacy optional write (for now)
alanpq Oct 23, 2024
896e8a5
fix: rebase woes
alanpq Oct 23, 2024
4df5657
fix: update bin read test snapshot
alanpq Oct 23, 2024
802dd07
feat: impl to_writer for BinTree/BinTreeObject
alanpq Oct 23, 2024
773ddd1
feat: `window` seek helper function
alanpq Oct 23, 2024
26ed347
fix: size seeking issue
alanpq Oct 23, 2024
4fd5235
chore (nix): add gdb to flake
alanpq Oct 23, 2024
4dcc81a
feat: add bin roundtrip test + reorg tests
alanpq Oct 23, 2024
bd3346d
fix: remove real data tests
alanpq Oct 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/league-toolkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ default = ["zstd"]
zstd = ["dep:zstd"]
ruzstd = ["dep:ruzstd"]

serde = ["dep:serde"]
serde = ["dep:serde", "glam/serde"]
rust_backends = [
"flate2/rust_backend",
"ruzstd",
Expand All @@ -20,7 +20,6 @@ bitflags = "2.5.0"
byteorder = "1.5.0"
flate2 = "1.0.30"
glam = { version = "0.27.0", features = ["glam-assert"] }
insta = "1.39.0"
lazy_static = "1.4.0"
log = "0.4.21"
memchr = "2.7.2"
Expand All @@ -31,6 +30,13 @@ zstd = { version = "0.13", default-features = false, optional = true }
ruzstd = { version = "0.7", optional = true }

serde = { version = "1.0.204", features = ["derive"], optional = true }
paste = "1.0.15"
miette = "7.2.0"
enum_dispatch = "0.3.13"

[dev-dependencies]
league-toolkit = {path = ".", features = ["serde"]}
approx = "0.5.1"
insta = { version = "1.39.0", features = ["ron"] }
serde = { version = "*", features = ["derive"] }
glam = { version = "*", features = ["glam-assert", "serde"] }
4 changes: 3 additions & 1 deletion crates/league-toolkit/src/core/mesh/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ pub enum ParseError {
#[error("Invalid '{0}' - got '{1}'")]
InvalidField(&'static str, String),
#[error("IO Error - {0}")]
ReaderError(#[from] std::io::Error),
IOError(#[from] std::io::Error),
#[error("UTF-8 Error - {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[error(transparent)]
ReaderError(#[from] crate::util::ReaderError),
}
3 changes: 0 additions & 3 deletions crates/league-toolkit/src/core/mesh/skinned/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use std::io::Read;

use byteorder::ReadBytesExt;
use glam::Vec3;
use num_enum::{IntoPrimitive, TryFromPrimitive};

Expand Down
2 changes: 1 addition & 1 deletion crates/league-toolkit/src/core/mesh/static/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl StaticMeshFace {
material,
vertex_ids,
uvs: (vec2(uvs.0, uvs.3), vec2(uvs.1, uvs.4), vec2(uvs.2, uvs.5)),
colors: (Color::ONE, Color::ONE, Color::ONE),
colors: (Color::<f32>::ONE, Color::<f32>::ONE, Color::<f32>::ONE),
})
}
}
3 changes: 0 additions & 3 deletions crates/league-toolkit/src/core/mesh/static/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use std::io::Read;

use byteorder::ReadBytesExt;
use glam::Vec3;

pub use face::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/league-toolkit/src/core/mesh/static/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl StaticMesh {
true => {
let mut v = Vec::with_capacity(vertex_count as usize);
for _ in 0..vertex_count {
v.push(reader.read_color::<LE>()?);
v.push(reader.read_color_f32::<LE>()?);
}
Some(v)
}
Expand Down
41 changes: 41 additions & 0 deletions crates/league-toolkit/src/core/meta/bin_tree/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::collections::HashMap;

mod object;
use super::error::ParseError;
pub use object::*;

pub mod read;
pub mod write;

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, PartialEq)]
pub struct BinTree {
pub is_override: bool,
pub version: u32,

pub objects: HashMap<u32, BinTreeObject>,
/// List of other property bins we depend on.
///
/// Property bins can depend on other property bins in a similar fashion to importing code libraries
pub dependencies: Vec<String>,

data_overrides: Vec<()>,
}

impl BinTree {
pub fn new(
objects: impl IntoIterator<Item = BinTreeObject>,
dependencies: impl IntoIterator<Item = String>,
) -> Self {
Self {
version: 3,
is_override: false,
objects: objects
.into_iter()
.map(|o: BinTreeObject| (o.path_hash, o))
.collect(),
dependencies: dependencies.into_iter().collect(),
data_overrides: Vec::new(),
}
}
}
69 changes: 69 additions & 0 deletions crates/league-toolkit/src/core/meta/bin_tree/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::{collections::HashMap, io};

use crate::util::{measure, window};

use super::{super::BinProperty, ParseError};
use byteorder::{ReadBytesExt as _, WriteBytesExt as _, LE};

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, PartialEq)]
pub struct BinTreeObject {
pub path_hash: u32,
pub class_hash: u32,
pub properties: HashMap<u32, BinProperty>,
}

impl BinTreeObject {
pub fn from_reader<R: io::Read + io::Seek + ?Sized>(
reader: &mut R,
class_hash: u32,
legacy: bool,
) -> Result<Self, ParseError> {
let size = reader.read_u32::<LE>()?;
let (real_size, value) = measure(reader, |reader| {
let path_hash = reader.read_u32::<LE>()?;

let prop_count = reader.read_u16::<LE>()? as usize;
let mut properties = HashMap::with_capacity(prop_count);
for _ in 0..prop_count {
let prop = BinProperty::from_reader(reader, legacy)?;
properties.insert(prop.name_hash, prop);
}

Ok::<_, ParseError>(Self {
path_hash,
class_hash,
properties,
})
})?;

if size as u64 != real_size {
return Err(ParseError::InvalidSize(size as _, real_size));
}
Ok(value)
}

pub fn to_writer<W: io::Write + io::Seek + ?Sized>(
&self,
writer: &mut W,
legacy: bool,
) -> io::Result<()> {
if legacy {
unimplemented!("legacy BinTreeObject write");
}
let size_pos = writer.stream_position()?;
writer.write_u32::<LE>(0)?;

let (size, _) = measure(writer, |writer| {
writer.write_u32::<LE>(self.path_hash)?;
writer.write_u16::<LE>(self.properties.len() as _)?;
for prop in self.properties.values() {
prop.to_writer(writer)?;
}
Ok::<_, io::Error>(())
})?;

window(writer, size_pos, |writer| writer.write_u32::<LE>(size as _))?;
Ok(())
}
}
113 changes: 113 additions & 0 deletions crates/league-toolkit/src/core/meta/bin_tree/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::{collections::HashMap, io};

use crate::core::meta::ParseError;

use super::{BinTree, BinTreeObject};
use crate::util::ReaderExt as _;
use byteorder::{ReadBytesExt as _, LE};

impl BinTree {
pub const PROP: u32 = u32::from_le_bytes(*b"PROP");
pub const PTCH: u32 = u32::from_le_bytes(*b"PTCH");
pub fn from_reader<R: io::Read + std::io::Seek + ?Sized>(
reader: &mut R,
) -> Result<Self, ParseError> {
let magic = reader.read_u32::<LE>()?;
let is_override = match magic {
Self::PROP => false,
Self::PTCH => {
let override_version = reader.read_u32::<LE>()?;
if override_version != 1 {
return Err(ParseError::InvalidFileVersion(override_version));
}

// It might be possible to create an override property bin
// and set the original file as a dependency.
// This seems to be the object count of the override section
let _maybe_override_object_count = reader.read_u32::<LE>()?;

let magic = reader.read_u32::<LE>()?;
if magic != Self::PROP {
// TODO (alan): repr this in the error
log::error!(
"Expected PROP ({:#x}) section after PTCH ({:#x}), got '{:#x}'",
Self::PROP,
Self::PTCH,
magic
);
return Err(ParseError::InvalidFileSignature);
}
true
}
_ => return Err(ParseError::InvalidFileSignature),
};

let version = reader.read_u32::<LE>()?;
if !matches!(version, 1..=3) {
// TODO (alan): distinguish override/non-override version
return Err(ParseError::InvalidFileVersion(version));
}

let dependencies = match version {
2.. => {
let dep_count = reader.read_u32::<LE>()?;
let mut dependencies = Vec::with_capacity(dep_count as _);
for _ in 0..dep_count {
dependencies.push(reader.read_len_prefixed_string::<LE>()?);
}
dependencies
}
_ => Vec::new(),
};

let obj_count = reader.read_u32::<LE>()? as usize;
let mut obj_classes = Vec::with_capacity(obj_count);
for _ in 0..obj_count {
obj_classes.push(reader.read_u32::<LE>()?);
}

let mut objects = HashMap::with_capacity(obj_count);
match Self::try_read_objects(reader, &obj_classes, &mut objects, false) {
Ok(_) => {}
Err(ParseError::InvalidPropertyTypePrimitive(kind)) => {
log::warn!("Invalid prop type {kind}. Trying reading objects as legacy.");
Self::try_read_objects(reader, &obj_classes, &mut objects, true)?;
}
e => e?,
}

let data_overrides = match (is_override, version) {
(true, 3..) => {
let count = reader.read_u32::<LE>()?;
let mut v = Vec::with_capacity(count as _);
for _ in 0..count {
v.push(()); // TODO: impl data overrides
}
v
}
_ => Vec::new(),
};

Ok(Self {
version,
is_override,
objects,
dependencies,
data_overrides,
})
}

fn try_read_objects<R: io::Read + std::io::Seek + ?Sized>(
reader: &mut R,
obj_classes: &[u32],
objects: &mut HashMap<u32, BinTreeObject>,
legacy: bool,
) -> Result<(), ParseError> {
objects.clear();
for &class_hash in obj_classes {
let tree_obj = BinTreeObject::from_reader(reader, class_hash, legacy)?;
objects.insert(tree_obj.path_hash, tree_obj);
}
Ok(())
}
}
57 changes: 57 additions & 0 deletions crates/league-toolkit/src/core/meta/bin_tree/write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::io;

use super::BinTree;
use crate::util::WriterExt as _;
use byteorder::{WriteBytesExt as _, LE};

impl BinTree {
pub fn to_writer<W: io::Write + io::Seek + ?Sized>(
&self,
writer: &mut W,
legacy: bool,
) -> io::Result<()> {
match self.is_override {
true => todo!("implement is_override BinTree write"),
false => {
writer.write_u32::<LE>(Self::PROP)?;
}
}

writer.write_u32::<LE>(self.version)?;

if !self.dependencies.is_empty() && self.version < 2 {
// FIXME: move this assertion to object creation
panic!(
"cannot write BinTree with dependencies @ version {}",
self.version
);
}

if self.version >= 2 {
writer.write_u32::<LE>(self.dependencies.len() as _)?;
for dep in &self.dependencies {
writer.write_len_prefixed_string::<LE, _>(dep)?;
}
}

writer.write_u32::<LE>(self.objects.len() as _)?;
for obj in self.objects.values() {
writer.write_u32::<LE>(obj.class_hash)?;
}
for obj in self.objects.values() {
obj.to_writer(writer, legacy)?;
}

if self.is_override {
if self.version < 3 {
panic!("cannot write data overrides @ version {}", self.version);
}
writer.write_u32::<LE>(self.data_overrides.len() as _)?;
// TODO: impl data overrides
//for o in &self.data_overrides {
//}
}

Ok(())
}
}
29 changes: 29 additions & 0 deletions crates/league-toolkit/src/core/meta/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use miette::Diagnostic;

use super::property::BinPropertyKind;

#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum ParseError {
#[error("Invalid file signature")]
InvalidFileSignature,
#[error("Invalid file version '{0}'")]
InvalidFileVersion(u32),
#[error("Invalid '{0}' - got '{1}'")]
InvalidField(&'static str, String),
#[error("Invalid property kind - {0}")]
InvalidPropertyTypePrimitive(#[from] num_enum::TryFromPrimitiveError<BinPropertyKind>),
#[error("Invalid size - expected {0}, got {1} bytes")]
InvalidSize(u64, u64),

#[error("Container type {0:?} cannot be nested!")]
InvalidNesting(BinPropertyKind),
#[error("Invalid map key type {0:?}, only primitive types can be used as keys.")]
InvalidKeyType(BinPropertyKind),

#[error(transparent)]
ReaderError(#[from] crate::util::ReaderError),
#[error("IO Error - {0}")]
IOError(#[from] std::io::Error),
#[error("UTF-8 Error - {0}")]
Utf8Error(#[from] std::str::Utf8Error),
}
10 changes: 10 additions & 0 deletions crates/league-toolkit/src/core/meta/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod property;
pub use property::BinProperty;

mod bin_tree;
pub use bin_tree::*;

pub mod error;
pub use error::*;

pub mod traits;
Loading
Loading