diff --git a/Cargo.lock b/Cargo.lock index 558d63d..9ce97a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,54 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "anstream" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -20,6 +68,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "deflate" version = "1.0.0" @@ -49,6 +143,7 @@ name = "fot-save-edit" version = "0.1.0" dependencies = [ "byteorder", + "clap", "deflate", "encoding_rs", "indexmap", @@ -62,6 +157,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "indexmap" version = "2.0.0" @@ -86,3 +187,116 @@ name = "memmem" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index c6bb7ae..60b105c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,14 @@ name = "fot-save-edit" version = "0.1.0" edition = "2021" +auhtors = ["mykola2312", "puuuuh"] +repository = "https://github.com/mykola2312/fot-save-edit/" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] byteorder = "1.4.3" +clap = { version = "4.4.2", features = ["derive"] } deflate = "1.0.0" encoding_rs = "0.8.33" indexmap = "2.0.0" diff --git a/src/fot.rs b/src/fot.rs index f917159..21e0953 100644 --- a/src/fot.rs +++ b/src/fot.rs @@ -1,14 +1,14 @@ -mod attributes; -mod decoder; -mod entity; -mod entitylist; -mod esh; -mod ferror; -mod fstring; -mod raw; +pub mod attributes; +pub mod decoder; +pub mod entity; +pub mod entitylist; +pub mod esh; +pub mod ferror; +pub mod fstring; +pub mod raw; pub mod save; -mod sgd; -mod ssg; -mod stream; -mod tag; -mod world; +pub mod sgd; +pub mod ssg; +pub mod stream; +pub mod tag; +pub mod world; diff --git a/src/fot/esh.rs b/src/fot/esh.rs index 8b3897e..7be5d2b 100644 --- a/src/fot/esh.rs +++ b/src/fot/esh.rs @@ -267,12 +267,12 @@ impl ESH { pub fn get_nested(&self, name: &str) -> Result { let value = match self.get(name) { Some(value) => value, - None => return Err(FE::NoESHValue) + None => return Err(FE::NoESHValue), }; if let ESHValue::Binary(bin) = value { let mut rd = ReadStream::new(bin, 0); - + let _ = rd.read_u32()?; rd.read::() } else { diff --git a/src/fot/ferror.rs b/src/fot/ferror.rs index d8c5aa4..af4e7e7 100644 --- a/src/fot/ferror.rs +++ b/src/fot/ferror.rs @@ -12,7 +12,7 @@ pub enum FError { NoESHValue, ESHValueNonBinary, AttributesNonBinary, - ValueNoESBIN + ValueNoESBIN, } impl std::fmt::Display for FError { @@ -35,7 +35,7 @@ impl std::fmt::Display for FError { FE::NoESHValue => write!(f, "Entity has no specific ESH value"), FE::ESHValueNonBinary => write!(f, "ESH value is not binary"), FE::AttributesNonBinary => write!(f, "Attributes Binary != true"), - FE::ValueNoESBIN => write!(f, "Value has no esbin") + FE::ValueNoESBIN => write!(f, "Value has no esbin"), } } } diff --git a/src/fot/world.rs b/src/fot/world.rs index b154822..f8391c5 100644 --- a/src/fot/world.rs +++ b/src/fot/world.rs @@ -1,6 +1,5 @@ use super::decoder::DecoderCtx; use super::entitylist::{EntityEncoding, EntityList}; -use super::esh::ESHValue; use super::ferror::FError as FE; use super::fstring::FString; use super::sgd::SGD; @@ -28,30 +27,6 @@ pub struct World { impl World { const WORLD_HDR_LEN: usize = 0x13; - - pub fn test(&mut self) -> Result<(), FE> { - //let actor_type = self.entlist.get_type_idx("Actor").unwrap(); - //let ent = self.entlist.get_entity_mut(2122); - let ent = self.entlist.get_entity_mut(2122); - let esh = ent.get_esh_mut()?; - for (name, value) in &esh.props { - println!("{} {}", name, value); - } - //self.entlist.dump_to_entfile(ent, Path::new("D:\\actor.ent"))?; - - println!(""); - let mut attribs = esh.get_nested("Current Attributes")?; - for (name, value) in &attribs.props { - println!("{} {}", name, value); - } - - attribs.set("hitPoints", ESHValue::Int(999)); - attribs.set("poisonPoints", ESHValue::Int(0)); - - esh.set_nested("Current Attributes", attribs)?; - - Ok(()) - } } pub type WorldOffsetSize = (usize, usize); diff --git a/src/main.rs b/src/main.rs index a40d270..9f88b16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,116 @@ #![deny(rust_2018_idioms)] -use std::env; +use clap::{Parser, Subcommand, ValueEnum}; +use std::collections::HashMap; +use std::io::{stdout, BufWriter, Write}; use std::path::Path; mod fot; +use fot::attributes::*; +use fot::entitylist::EntityList; use fot::save::Save; -fn main() { - let args: Vec<_> = env::args().collect(); - let save_path = args.get(1).unwrap(); - let out_path = match args.get(2) { - Some(path) => path, - None => "out.sav" +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Input file path (.sav or .ent) + #[arg(short, long)] + input: String, + + // Specify save file or ent file type + #[arg(value_enum)] + kind: Kind, + + /// Output file path + #[arg(short, long)] + output: String, + + #[command(subcommand)] + command: Commands, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Kind { + Save, + Ent, +} + +#[derive(Subcommand, Debug)] +enum Commands { + ListEntities, + /// Find entities, kv = key1=value,key2=value2 + FindEntities { + kv: String, + }, +} + +fn list_entities(entlist: &EntityList) { + let mut bf = BufWriter::new(stdout().lock()); + for (id, ent) in entlist { + let type_name = if ent.type_idx != 0xFFFF { + entlist.get_type_name(ent.type_idx).str.as_str() + } else { + "" + }; + write!(bf, "{}\t{}\n", id, type_name).expect("failed to write stdout"); + } +} + +fn find_entities(entlist: &EntityList, line: String) { + let kv = line + .split(",") + .map(|kv| kv.split_once("=")) + .collect::>>() + .unwrap(); + + let mut bf = BufWriter::new(stdout().lock()); + for (id, ent) in entlist { + let type_name = if ent.type_idx != 0xFFFF { + entlist.get_type_name(ent.type_idx).str.as_str() + } else { + "" + }; + + let esh = match &ent.esh { + Some(esh) => esh, + None => continue, + }; + + for (name, value) in &esh.props { + let key = name.str.as_str(); + if kv.contains_key(key) { + let svalue = value.to_string(); + if svalue == kv[key] { + write!(bf, "{}\t{}\n", id, type_name).expect("failed to write stdout"); + } + } + } + } +} + +fn do_save(cli: Cli) { + let mut save = match Save::load(Path::new(cli.input.as_str())) { + Ok(save) => save, + Err(fe) => panic!("{}", fe), }; - let mut save = Save::load(Path::new(save_path)).expect("load save"); - save.world.test().expect("test"); - save.save(Path::new(out_path)).expect("failed to save"); + match cli.command { + Commands::ListEntities => { + list_entities(&save.world.entlist); + }, + Commands::FindEntities { kv } => { + find_entities(&save.world.entlist, kv); + } + } +} + +fn main() { + let cli = Cli::parse(); + + match cli.kind { + Kind::Save => do_save(cli), + Kind::Ent => todo!(), + } + + //let mut save = Save::load(Path::new(save_path)).expect("load save"); + //save.save(Path::new(out_path)).expect("failed to save"); }