### Example Searches in Rust Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/crypto/custom_encryption/fn.maple_custom_decrypt.html?search= These examples illustrate common search patterns that can be performed, including searching for vectors, boolean conversions, and option transformations. ```rust std::vec ``` ```rust u32 -> bool ``` ```rust Option, (T -> U) -> Option ``` -------------------------------- ### Example Search: u32 to bool Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/crypto/constants/static.UserKey.html?search= Illustrates a search pattern for converting u32 to bool. ```rust u32 -> bool ``` -------------------------------- ### FromIterator Example (Success) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Demonstrates collecting an iterator of Results into a single Result, where all elements are Ok. ```rust let v = vec![1, 2]; let res: Result, &'static str> = v.iter().map(|x: &u32| x.checked_add(1).ok_or("Overflow!") ).collect(); assert_eq!(res, Ok(vec![2, 3])); ``` -------------------------------- ### Initialize WzBinaryReader Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Creates a new WzBinaryReader instance. Requires a reader, an IV, WZ header information, and the starting offset. ```rust pub fn new(reader: R, iv: [u8; 4], header: WzHeader, start_offset: u64) -> Self { WzBinaryReader { reader, wz_key: WzKey::new(iv), hash: 0, header, start_offset, } } ``` -------------------------------- ### IntoIterator Example (Ok) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Shows how to convert an Ok Result into an iterator that yields the contained value. ```rust let x: Result = Ok(5); let v: Vec = x.into_iter().collect(); assert_eq!(v, [5]); ``` -------------------------------- ### V2 Entry Decryption Setup Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html Sets up variables and calculates necessary values for V2 entry decryption, including deriving keys, nonces, and counters. ```rust let salt = "test_v2_salt"; let entry_name = "Mob/test.img"; let entry_key = [0x55u8; 16]; let original = vec![0x73u8; 200]; // Encrypt like v2: only first min(size, 1024) bytes via ChaCha20 let img_key = derive_img_key_v2(salt, entry_name, &entry_key); let (nonce, counter) = derive_img_nonce_counter(salt); let crypted_size = original.len().min(DOUBLE_ENCRYPT_BYTES); let decrypt_len = (crypted_size + CHACHA_BLOCK_SIZE - 1) & !(CHACHA_BLOCK_SIZE - 1); let mut encrypted = vec![0u8; decrypt_len]; ``` -------------------------------- ### Example Search: std::vec Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/directory.rs.html?search= Demonstrates a search for 'std::vec'. This is a common pattern for finding vector-related functionalities. ```rust * std::vec ``` -------------------------------- ### FromIterator Example (Failure) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Demonstrates collecting an iterator of Results into a single Result, where an Err is encountered. ```rust let v = vec![1, 2, 0]; let res: Result, &'static str> = v.iter().map(|x: &u32| x.checked_sub(1).ok_or("Underflow!") ).collect(); assert_eq!(res, Err("Underflow!")); ``` -------------------------------- ### Example Search: Option, (T -> U) -> Option Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/directory.rs.html?search= Shows a search pattern for mapping operations on optional values. This is relevant for functional programming styles involving Option types. ```rust * Option, (T -> U) -> Option ``` -------------------------------- ### unwrap_or_else Example Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Demonstrates the use of unwrap_or_else to provide a default value when the Result is an Err. ```rust assert_eq!(Ok(2).unwrap_or_else(count), 2); assert_eq!(Err("foo").unwrap_or_else(count), 3); ``` -------------------------------- ### Example Search: u32 -> bool Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/directory.rs.html?search= Illustrates a search for type transformations from 'u32' to 'bool'. Useful for finding functions that perform boolean checks on unsigned 32-bit integers. ```rust * u32 -> bool ``` -------------------------------- ### Product of Results Example Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Demonstrates using the `product` method on an iterator of Results. If any element fails to parse, the entire operation returns an Err. Otherwise, it returns the Ok product. ```Rust let nums = vec!["5", "10", "1", "2"]; let total: Result = nums.iter().map(|w| w.parse::()).product(); assert_eq!(total, Ok(100)); let nums = vec!["5", "10", "one", "2"]; let total: Result = nums.iter().map(|w| w.parse::()).product(); assert!(total.is_err()); ``` -------------------------------- ### Test: get_slice at offset Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Validates that `get_slice` can retrieve a portion of the key stream starting from an offset and that its contents match individual `get()` calls. ```Rust #[test] fn test_get_slice_at_offset() { let mut key = WzKey::new([0x4D, 0x23, 0xC7, 0x2B]); let snapshot: Vec = key.get_slice(50, 10).to_vec(); assert_eq!(snapshot.len(), 10); // Verify it matches individual get() calls for (i, &b) in snapshot.iter().enumerate() { assert_eq!(b, key.get(50 + i)); } } ``` -------------------------------- ### WzKey::new Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/keys/struct.WzKey.html Creates a new WzKey instance with a default initialization vector. ```APIDOC ## WzKey::new ### Description Creates a new `WzKey` instance initialized with a default 4-byte initialization vector. ### Signature `pub fn new(iv: [u8; 4]) -> Self` ### Parameters * `iv` - A 4-byte array representing the initialization vector. ``` -------------------------------- ### Get Key Slice Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html?search=u32+-%3E+bool Retrieves a slice of bytes from the key buffer, starting at the specified offset and with the given length. If the buffer is not large enough, it automatically expands. ```rust pub fn get_slice(&mut self, start: usize, len: usize) -> &[u8] { self.ensure_size(start + len); &self.keys[start..start + len] } ``` -------------------------------- ### WzKey::new Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/keys/struct.WzKey.html?search= Creates a new WzKey instance with a default initialization vector. ```APIDOC ## pub fn new(iv: [u8; 4]) -> Self ### Description Creates a new `WzKey` instance. ### Parameters * **iv** ([u8; 4]) - The initialization vector. ### Returns A new `WzKey` instance. ``` -------------------------------- ### WzKey::new Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Creates a new `WzKey` instance with the specified initialization vector. ```APIDOC ## WzKey::new ### Description Creates a new `WzKey` instance with the specified initialization vector. ### Method `pub fn new(iv: [u8; 4]) -> Self` ### Parameters * `iv`: [u8; 4] - The initialization vector for key generation. ``` -------------------------------- ### unsafe unwrap_err_unchecked Example (Err) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Shows how to use unwrap_err_unchecked to get the Err value without checking for errors. This is unsafe and should only be used when the Result is guaranteed to be Err. ```rust let x: Result = Err("emergency failure"); assert_eq!(unsafe { x.unwrap_err_unchecked() }, "emergency failure"); ``` -------------------------------- ### unsafe unwrap_unchecked Example (Ok) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Shows how to use unwrap_unchecked to get the Ok value without checking for errors. This is unsafe and should only be used when the Result is guaranteed to be Ok. ```rust let x: Result = Ok(2); assert_eq!(unsafe { x.unwrap_unchecked() }, 2); ``` -------------------------------- ### Building and Parsing a Multi-Entry V2 MS File Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search=std%3A%3Avec Demonstrates creating a WZ MS file (version 2) with multiple entries and then parsing it. It iterates through the parsed entries to verify names, sizes, and decrypted data against the original entries. ```rust let file_name = "multi_v2.ms"; let salt = "xyz123"; let entries = vec![ MsSaveEntry { name: "Map/town.img".into(), image_data: vec![0x73; 500], entry_key: [0x22; 16], original_size: None, }, MsSaveEntry { name: "Npc/shop.img".into(), image_data: vec![0x73; 1500], entry_key: [0x33; 16], original_size: None, }, MsSaveEntry { name: "Mob/boss.img".into(), image_data: vec![0x42; 3000], entry_key: [0x44; 16], original_size: None, }, ]; let saved = build_ms_file(file_name, salt, &entries, MsVersion::V2).unwrap(); let parsed = parse_ms_file(&saved, file_name).unwrap(); assert_eq!(parsed.version, MsVersion::V2); assert_eq!(parsed.entries.len(), 3); for i in 0..3 { assert_eq!(parsed.entries[i].name, entries[i].name); assert_eq!(parsed.entries[i].size, entries[i].image_data.len()); let decrypted = decrypt_entry_data(&saved, &parsed, i).unwrap(); assert_eq!( &decrypted[..entries[i].image_data.len()], &entries[i].image_data[..] ); } ``` -------------------------------- ### Write and Read Basic String Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_writer.rs.html?search= Demonstrates writing a simple string to the WZ binary format and then reading it back to verify. ```Rust writer.write_wz_string("Hello").unwrap(); let data = finish_writer(writer); let mut reader = make_reader(data); assert_eq!(reader.read_wz_string().unwrap(), "Hello"); ``` -------------------------------- ### Read String with Start Offset in Rust Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Reads a string from a WzBinaryReader when the reader is initialized with a starting offset. The provided offset is relative to the actual data start. ```Rust #[test] fn test_read_string_at_offset_with_start_offset() { // String at buffer position 10. start_offset=5, so caller passes offset=15. let encoded = encode_wz_ascii("Offset"); let mut data = vec![0u8; 10]; data.extend_from_slice(&encoded); let header = dummy_header(data.len() as u64); let mut reader = WzBinaryReader::new(Cursor::new(data), [0; 4], header, 5); let result = reader.read_string_at_offset(15).unwrap(); assert_eq!(result, "Offset"); } ``` -------------------------------- ### Rust: Read String with Start Offset Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=std%3A%3Avec Reads a string from a WzBinaryReader when the reader is initialized with a starting offset. The provided offset to read_string_at_offset is relative to the buffer's beginning, not the reader's internal start offset. ```rust let encoded = encode_wz_ascii("Offset"); let mut data = vec![0u8; 10]; data.extend_from_slice(&encoded); let header = dummy_header(data.len() as u64); let mut reader = WzBinaryReader::new(Cursor::new(data), [0; 4], header, 5); let result = reader.read_string_at_offset(15).unwrap(); assert_eq!(result, "Offset"); ``` -------------------------------- ### Test: get is deterministic Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Ensures that calling `get` repeatedly with the same IV produces the same sequence of keys. ```Rust #[test] fn test_get_deterministic() { let mut key1 = WzKey::new([0x4D, 0x23, 0xC7, 0x2B]); let mut key2 = WzKey::new([0x4D, 0x23, 0xC7, 0x2B]); for i in 0..32 { assert_eq!(key1.get(i), key2.get(i), "mismatch at index {}", i); } } ``` -------------------------------- ### Test: get auto-expands Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Verifies that calling `get` on an empty key buffer automatically triggers key stream expansion. ```Rust #[test] fn test_get_auto_expands() { let mut key = WzKey::new([0; 4]); // keys buffer is empty, get(0) should auto-expand let _byte = key.get(0); assert!(!key.keys.is_empty()); } ``` -------------------------------- ### WzBinaryWriter::new Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_writer.rs.html Creates a new WzBinaryWriter instance. It initializes the writer with a given underlying writer, an IV for encryption, and WZ header information. ```APIDOC ## WzBinaryWriter::new ### Description Creates a new WzBinaryWriter instance. It initializes the writer with a given underlying writer, an IV for encryption, and WZ header information. ### Parameters - **writer**: The underlying writer implementing `Write` and `Seek` traits. - **iv**: A 4-byte array representing the Initialization Vector for encryption. - **header**: The `WzHeader` information for the WZ file. ### Returns A new `WzBinaryWriter` instance. ``` -------------------------------- ### Available Data with Data Start Offset Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=std%3A%3Avec Calculates available data considering a non-zero data start offset within the file. ```rust fn test_available_with_data_start() { // file_size=50, data_start=10, pos=0 → end = 10+50=60, available = 60-0=60 let mut reader = make_reader_with_header(vec![0; 100], 10, 50); assert_eq!(reader.available().unwrap(), 60); } ``` -------------------------------- ### Test WzProperty::SubProperty get for existing child Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/properties/mod.rs.html?search=std%3A%3Avec Tests the get method on WzProperty::SubProperty to retrieve existing child properties by name. ```rust let prop = WzProperty::SubProperty { properties: vec![ ("x".into(), WzProperty::Int(10)), ("y".into(), WzProperty::Int(20)), ], }; assert_eq!(prop.get("x").unwrap().as_int(), Some(10)); assert_eq!(prop.get("y").unwrap().as_int(), Some(20)); ``` -------------------------------- ### Create Dummy WZHeader Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/header.rs.html Creates a WzHeader with default values, useful for testing or initialization when only the file size is known. ```rust pub fn dummy(file_size: u64) -> Self { WzHeader { ident: String::new(), file_size, data_start: 0, copyright: String::new(), } } ``` -------------------------------- ### Test: Get Auto Expands Buffer Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html?search=u32+-%3E+bool Verifies that calling `get` on an empty key buffer automatically triggers `ensure_size` and populates the buffer. ```rust #[test] fn test_get_auto_expands() { let mut key = WzKey::new([0; 4]); // keys buffer is empty, get(0) should auto-expand let _byte = key.get(0); assert!(!key.keys.is_empty()); } ``` -------------------------------- ### WzKey::new Constructor Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Initializes a new WzKey with a given 4-byte IV. The key stream is initially empty. ```Rust pub fn new(iv: [u8; 4]) -> Self { WzKey { iv, user_key: None, keys: Vec::new(), } } ``` -------------------------------- ### WzKey::get_slice Method Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/keys.rs.html Retrieves a slice of bytes from the key stream, starting at `start` and with a length of `len`. Automatically expands the key stream if necessary. ```Rust pub fn get_slice(&mut self, start: usize, len: usize) -> &[u8] { self.ensure_size(start + len); &self.keys[start..start + len] } ``` -------------------------------- ### Test leaf WzProperty get Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/properties/mod.rs.html?search=std%3A%3Avec Tests that calling get() on leaf WzProperty types (e.g., Int) correctly returns None, as they do not contain child properties. ```rust assert!(WzProperty::Int(1).get("anything").is_none()); ``` -------------------------------- ### Rust Test: V2 Decrypt Entry Roundtrip Setup Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search= Sets up the initial state for testing V2 entry decryption. This involves deriving the image key and nonce/counter, and preparing an encrypted buffer with original data. ```rust #[test] fn test_v2_decrypt_entry_roundtrip() { let salt = "test_v2_salt"; let entry_name = "Mob/test.img"; let entry_key = [0x55u8; 16]; let original = vec![0x73u8; 200]; // Encrypt like v2: only first min(size, 1024) bytes via ChaCha20 let img_key = derive_img_key_v2(salt, entry_name, &entry_key); let (nonce, counter) = derive_img_nonce_counter(salt); let crypted_size = original.len().min(DOUBLE_ENCRYPT_BYTES); let decrypt_len = (crypted_size + CHACHA_BLOCK_SIZE - 1) & !(CHACHA_BLOCK_SIZE - 1); let mut encrypted = vec![0u8; decrypt_len]; encrypted[..original.len()].copy_from_slice(&original); ``` -------------------------------- ### Test WzProperty::SubProperty get for missing child Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/properties/mod.rs.html?search=std%3A%3Avec Tests the get method on WzProperty::SubProperty to ensure it returns None when the requested child property does not exist. ```rust let prop = WzProperty::SubProperty { properties: vec![("x".into(), WzProperty::Int(10))], }; assert!(prop.get("z").is_none()); ``` -------------------------------- ### Test get method for missing child properties Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/properties/mod.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Verifies that the get method returns None when attempting to retrieve a child property that does not exist in a WzProperty::SubProperty. ```rust let prop = WzProperty::SubProperty { properties: vec![("x".into(), WzProperty::Int(10))], }; assert!(prop.get("z").is_none()); ``` -------------------------------- ### Initialize WzBinaryWriter Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_writer.rs.html?search= Creates a new WzBinaryWriter instance with the provided writer, IV, and WZ header. ```rust pub fn new(writer: W, iv: [u8; 4], header: WzHeader) -> Self { WzBinaryWriter { writer, wz_key: WzKey::new(iv), hash: 0, header, string_cache: HashMap::new(), } } ``` -------------------------------- ### build_ms_file Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/ms_file/fn.build_ms_file.html Constructs a WZ file in MS format. This function takes a file name, a salt, a slice of MsSaveEntry items, and a MsVersion, then returns the WZ file content as a Vec or a WzResult error. ```APIDOC ## Function build_ms_file ### Description Constructs a WZ file in MS format. This function takes a file name, a salt, a slice of MsSaveEntry items, and a MsVersion, then returns the WZ file content as a Vec or a WzResult error. ### Signature ```rust pub fn build_ms_file( file_name: &str, salt: &str, entries: &[MsSaveEntry], version: MsVersion, ) -> WzResult> ``` ### Parameters * `file_name`: &str - The name of the file to be built. * `salt`: &str - The salt to be used in the file construction. * `entries`: &[MsSaveEntry] - A slice of `MsSaveEntry` items to include in the WZ file. * `version`: MsVersion - The version of the WZ file format to use. ### Returns * `WzResult>` - Ok containing the WZ file content as a byte vector, or an Err containing a `WzResult` error. ``` -------------------------------- ### Test get method for finding child properties Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/properties/mod.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Demonstrates using the get method to find and retrieve specific child properties by their name from a WzProperty::SubProperty. ```rust let prop = WzProperty::SubProperty { properties: vec![ ("x".into(), WzProperty::Int(10)), ("y".into(), WzProperty::Int(20)), ], }; assert_eq!(prop.get("x").unwrap().as_int(), Some(10)); assert_eq!(prop.get("y").unwrap().as_int(), Some(20)); ``` -------------------------------- ### WzDirectoryEntry::get_offsets Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/directory/struct.WzDirectoryEntry.html?search=u32+-%3E+bool Gets the offsets for the WzDirectoryEntry. ```APIDOC ## WzDirectoryEntry::get_offsets ### Description Gets the offsets for the WzDirectoryEntry. ### Signature `pub fn get_offsets(&mut self, cur_offset: u32) -> u32` ### Parameters - **cur_offset**: `u32` - The current offset. ### Returns - `u32` - The computed offset. ``` -------------------------------- ### WzDirectoryEntry::get_img_offsets Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/directory/struct.WzDirectoryEntry.html?search=u32+-%3E+bool Gets the image offsets for the WzDirectoryEntry. ```APIDOC ## WzDirectoryEntry::get_img_offsets ### Description Gets the image offsets for the WzDirectoryEntry. ### Signature `pub fn get_img_offsets(&mut self, cur_offset: u32) -> u32` ### Parameters - **cur_offset**: `u32` - The current offset. ### Returns - `u32` - The computed image offset. ``` -------------------------------- ### Generic Implementations for WzImageEntry Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/directory/struct.WzImageEntry.html?search= Shows blanket implementations for various traits like Any, Borrow, CloneToUninit, DeserializeOwned, From, Into, Same, ToOwned, TryFrom, and TryInto. ```rust fn type_id(&self) -> TypeId ``` ```rust fn borrow(&self) -> &T ``` ```rust fn borrow_mut(&mut self) -> &mut T ``` ```rust unsafe fn clone_to_uninit(&self, dest: *mut u8) ``` ```rust fn from(t: T) -> T ``` ```rust fn into(self) -> U ``` ```rust type Output = T ``` ```rust type Owned = T ``` ```rust fn to_owned(&self) -> T ``` ```rust fn clone_into(&self, target: &mut T) ``` ```rust type Error = Infallible ``` ```rust fn try_from(value: U) -> Result>::Error> ``` ```rust type Error = >::Error ``` ```rust fn try_into(self) -> Result>::Error> ``` -------------------------------- ### WzDirectoryEntry::new Constructor Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/directory.rs.html?search=std%3A%3Avec Creates a new WzDirectoryEntry with a given name and entry type, initializing other fields to default values. ```Rust pub fn new(name: String, entry_type: u8) -> Self { WzDirectoryEntry { name, size: 0, checksum: 0, offset: 0, entry_type, offset_size: 0, subdirectories: Vec::new(), images: Vec::new(), } } ``` -------------------------------- ### Get Offsets for WzDirectoryEntry Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/directory/struct.WzDirectoryEntry.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Retrieves the offsets for the directory entry. ```rust pub fn get_offsets(&mut self, cur_offset: u32) -> u32 ``` -------------------------------- ### ChaCha20 TypeId Implementation Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/crypto/chacha20/struct.ChaCha20.html?search=std%3A%3Avec Gets the TypeId of the ChaCha20 instance. ```rust fn type_id(&self) -> TypeId ``` -------------------------------- ### Build V2 MapleStory File Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search= Constructs a V2 MapleStory file from a file name, salt, and a list of entries. This involves generating random bytes, encoding the salt, calculating header hashes, and encrypting entry data. ```rust fn build_ms_file_v2(file_name: &str, salt: &str, entries: &[MsSaveEntry]) -> WzResult> { let file_name_lower = file_name.to_lowercase(); let mut output = Vec::new(); let rand_bytes = generate_rand_bytes(rand_byte_count(&file_name_lower)); let shifted_rand: Vec = rand_bytes.iter().map(|&b| ((b as i8) >> 1) as u8).collect(); output.extend_from_slice(&rand_bytes); let raw_version_byte = MS_VERSION_V2 ^ shifted_rand[0]; output.push(raw_version_byte); let (raw_salt_bytes, hashed_salt_len, salt_u16_sum) = v2_encode_salt(salt, &shifted_rand); write_i32_le(&mut output, hashed_salt_len); output.extend_from_slice(&raw_salt_bytes); let file_name_with_salt = format!("{}{}", file_name_lower, salt); let header_hash = hashed_salt_len + raw_version_byte as i32 + MS_VERSION_V2 as i32 + entries.len() as i32 + salt_u16_sum; let mut header_block = [0u8; CHACHA_BLOCK_SIZE]; header_block[0..4].copy_from_slice(&header_hash.to_le_bytes()); header_block[4..8].copy_from_slice(&(entries.len() as i32).to_le_bytes()); let header_key = derive_chacha_key(&file_name_with_salt, false); let empty_nonce = [0u8; CHACHA_NONCE_LEN]; ChaCha20::new(&header_key, &empty_nonce, 0).process(&mut header_block); output.extend_from_slice(&header_block); // entry_start = header_pos + 8 + pad + 64 (block), so inter-pad = 8 + pad let epa = entry_pad_amount(&file_name_lower); let inter_pad_len = 8 + epa; output.extend(std::iter::repeat(0u8).take(inter_pad_len)); let entry_key_chacha = derive_chacha_key(&file_name_with_salt, true); let mut writer = ChaCha20StreamWriter::new(&entry_key_chacha, &empty_nonce); let mut block_offset: usize = 0; for entry in entries { let aligned_size = entry.aligned_size(); let block_idx = block_offset / BLOCK_ALIGNMENT; let ek_sum: i32 = entry.entry_key.iter().map(|&b| b as i32).sum(); let flags: i32 = 0; let unk1: i32 = 0; let unk2: i32 = 0; let checksum = flags + block_idx as i32 + entry.image_data.len() as i32 + aligned_size as i32 + unk1 + ek_sum; writer.write_string(&entry.name); writer.write_i32(checksum); writer.write_i32(flags); writer.write_i32(block_idx as i32); writer.write_i32(entry.image_data.len() as i32); writer.write_i32(aligned_size as i32); writer.write_i32(unk1); writer.write_i32(unk2); writer.write_bytes(&entry.entry_key); writer.write_i32(0); // unk3 writer.write_i32(0); // unk4 block_offset += aligned_size; } let encrypted_entries = writer.finish(); output.extend_from_slice(&encrypted_entries); output } ``` -------------------------------- ### WzBinaryReader Methods Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html Provides methods for initializing the reader, managing its position, and reading primitive and WZ-specific data types. ```APIDOC ## Struct: WzBinaryReader ### Description A reader for WZ binary files that handles encrypted strings, compressed integers, and offsets. It is ported from MapleLib's `WzBinaryReader.cs`. ### Methods #### `new(reader: R, iv: [u8; 4], header: WzHeader, start_offset: u64) -> Self` Creates a new `WzBinaryReader` instance. - **reader** (R): The underlying reader implementing `Read` and `Seek` traits. - **iv** ([u8; 4]): The initial vector for WZ encryption. - **header** (WzHeader): The header information for the WZ file. - **start_offset** (u64): The starting offset of the WZ data. #### `position(&mut self) -> WzResult` Returns the current position of the reader in the stream. #### `seek(&mut self, pos: u64) -> WzResult<()>` Seeks the reader to a specific position. - **pos** (u64): The position to seek to. #### `available(&mut self) -> WzResult` Calculates the number of available bytes to read in the WZ file based on the header and current position. #### Primitive Reads: - `read_u8(&mut self) -> WzResult`: Reads an unsigned 8-bit integer. - `read_u16(&mut self) -> WzResult`: Reads an unsigned 16-bit integer. - `read_i16(&mut self) -> WzResult`: Reads a signed 16-bit integer. - `read_u32(&mut self) -> WzResult`: Reads an unsigned 32-bit integer. - `read_i32(&mut self) -> WzResult`: Reads a signed 32-bit integer. - `read_i64(&mut self) -> WzResult`: Reads a signed 64-bit integer. - `read_f32(&mut self) -> WzResult`: Reads a 32-bit floating-point number. - `read_f64(&mut self) -> WzResult`: Reads a 64-bit floating-point number. - `read_i8(&mut self) -> WzResult`: Reads a signed 8-bit integer. #### `read_bytes(&mut self, len: usize) -> WzResult>` Reads a specified number of bytes from the reader. - **len** (usize): The number of bytes to read. #### WZ-Specific Compressed Reads: - `read_compressed_int(&mut self) -> WzResult`: Reads a WZ-compressed signed 32-bit integer. - `read_compressed_long(&mut self) -> WzResult`: Reads a WZ-compressed signed 64-bit integer. #### WZ Encrypted String Reads: - `read_wz_string(&mut self) -> WzResult`: Reads a WZ-encrypted string, automatically handling both Unicode and ASCII variants. - `read_wz_unicode_string(&mut self, indicator: i8) -> WzResult`: Reads a WZ-encrypted Unicode string. - `read_wz_ascii_string(&mut self, indicator: i8) -> WzResult`: Reads a WZ-encrypted ASCII string. ``` -------------------------------- ### WzBinaryWriter::position Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_writer.rs.html Gets the current position of the underlying writer. ```APIDOC ## WzBinaryWriter::position ### Description Gets the current position of the underlying writer. ### Returns A `WzResult` containing the current stream position as a `u64`. ``` -------------------------------- ### Getting Current Position Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=std%3A%3Avec Retrieves the current position of the reader within the stream. ```Rust pub fn position(&mut self) -> WzResult { Ok(self.reader.stream_position()?) } ``` -------------------------------- ### Get Current Position Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_reader.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Retrieves the current position of the reader within the stream. ```rust pub fn position(&mut self) -> WzResult { Ok(self.reader.stream_position()?) } ``` -------------------------------- ### WzHeader Implementations Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/header/struct.WzHeader.html?search= Provides methods for interacting with the WzHeader, including parsing, writing, and creating dummy headers. ```APIDOC ## impl WzHeader ### `pub fn dummy(file_size: u64) -> Self` Creates a dummy WzHeader with the specified file size. ### `pub fn parse(reader: &mut R) -> WzResult` Parses a WzHeader from a given reader. ### `pub fn write(&self, writer: &mut W) -> WzResult<()>` Writes the WzHeader to a given writer. ``` -------------------------------- ### WZ File V2 Building Logic Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Constructs a V2 WZ file, including generating random bytes, encoding the salt, calculating header hashes, and encrypting file entries. Requires WZ file name, salt, and a list of entries. ```rust fn build_ms_file_v2(file_name: &str, salt: &str, entries: &[MsSaveEntry]) -> WzResult> { let file_name_lower = file_name.to_lowercase(); let mut output = Vec::new(); let rand_bytes = generate_rand_bytes(rand_byte_count(&file_name_lower)); let shifted_rand: Vec = rand_bytes.iter().map(|&b| ((b as i8) >> 1) as u8).collect(); output.extend_from_slice(&rand_bytes); let raw_version_byte = MS_VERSION_V2 ^ shifted_rand[0]; output.push(raw_version_byte); let (raw_salt_bytes, hashed_salt_len, salt_u16_sum) = v2_encode_salt(salt, &shifted_rand); write_i32_le(&mut output, hashed_salt_len); output.extend_from_slice(&raw_salt_bytes); let file_name_with_salt = format!("{}{}", file_name_lower, salt); let header_hash = hashed_salt_len + raw_version_byte as i32 + MS_VERSION_V2 as i32 + entries.len() as i32 + salt_u16_sum; let mut header_block = [0u8; CHACHA_BLOCK_SIZE]; header_block[0..4].copy_from_slice(&header_hash.to_le_bytes()); header_block[4..8].copy_from_slice(&(entries.len() as i32).to_le_bytes()); let header_key = derive_chacha_key(&file_name_with_salt, false); let empty_nonce = [0u8; CHACHA_NONCE_LEN]; ChaCha20::new(&header_key, &empty_nonce, 0).process(&mut header_block); output.extend_from_slice(&header_block); // entry_start = header_pos + 8 + pad + 64 (block), so inter-pad = 8 + pad let epa = entry_pad_amount(&file_name_lower); let inter_pad_len = 8 + epa; output.extend(std::iter::repeat(0u8).take(inter_pad_len)); let entry_key_chacha = derive_chacha_key(&file_name_with_salt, true); let mut writer = ChaCha20StreamWriter::new(&entry_key_chacha, &empty_nonce); let mut block_offset: usize = 0; for entry in entries { let aligned_size = entry.aligned_size(); let block_idx = block_offset / BLOCK_ALIGNMENT; let ek_sum: i32 = entry.entry_key.iter().map(|&b| b as i32).sum(); let flags: i32 = 0; let unk1: i32 = 0; let unk2: i32 = 0; let checksum = flags + block_idx as i32 + entry.image_data.len() as i32 + aligned_size as i32 + unk1 + ek_sum; writer.write_string(&entry.name); writer.write_i32(checksum); writer.write_i32(flags); writer.write_i32(block_idx as i32); writer.write_i32(entry.image_data.len() as i32); writer.write_i32(aligned_size as i32); writer.write_i32(unk1); writer.write_i32(unk2); writer.write_bytes(&entry.entry_key); writer.write_i32(0); // unk3 writer.write_i32(0); // unk4 block_offset += aligned_size; } let encrypted_entries = writer.finish(); output.extend_from_slice(&encrypted_entries); Ok(output) } ``` -------------------------------- ### Get Current Writer Position Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/binary_writer.rs.html?search= Retrieves the current position of the underlying writer. ```rust pub fn position(&mut self) -> WzResult { Ok(self.writer.stream_position()?) } ``` -------------------------------- ### ChaCha20::new Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/crypto/chacha20.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Initializes a new ChaCha20 cipher instance with a given key, nonce, and counter. The key must be 32 bytes, and the nonce must be 12 bytes. ```APIDOC ## ChaCha20::new ### Description Initializes a new ChaCha20 cipher instance with a given key, nonce, and counter. The key must be 32 bytes, and the nonce must be 12 bytes. ### Method `ChaCha20::new(key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> Self` ### Parameters * **key** (`&[u8; 32]`) - The 32-byte secret key. * **nonce** (`&[u8; 12]`) - The 12-byte nonce. * **counter** (`u32`) - The initial block counter value. ### Returns A new `ChaCha20` instance. ``` -------------------------------- ### Get Image Offsets for WzDirectoryEntry Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/directory/struct.WzDirectoryEntry.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Retrieves the offsets for the image entries within the directory. ```rust pub fn get_img_offsets(&mut self, cur_offset: u32) -> u32 ``` -------------------------------- ### Building MS v1 File Structure Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Constructs an MS v1 file from a list of entries. It generates random bytes, calculates and encrypts the header, and prepares the entry buffer. ```Rust fn build_ms_file_v1(file_name: &str, salt: &str, entries: &[MsSaveEntry]) -> WzResult> { let file_name_lower = file_name.to_lowercase(); let mut output = Vec::new(); let rand_bytes = generate_rand_bytes(rand_byte_count(&file_name_lower)); output.extend_from_slice(&rand_bytes); let salt_len = salt.len(); let hashed_salt_len = (salt_len as u8 ^ rand_bytes[0]) as i32; write_i32_le(&mut output, hashed_salt_len); let mut salt_u16_values = Vec::with_capacity(salt_len); for i in 0..salt_len { let lo = salt.as_bytes()[i] ^ rand_bytes[i]; output.push(lo); output.push(0); salt_u16_values.push(u16::from_le_bytes([lo, 0])); } let file_name_with_salt = format!("{}{}", file_name_lower, salt); let salt_u16_sum: i32 = salt_u16_values.iter().map(|&v| v as i32).sum(); let hash = hashed_salt_len + MS_VERSION_V1 as i32 + entries.len() as i32 + salt_u16_sum; let mut header_buf = [0u8; 12]; header_buf[0..4].copy_from_slice(&hash.to_le_bytes()); header_buf[4] = MS_VERSION_V1; header_buf[5..9].copy_from_slice(&(entries.len() as i32).to_le_bytes()); let header_key = derive_snow_key(&file_name_with_salt, false); Snow2::new(&header_key, &[], true).process(&mut header_buf); output.extend_from_slice(&header_buf[..9]); let pad = entry_pad_amount(&file_name_lower) + 33; output.extend(std::iter::repeat(0u8).take(pad)); let mut entry_buf = Vec::new(); // ... rest of the function } ``` -------------------------------- ### IntoIterator Example (Err) Source: https://docs.rs/wzlib-rs/latest/wzlib_rs/wz/error/type.WzResult.html Shows how to convert an Err Result into an iterator that yields no values. ```rust let x: Result = Err("nothing!"); let v: Vec = x.into_iter().collect(); assert_eq!(v, []); ``` -------------------------------- ### Build MS File V1 Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search=std%3A%3Avec Constructs a V1 MS file from a list of entries. It generates random bytes, calculates a hashed salt, and writes a processed header. Ensure the file name is lowercased for consistent hashing. ```Rust fn build_ms_file_v1(file_name: &str, salt: &str, entries: &[MsSaveEntry]) -> WzResult> { let file_name_lower = file_name.to_lowercase(); let mut output = Vec::new(); let rand_bytes = generate_rand_bytes(rand_byte_count(&file_name_lower)); output.extend_from_slice(&rand_bytes); let salt_len = salt.len(); let hashed_salt_len = (salt_len as u8 ^ rand_bytes[0]) as i32; write_i32_le(&mut output, hashed_salt_len); let mut salt_u16_values = Vec::with_capacity(salt_len); for i in 0..salt_len { let lo = salt.as_bytes()[i] ^ rand_bytes[i]; output.push(lo); output.push(0); salt_u16_values.push(u16::from_le_bytes([lo, 0])); } let file_name_with_salt = format!("{}{}", file_name_lower, salt); let salt_u16_sum: i32 = salt_u16_values.iter().map(|&v| v as i32).sum(); let hash = hashed_salt_len + MS_VERSION_V1 as i32 + entries.len() as i32 + salt_u16_sum; let mut header_buf = [0u8; 12]; header_buf[0..4].copy_from_slice(&hash.to_le_bytes()); header_buf[4] = MS_VERSION_V1; header_buf[5..9].copy_from_slice(&(entries.len() as i32).to_le_bytes()); let header_key = derive_snow_key(&file_name_with_salt, false); Snow2::new(&header_key, &[], true).process(&mut header_buf); output.extend_from_slice(&header_buf[..9]); let pad = entry_pad_amount(&file_name_lower) + 33; output.extend(std::iter::repeat(0u8).take(pad)); let mut entry_buf = Vec::new(); } ``` -------------------------------- ### Build WZ File V2 Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/wz/ms_file.rs.html?search=std%3A%3Avec Constructs a WZ file in V2 format, including generating random bytes, encoding the salt, calculating header hashes, and encrypting entry data. Requires file name, salt, and a list of entries. ```Rust fn build_ms_file_v2(file_name: &str, salt: &str, entries: &[MsSaveEntry]) -> WzResult> { let file_name_lower = file_name.to_lowercase(); let mut output = Vec::new(); let rand_bytes = generate_rand_bytes(rand_byte_count(&file_name_lower)); let shifted_rand: Vec = rand_bytes.iter().map(|&b| ((b as i8) >> 1) as u8).collect(); output.extend_from_slice(&rand_bytes); let raw_version_byte = MS_VERSION_V2 ^ shifted_rand[0]; output.push(raw_version_byte); let (raw_salt_bytes, hashed_salt_len, salt_u16_sum) = v2_encode_salt(salt, &shifted_rand); write_i32_le(&mut output, hashed_salt_len); output.extend_from_slice(&raw_salt_bytes); let file_name_with_salt = format!("{}{}", file_name_lower, salt); let header_hash = hashed_salt_len + raw_version_byte as i32 + MS_VERSION_V2 as i32 + entries.len() as i32 + salt_u16_sum; let mut header_block = [0u8; CHACHA_BLOCK_SIZE]; header_block[0..4].copy_from_slice(&header_hash.to_le_bytes()); header_block[4..8].copy_from_slice(&(entries.len() as i32).to_le_bytes()); let header_key = derive_chacha_key(&file_name_with_salt, false); let empty_nonce = [0u8; CHACHA_NONCE_LEN]; ChaCha20::new(&header_key, &empty_nonce, 0).process(&mut header_block); output.extend_from_slice(&header_block); // entry_start = header_pos + 8 + pad + 64 (block), so inter-pad = 8 + pad let epa = entry_pad_amount(&file_name_lower); let inter_pad_len = 8 + epa; output.extend(std::iter::repeat(0u8).take(inter_pad_len)); let entry_key_chacha = derive_chacha_key(&file_name_with_salt, true); let mut writer = ChaCha20StreamWriter::new(&entry_key_chacha, &empty_nonce); let mut block_offset: usize = 0; for entry in entries { let aligned_size = entry.aligned_size(); let block_idx = block_offset / BLOCK_ALIGNMENT; let ek_sum: i32 = entry.entry_key.iter().map(|&b| b as i32).sum(); let flags: i32 = 0; let unk1: i32 = 0; let unk2: i32 = 0; let checksum = flags + block_idx as i32 + entry.image_data.len() as i32 + aligned_size as i32 + unk1 + ek_sum; writer.write_string(&entry.name); writer.write_i32(checksum); writer.write_i32(flags); writer.write_i32(block_idx as i32); writer.write_i32(entry.image_data.len() as i32); writer.write_i32(aligned_size as i32); writer.write_i32(unk1); writer.write_i32(unk2); writer.write_bytes(&entry.entry_key); writer.write_i32(0); // unk3 writer.write_i32(0); // unk4 block_offset += aligned_size; } let encrypted_entries = writer.finish(); output.extend_from_slice(&encrypted_entries); Ok(output) } ``` -------------------------------- ### ChaCha20::new Source: https://docs.rs/wzlib-rs/latest/src/wzlib_rs/crypto/chacha20.rs.html Initializes a new ChaCha20 cipher instance with a key, nonce, and initial counter. ```APIDOC ## ChaCha20::new ### Description Initializes a new ChaCha20 cipher instance with a key, nonce, and initial counter. ### Signature `pub fn new(key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> Self` ### Parameters * **key**: A 32-byte array representing the encryption key. * **nonce**: A 12-byte array representing the nonce. * **counter**: A u32 value for the initial block counter. ```