diff --git a/Cargo.lock b/Cargo.lock index 1e68efe..bcf5f2c 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,6 +269,30 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hard-xml" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3477ce594ff6d821c38fc3f8d28744b9bac0340c94b152ebb0f8a1fd5b740f54" +dependencies = [ + "hard-xml-derive", + "jetscii", + "lazy_static", + "memchr", + "xmlparser", +] + +[[package]] +name = "hard-xml-derive" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa4585e2b133d2479ff3f03febd76972234cc04b40cdb374fab11a7f7b797ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -309,6 +333,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "jetscii" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e" + [[package]] name = "js-sys" version = "0.3.58" @@ -350,6 +380,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "memoffset" version = "0.6.5" @@ -417,8 +453,11 @@ dependencies = [ "crc", "criterion", "half 2.1.0", + "hard-xml", "libz-sys", "paste", + "serde", + "serde_json", ] [[package]] @@ -747,3 +786,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" diff --git a/Cargo.toml b/Cargo.toml index d81d533..23ff78c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,25 @@ criterion = { git = "https://github.com/bheisler/criterion.rs", branch="version- retail_game_testing = [] [dependencies] +# used for jamcrc implementation, should eventually move away from it crc = "3.0.0" + +# amazing binary parsing/writing library binrw = "0.9.2" + +# used for zlib compression in sqpack files libz-sys = { version = "1.1.8", default-features = false } + +# nice to have features rust is lacking at the moment bitfield-struct = "0.1.7" paste = "1.0.7" -half = "2.1.0" \ No newline at end of file + +# needed for half-float support which FFXIV uses in it's model data +half = "2.1.0" + +# needed for havok xml parsing +hard-xml = "1.13.0" + +# needed for textools skel parsing +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/src/skeleton.rs b/src/skeleton.rs new file mode 100644 index 0000000..d7158c9 --- /dev/null +++ b/src/skeleton.rs @@ -0,0 +1,139 @@ +use crate::gamedata::MemoryBuffer; +use hard_xml::XmlRead; + +#[derive(Debug)] +pub struct Bone { + name: String, + parent_index: usize, + + position: [f32; 3], + rotation: [f32; 4], + scale: [f32; 3] +} + +#[derive(Debug)] +pub struct Skeleton { + bones : Vec +} + +impl Skeleton { + /// Parses a Havok XML packfile generated by the Havok SDK. + pub fn from_packfile(buffer : &MemoryBuffer) -> Option { + #[derive(XmlRead, Debug)] + #[xml(tag = "hkpackfile")] + struct HkPackfile { + #[xml(child = "hksection")] + sections: Vec, + #[xml(attr = "toplevelobject")] + top_level_object : String + } + + #[derive(XmlRead, Debug)] + #[xml(tag = "hksection")] + struct HkSection { + #[xml(attr = "name")] + name: String, + + #[xml(child = "hkobject")] + objects: Vec + } + + #[derive(XmlRead, Debug)] + #[xml(tag = "hkobject")] + struct HkObject { + #[xml(attr = "name")] + name: Option, + + #[xml(attr = "class")] + class: Option, + + #[xml(child = "hkparam")] + params: Vec + } + + #[derive(XmlRead, Debug)] + #[xml(tag = "hkparam")] + struct HkParam { + #[xml(attr = "name")] + name : String, + + #[xml(attr = "className")] + class_name : Option, + + #[xml(attr = "variant")] + variant : Option, + + #[xml(child = "hkobject")] + objects : Vec, + + #[xml(text)] + content : String + } + + let pak = HkPackfile::from_str(&mut std::str::from_utf8(&buffer).unwrap()) + .expect("Failed to parse sidecar file!"); + + // find the root level object + let root_level_object = pak.sections[0].objects.iter() + .find(|s| s.name.as_ref() == Some(&pak.top_level_object)) + .expect("Cannot locate root level object."); + + println!("{:#?}", root_level_object); + + println!("{:#?}", pak); + + None + } + + /// Parses the TexTools skeleton format, as a nice alternative to packfiles. + pub fn from_skel(buffer : &MemoryBuffer) -> Option { + let mut string_repr = String::from_utf8(buffer.to_vec()).unwrap(); + + // for some reason, textools does NOT write valid JSON. + // let's begin by surrounding all of their json object blocks with an array, which is a valid + // JSON root. + string_repr.insert(0, '['); + string_repr.push(']'); + + // then we turn all of newlines into commas, except of course for the last one! + string_repr = string_repr.replacen("\n", ",", string_repr.matches("\n").count() - 1); + + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + #[serde(rename_all = "PascalCase")] + struct BoneObject { + bone_name : String, + bone_number : i32, + bone_parent : i32, + pose_matrix : [f32; 16] + } + + let json_bones : Vec = serde_json::from_str(&string_repr).unwrap(); + + let mut skeleton = Skeleton { + bones: vec![] + }; + + for bone in &json_bones { + skeleton.bones.push(Bone { + name: bone.bone_name.clone(), + parent_index: 0, + position: [0.0; 3], + rotation: [0.0; 4], + scale: [0.0; 3] + }); + } + + // assign parenting + for bone in &json_bones { + if bone.bone_parent != -1 { + let mut new_bone = &mut skeleton.bones[bone.bone_number as usize]; + + new_bone.parent_index = bone.bone_parent as usize; + } + } + + Some(skeleton) + } +} \ No newline at end of file