From 68eb498238d4fe30e4cc0ac9bb4ff5eb4fae1412 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Fri, 15 Apr 2022 00:45:26 +0000 Subject: [PATCH] most stock _txt.gmfs now match --- Cargo.lock | 134 +++++++++++++++++++++++++++++++++++++++++++ gmflib/src/gmi.rs | 3 + gmflib/src/types.rs | 91 ++++++++++++++++++++++------- gmfmacros/src/lib.rs | 13 ++++- gmftool/Cargo.toml | 2 + gmftool/src/main.rs | 74 ++++++++++++------------ 6 files changed, 257 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c319f7..6c05a92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "gmflib" version = "0.1.0" @@ -22,9 +61,47 @@ dependencies = [ name = "gmftool" version = "0.1.0" dependencies = [ + "env_logger", "gmflib", + "log", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -43,6 +120,23 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + [[package]] name = "syn" version = "1.0.90" @@ -54,8 +148,48 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/gmflib/src/gmi.rs b/gmflib/src/gmi.rs index 4111d55..9b71f3f 100644 --- a/gmflib/src/gmi.rs +++ b/gmflib/src/gmi.rs @@ -187,6 +187,9 @@ pub struct TLV { impl TLV { pub fn check(&self, r: &mut ReadStream) -> ReadResult<()> { + if self.length == 0 { + return Ok(()) + } let expected = self.start_pos + self.length as usize + 12usize; if expected != r.pos { return Err(ReadError { diff --git a/gmflib/src/types.rs b/gmflib/src/types.rs index ae0cba8..f1d84ea 100644 --- a/gmflib/src/types.rs +++ b/gmflib/src/types.rs @@ -4,6 +4,7 @@ use gmfmacros::{GMISerializable, GMASerializable}; use crate::{ gmi, gma, + gma::Atom, gmi::Serializable as GMISerializable, gma::Serializable as GMASerializable, }; @@ -182,12 +183,27 @@ pub enum Shading { Blinn = 0xc, } -#[derive(Debug, GMISerializable,GMASerializable)] +#[derive(Debug, GMISerializable)] pub enum Falloff { In = 0, InTwoSided = 1, } +impl gma::Serializable for Falloff { + fn write(&self, _name: S, w: &mut gma::WriteStream) -> gma::WriteResult<()> { + match self { + Falloff::In => { + w.emit_pair("*MATERIAL_FALLOFF", "In")?; + }, + Falloff::InTwoSided => { + w.emit("*MATERIAL_TWOSIDED")?; + w.emit_pair("*MATERIAL_FALLOFF", "In")?; + }, + } + Ok(()) + } +} + #[derive(Debug,GMISerializable,GMASerializable)] pub enum XPType { Other = 0, @@ -298,9 +314,9 @@ impl gmi::Serializable for Object { (5, 3) => (Object::Light(r.read("light")?), false), (21, 2) => (Object::AttachmentPoint(r.read("attachmentpoint")?), true), (30, 2) => (Object::Simulation(r.read("simulation")?), false), - (31, 4) => (Object::RBCollection(r.read("rigidbodycollection")?), false), - (42, 3) => (Object::ConstraintSolver(r.read("constraintsolver")?), false), - (49, 2) => (Object::AngularDashpot(r.read("angulardashpot")?), false), + (31, 4) => (Object::RBCollection(r.read("rigidbodycollection")?), true), + (42, 3) => (Object::ConstraintSolver(r.read("constraintsolver")?), true), + (49, 2) => (Object::AngularDashpot(r.read("angulardashpot")?), true), _ => return Err(r.error(format!("unknown object type ({}, {})", tlv.tag, tlv.flags))), }; if check { @@ -339,14 +355,10 @@ pub struct GeometryObject { pub mesh: Mesh, } -#[derive(Debug,GMISerializable,GMASerializable)] -#[gma_name("CAMERA")] +#[derive(Debug,GMISerializable)] pub struct CameraObject { - #[gma_name("NODE_NAME")] pub name: String, - #[gma_name("NODE_TM")] pub tm1: TransformMatrix, - #[gma_name("NODE_TM")] pub tm2: Option, pub type_: CameraType, pub hither: f32, @@ -357,6 +369,25 @@ pub struct CameraObject { pub tdist: f32, } +impl gma::Serializable for CameraObject { + fn write(&self, _name: S, w: &mut gma::WriteStream) -> gma::WriteResult<()> { + + w.push("*CAMERA")?; + self.name.write("*NODE_NAME", w)?; + self.type_.write("*CAMERA_TYPE", w)?; + self.tm1.write("*NODE_TM", w)?; + self.tm2.write("*NODE_TM", w)?; + self.hither.write("*CAMERA_HITHER", w)?; + self.yon.write("*CAMERA_YON", w)?; + self.near.write("*CAMERA_NEAR", w)?; + self.far.write("*CAMERA_FAR", w)?; + self.fov.write("*CAMERA_FOV", w)?; + self.tdist.write("*CAMERA_TDIST", w)?; + w.pop()?; + Ok(()) + } +} + #[derive(Debug,GMISerializable,GMASerializable)] pub enum CameraType { Target = 0, @@ -635,8 +666,7 @@ pub struct LightObject { pub attn_start: f32, pub attn_end: f32, pub tdist: f32, - #[gma_name("USE FAR ATTENUATION = ")] - pub use_far_attenuation: u32, + pub use_far_attenuation: FarAttenuation, } #[derive(Debug,GMISerializable,GMASerializable)] @@ -662,6 +692,23 @@ impl gmi::Serializable for LightSpotShape { } } +#[derive(Debug)] +pub struct FarAttenuation(u32); + +impl gmi::Serializable for FarAttenuation { + fn read(r: &mut gmi::ReadStream) -> gmi::ReadResult { + // Uninitialized memory in stock .GMFs. Whoops. + let v = u32::read(r)? & 0xff; + Ok(FarAttenuation(v)) + } +} + +impl gma::Serializable for FarAttenuation { + fn write(&self, _name: S, w: &mut gma::WriteStream) -> gma::WriteResult<()> { + w.emit(&format!("*USE FAR ATTENUATION = \t{}", self.0)) + } +} + #[derive(Debug,GMISerializable,GMASerializable)] #[gma_name("GMID_ATTACHMENTPT")] pub struct AttachmentPointObject { @@ -686,7 +733,9 @@ pub struct ConstraintSolverObject { #[derive(Debug,GMISerializable,GMASerializable)] #[gmi_tagged_nolen(44, 2)] +#[gma_name("GMID_HAVOK_CONSTRAINT_LIST")] pub struct ConstraintList { + #[gma_name("")] pub constraints: Vec, } @@ -699,14 +748,12 @@ pub enum Constraint { impl gmi::Serializable for Constraint { fn read(r: &mut gmi::ReadStream) -> gmi::ReadResult { let tlv = r.tlv()?; - let (res, check) = match (tlv.tag, tlv.flags) { - (47, 2) => (Constraint::Hinge(r.read("hinge")?), false), - (53, 2) => (Constraint::PointToPoint(r.read("pointtopoint")?), false), + let res = match (tlv.tag, tlv.flags) { + (47, 2) => Constraint::Hinge(r.read("hinge")?), + (53, 2) => Constraint::PointToPoint(r.read("pointtopoint")?), _ => return Err(r.error(format!("unknown constraint type ({}, {})", tlv.tag, tlv.flags))), }; - if check { - tlv.check(r)?; - } + tlv.check(r)?; Ok(res) } } @@ -721,6 +768,7 @@ impl gma::Serializable for Constraint { } #[derive(Debug,GMISerializable,GMASerializable)] +#[gma_name("GMID_HAVOK_HINGE_CONSTRAINT")] pub struct HingeConstraint { #[gma_name("NODE_NAME")] pub name: String, @@ -734,14 +782,17 @@ pub struct HingeConstraint { #[gma_name("SPIN_AXIS")] pub spin_axis: [f32; 3], #[gma_name("IS_LIMITED")] + #[gma_space_delim] pub is_limited: u8, #[gma_name("FRICTION")] + #[gma_space_delim] pub friction: f32, #[gma_name("ANGLE_LIMITS")] pub angle_limits: [f32; 2], } #[derive(Debug,GMISerializable,GMASerializable)] +#[gma_name("GMID_HAVOK_POINTTOPOINT")] pub struct PointToPointConstraint { #[gma_name("NODE_NAME")] pub name: String, @@ -762,8 +813,8 @@ pub struct PTPPoints { impl gma::Serializable for PTPPoints { fn write(&self, _name: S, w: &mut gma::WriteStream) -> gma::WriteResult<()> { w.emit("*POINT 1")?; - w.emit(&format!("(\t{:.6}\t{:.6}\t{:.6}\t)", self.a.x, self.a.y, self.a.z))?; - w.emit(&format!("(\t{:.6}\t{:.6}\t{:.6}\t)", self.b.x, self.b.y, self.b.z))?; + w.emit(&format!("( {:.6}\t{:.6}\t{:.6} )", self.a.x, self.a.y, self.a.z))?; + w.emit(&format!("( {:.6}\t{:.6}\t{:.6} )", self.b.x, self.b.y, self.b.z))?; Ok(()) } } @@ -963,7 +1014,7 @@ impl gma::Serializable for Quaternion { fn write(&self, name: S, w: &mut gma::WriteStream) -> gma::WriteResult<()> { w.emit(&name.to_string())?; let values = self.0.iter().map(|v| format!("{:.6}", v)).collect::>().join("\t"); - w.emit(&format!("(\t{}\t)", values))?; + w.emit(&format!("( {} )", values))?; Ok(()) } } diff --git a/gmfmacros/src/lib.rs b/gmfmacros/src/lib.rs index f0321db..21759e2 100644 --- a/gmfmacros/src/lib.rs +++ b/gmfmacros/src/lib.rs @@ -229,7 +229,7 @@ impl GMAValue { } } -#[proc_macro_derive(GMASerializable, attributes(gma_name, gma_name_bare, gma_value, gma_skip))] +#[proc_macro_derive(GMASerializable, attributes(gma_name, gma_name_bare, gma_value, gma_skip, gma_space_delim))] pub fn gma_serializable_macro(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -267,6 +267,7 @@ pub fn gma_serializable_macro(input: TokenStream) -> TokenStream { let writes: Vec = fields.named.iter().map(|field| { let field_ident = &field.ident; let field_skip = field.attrs.iter().any(|a| a.path.is_ident("gma_skip")); + let field_space = field.attrs.iter().any(|a| a.path.is_ident("gma_space_delim")); let field_name = GMAName::from_attrs(&field.attrs) .map(|s| if s.bare { @@ -280,8 +281,14 @@ pub fn gma_serializable_macro(input: TokenStream) -> TokenStream { if field_skip { quote! { } } else { - quote! { - self.#field_ident.write(#field_name, _w)?; + if field_space { + quote! { + _w.emit(&format!("{} {}", #field_name, self.#field_ident.atom()))?; + } + } else { + quote! { + self.#field_ident.write(#field_name, _w)?; + } } } }).collect(); diff --git a/gmftool/Cargo.toml b/gmftool/Cargo.toml index 4e848e7..8d23659 100644 --- a/gmftool/Cargo.toml +++ b/gmftool/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" [dependencies] gmflib = { path = "../gmflib" } +log = "0" +env_logger = "0" diff --git a/gmftool/src/main.rs b/gmftool/src/main.rs index 9df7865..4a18370 100644 --- a/gmftool/src/main.rs +++ b/gmftool/src/main.rs @@ -1,43 +1,43 @@ -fn main() -> std::io::Result<()> { - let txt_gmfs = vec![ - "Arenas/practice arena/practice_arena_txt.gmf", - "Arenas/octagon/octagon_txt.gmf", - "Arenas/parkinglot/parkinglot_txt.gmf", - "Arenas/flextop/flextop_txt.gmf", - "Arenas/barrels/barrels_txt.gmf", - "Arenas/cones/cones_txt.gmf", - "Arenas/skull/skull_txt.gmf", - "Arenas/crates/crates_txt.gmf", - "Arenas/bridge/bridge_txt.gmf", - "Arenas/cinderblocks/blocks_txt.gmf", - "Arenas/event_results/event_results_txt.gmf", - "Arenas/compressor/compressor_txt.gmf", - "Arenas/hilltop/hilltop_txt.gmf", - "Arenas/kingofhill/king_txt.gmf", - "Arenas/electric/electric_txt.gmf", - "Arenas/half/half_txt.gmf", - "Arenas/box/boxarena_txt.gmf", - "Arenas/tabletop/clawtop_txt.gmf", - "Arenas/ramps/ramps_txt.gmf", - "UI/botlab/AP_box_selected_txt.gmf", - "UI/botlab/heading_arrow_txt.gmf", - "UI/botlab/AP_box_unselected_txt.gmf", - "Components/cannon/cannon_txt.gmf", - ]; - for txt_gmf_path in txt_gmfs.iter() { - let txt_gmf_path = "/home/q3k/Games/RA2/Robot Arena 2 v1.4/".to_string() + txt_gmf_path; - let gmf_path = txt_gmf_path.replace("_txt.gmf", ".gmf"); - println!("Testing {} vs {}...", txt_gmf_path, gmf_path); +fn main() { + env_logger::init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info")); - let f = std::fs::File::open(gmf_path).unwrap(); - let gmf = gmflib::GMF::read_gmi(f).unwrap(); + let args: Vec = std::env::args().collect(); + if args.len() != 3 { + log::error!("Usage: {} foo.gmf foo_txt.gmf", args[0]); + return; } + let gmf_path = &args[1]; + let txt_gmf_path = &args[2]; - //let f = std::fs::File::open("/home/q3k/Games/RA2/Robot Arena 2 v1.4/Arenas/barrels/barrels.gmf")?; - //let gmf = gmflib::GMF::read_gmi(f).unwrap(); + let f = match std::fs::File::open(gmf_path) { + Ok(f) => f, + Err(e) => { + log::error!("Could not open {}: {:?}", gmf_path, e); + return; + }, + }; + let mut res = match std::fs::File::create(txt_gmf_path) { + Ok(f) => f, + Err(e) => { + log::error!("Could not write {}: {:?}", txt_gmf_path, e); + return; + }, + }; - //gmf.write_gma(std::io::stdout()).unwrap(); - - Ok(()) + let gmf = match gmflib::GMF::read_gmi(f) { + Ok(gmf) => gmf, + Err(e) => { + log::error!("Reading GMI failed: {}", e.msg); + log::info!("Parse stack:"); + for frame in e.stack.iter() { + log::info!(" {} at offset {}", frame.name, frame.loc); + } + return; + }, + }; + if let Err(e) = gmf.write_gma(&mut res) { + log::error!("Writing GMA failed: {}", e.msg); + } }