From 17c9d552ebf282a0199062df5e9a081207afb8da Mon Sep 17 00:00:00 2001 From: Xevion Date: Thu, 28 Nov 2024 20:04:16 -0600 Subject: [PATCH] Begin work on patch & packing modules, add flate2/zip for zlib compressed archives --- src-tauri/Cargo.lock | 258 ++++++++++++++++++++++++++++++++++++++++- src-tauri/Cargo.toml | 3 + src-tauri/src/lib.rs | 2 + src-tauri/src/pack.rs | 89 ++++++++++++++ src-tauri/src/patch.rs | 39 +++++++ 5 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 src-tauri/src/pack.rs create mode 100644 src-tauri/src/patch.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b4fb688..1c96bc5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -17,6 +17,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -62,6 +73,15 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "ashpd" version = "0.10.2" @@ -260,6 +280,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cairo-rs" version = "0.18.5" @@ -333,6 +374,8 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -388,6 +431,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "cocoa" version = "0.26.0" @@ -437,6 +490,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.4.0" @@ -502,6 +561,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +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.4.2" @@ -608,6 +682,12 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + [[package]] name = "deranged" version = "0.3.11" @@ -618,6 +698,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -639,6 +730,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -860,8 +952,10 @@ dependencies = [ name = "factorio-achievements-fixer" version = "0.1.0" dependencies = [ + "flate2", "human_bytes", "humanize-duration", + "rand 0.8.5", "serde", "serde_json", "simple-home-dir", @@ -871,6 +965,7 @@ dependencies = [ "tauri-plugin-fs", "tauri-plugin-shell", "time", + "zip", ] [[package]] @@ -1362,6 +1457,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -1678,6 +1782,15 @@ dependencies = [ "cfb", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -1769,6 +1882,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.72" @@ -1902,12 +2024,28 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "mac" version = "0.1.1" @@ -2437,6 +2575,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3191,6 +3339,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -3365,6 +3524,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -5054,6 +5219,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "zerovec" version = "0.10.4" @@ -5076,6 +5261,77 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "zip" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap 2.6.0", + "lzma-rs", + "memchr", + "pbkdf2", + "rand 0.8.5", + "sha1", + "thiserror 2.0.3", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zvariant" version = "5.1.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 65f4419..f4a5749 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,4 +28,7 @@ simple-home-dir = "0.4.4" human_bytes = "0.4.3" time = "0.3.36" humanize-duration = "0.0.6" +flate2 = "1.0.35" +zip = "2.2.1" +rand = "0.8.5" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 49df870..f6aa691 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,7 @@ mod dirs; mod format; +mod pack; +mod patch; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { diff --git a/src-tauri/src/pack.rs b/src-tauri/src/pack.rs new file mode 100644 index 0000000..dd73987 --- /dev/null +++ b/src-tauri/src/pack.rs @@ -0,0 +1,89 @@ +use std::{env, path::PathBuf}; +use rand::Rng; +use zip::ZipArchive; + +struct UnpackResult { + levels: Vec, +} + +fn get_tempdir() -> PathBuf { + let mut tempdir = env::temp_dir(); + + // Generate a random suffix for the tempdir + let suffix: String = (0..6) + .map(|_| rand::thread_rng().gen_range(b'A'..=b'z') as char) + .collect(); + + tempdir.push(format!("factorio_achievements_fixer.{}", suffix)); + tempdir +} + +fn unpack(src: PathBuf, dest: PathBuf) -> Result { + // Ensure the src and dest paths exist + if !src.exists() { + return Err("Source path does not exist".to_string()); + } else if !dest.exists() { + return Err("Destination path does not exist".to_string()); + } + + // Ensure src is zlib compressed .zip, and dest is a directory + if src.extension() != Some("zip".as_ref()) { + return Err("Source path is not a .zip file".to_string()); + } else if !dest.is_dir() { + return Err("Destination path is not a directory".to_string()); + } + + // Unpack the src to dest + let file = std::fs::File::open(src); + if file.is_err() { + return Err("Failed to open source file: ".to_string() + &file.err().unwrap().to_string()); + + } + let mut archive = ZipArchive::new(file.unwrap()).unwrap(); + + // archive.extract(); + + // Return pathbufs for each level file of the unpacked archive + + Err("Not implemented".to_string()) +} + +fn pack(src: PathBuf, dest: PathBuf) -> Result { + // Ensure src exists, ensure dest does not exist (but parent folder does) + // Ensure src is a directory, and dest is a .zip file + // Pack the src to dest + Err("Not implemented".to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] +fn test_get_tempdir_uniqueness() { + use std::collections::HashSet; + + // Generate 1000 paths + let mut paths = HashSet::new(); + let mut filenames = Vec::with_capacity(1000); + + for i in 0..1000 { + let path = get_tempdir(); + + // Ensure UTF-8 valid filename compatibility + let filename = path.file_name() + .and_then(|n| n.to_str()) + .unwrap_or_else(|| panic!("Path {} should have valid UTF-8 filename", i)); + + filenames.push(filename.to_string()); + + // Insert path into set and ensure it wasn't already present + assert!(paths.insert(path.clone()), + "Duplicate path found at iteration {}: {}", i, filename); + } + + // Verify we got exactly 1000 unique paths + assert_eq!(paths.len(), 1000, + "Expected 1000 unique paths, got {}", paths.len()); + } +} \ No newline at end of file diff --git a/src-tauri/src/patch.rs b/src-tauri/src/patch.rs new file mode 100644 index 0000000..80aca9a --- /dev/null +++ b/src-tauri/src/patch.rs @@ -0,0 +1,39 @@ +// Scan level files for pattern +// Provide list of patterns to user for patch options, byte offsets with changes +// Byte offset will be single, as Map Editor/Command byte offsets don't change order +use std::path::PathBuf; + +// State is required in order to track the patterns scanned, and ensure that level files are correctly closed/moved when exiting the application. +struct Patcher { + src_archive: Option, + working_dir: Option, + patch: Option, +} + + +struct Patch { + // The relative path to the s pecific level file being patched + file: PathBuf, + // The byte offset (from the start of the file) of the first (earliest) 8 byte 0xFF pattern ('fblock') + fblock_pattern_index: u64, + // The customizable marker pattern offset, specifically of the achievement byte that would be modified by the patch. + marker_pattern_offset: u64, +} + +impl Patcher { + // Create a new Patcher instance + pub fn new() -> Self { + Patcher { + src_archive: None, + working_dir: None, + patch: None, + // Initialize any required fields here + } + } + + // Cleanup method to ensure level files are correctly closed/moved + pub fn cleanup(&self) { + // Perform cleanup operations here + } +} +