diff --git a/Cargo.lock b/Cargo.lock index 51dbfe6..d832a2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,36 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit-set" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -107,6 +137,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" @@ -129,6 +171,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.5.60" @@ -224,6 +275,30 @@ dependencies = [ "url", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.5.0" @@ -264,6 +339,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "deranged" version = "0.5.8" @@ -273,6 +358,16 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs" version = "6.0.0" @@ -379,6 +474,28 @@ dependencies = [ "unicase", ] +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "filetime_creation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c25b5d475550e559de5b0c0084761c65325444e3b6c9e298af9cefe7a9ef3a5f" +dependencies = [ + "cfg-if", + "filetime", + "windows-sys 0.52.0", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -425,6 +542,7 @@ dependencies = [ "log", "quick-xml", "serde", + "sevenz-rust", "thiserror", "toml", "ureq", @@ -441,6 +559,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -670,6 +798,16 @@ dependencies = [ "syn", ] +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "keyvalues-parser" version = "0.2.3" @@ -728,7 +866,10 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ + "bitflags", "libc", + "plain", + "redox_syscall", ] [[package]] @@ -762,6 +903,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "lzma-rust" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baab2bbbd7d75a144d671e9ff79270e903957d92fb7386fd39034c709bd2661" +dependencies = [ + "byteorder", +] + [[package]] name = "memchr" version = "2.8.0" @@ -787,6 +937,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "nt-time" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de419e64947cd8830e66beb584acc3fb42ed411d103e3c794dda355d1b374b5" +dependencies = [ + "chrono", + "time", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -867,6 +1027,12 @@ dependencies = [ "serde", ] +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -945,6 +1111,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -1061,6 +1236,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "same-file" version = "1.0.6" @@ -1145,6 +1326,34 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sevenz-rust" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26482cf1ecce4540dc782fc70019eba89ffc4d87b3717eb5ec524b5db6fdefef" +dependencies = [ + "bit-set", + "byteorder", + "crc", + "filetime_creation", + "js-sys", + "lzma-rust", + "nt-time", + "sha2", + "wasm-bindgen", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1312,6 +1521,12 @@ version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "ucd-trie" version = "0.1.7" @@ -1420,6 +1635,51 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + [[package]] name = "webpki-roots" version = "1.0.6" diff --git a/Cargo.toml b/Cargo.toml index 60ab5d6..fbd870a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ libloot = "0.29.0" log = "0.4.29" quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] } serde = { version = "1.0.228", features = ["derive"] } +sevenz-rust = { version = "0.6.1", default-features = false } thiserror = "2.0.18" toml = "1.0.3" ureq = { version = "3.2.0", features = ["json"] } diff --git a/src/cli.rs b/src/cli.rs index 096561b..9faae7e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,4 +19,5 @@ pub enum Commands { LoadOrder { instance: String }, ApiCheck, Download { url: String }, + Unpack { id: String, path: String }, } diff --git a/src/main.rs b/src/main.rs index 9b6481f..3f43296 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use crate::{ instance::{files_to_install_mod, insert_mod_to_instance}, nexus::{NexusAPI, download_nxm}, types::RootConfig, + unpacker::unpack, }; mod activator; @@ -21,6 +22,7 @@ mod load_order; mod mod_config_installer; mod nexus; mod types; +mod unpacker; mod utils; fn command_activate( @@ -39,9 +41,9 @@ fn command_add(root_config: &RootConfig, instance_id: &str, mod_id: &str) -> any .mod_by_id(mod_id) .ok_or(anyhow!("Can't find mod in config"))?; - let files = files_to_install_mod(root_config, &instance, mod_to_install)?; + let files = files_to_install_mod(root_config, &instance, &mod_to_install)?; - match insert_mod_to_instance(&mut instance, mod_to_install, &files, 0) { + match insert_mod_to_instance(&mut instance, &mod_to_install, &files, 0) { Ok(_) => { instance.save_to_file()?; Ok(()) @@ -104,6 +106,25 @@ fn command_download(root_config: &RootConfig, nxm_url: &str) -> anyhow::Result<( Ok(()) } +fn command_unpack( + root_config: &mut RootConfig, + id: &str, + file: impl AsRef, +) -> anyhow::Result<()> { + if root_config.game_by_id(id).is_some() { + error!("Mod already present"); + return Err(anyhow!("Mod already exists")); + } + + let new_mod = unpack(root_config, id, file)?; + + root_config.add_mod(&new_mod); + + root_config.save_to_file()?; + + Ok(()) +} + fn setup_logger() { env_logger::builder() .filter_level(log::LevelFilter::max()) @@ -118,7 +139,7 @@ fn main() -> Result<(), Box> { let args = Args::parse(); debug!("Loading config from {:?}", args.config); - let root_config = RootConfig::load_from_file(args.config)?; + let mut root_config = RootConfig::load_from_file(args.config)?; match args.command { cli::Commands::Activate { instance, target } => { @@ -137,6 +158,9 @@ fn main() -> Result<(), Box> { cli::Commands::Download { url } => { command_download(&root_config, &url)?; } + cli::Commands::Unpack { id, path } => { + command_unpack(&mut root_config, &id, path)?; + } } Ok(()) diff --git a/src/unpacker.rs b/src/unpacker.rs new file mode 100644 index 0000000..728bb57 --- /dev/null +++ b/src/unpacker.rs @@ -0,0 +1,28 @@ +use std::{fs, path::Path}; + +use anyhow::anyhow; + +use crate::types::{ModConfig, RootConfig}; + +pub fn unpack( + root_config: &RootConfig, + id: &str, + path: impl AsRef, +) -> anyhow::Result { + let extract_to = root_config.mod_location().join(id); + + if fs::exists(&extract_to)? { + return Err(anyhow!("File already exists")); + } + + unpack_7z_file(path, &extract_to)?; + + let new_mod = ModConfig::new(id, id); + + Ok(new_mod) +} + +fn unpack_7z_file(path: impl AsRef, to: impl AsRef) -> anyhow::Result<()> { + sevenz_rust::decompress_file(path, to)?; + Ok(()) +}