use wow_mpq::Archive;
use wow_mpq::patch::{PatchFile, PatchType};
const UPDATE_MPQ1: &str =
"/home/danielsreichenbach/Downloads/wow/4.3.4/4.3.4/Data/wow-update-base-15211.MPQ";
const TEST_FILE: &str = "Item/ObjectComponents/Head/Helm_Robe_RaidWarlock_F_01_WoF.M2";
#[test]
#[ignore = "Requires MPQ test files not available in CI"]
fn test_identify_patch_file() {
let archive = Archive::open(UPDATE_MPQ1).expect("Failed to open update MPQ");
let file_info = archive
.find_file(TEST_FILE)
.expect("Failed to find file")
.expect("File not found");
println!("File flags: 0x{:08X}", file_info.flags);
println!("Is patch file: {}", file_info.is_patch_file());
assert!(
file_info.is_patch_file(),
"Expected file to be a patch file"
);
}
#[test]
#[ignore] fn test_parse_patch_header_real_data() {
let mut archive = Archive::open(UPDATE_MPQ1).expect("Failed to open update MPQ");
let result = archive.read_file(TEST_FILE);
match result {
Err(wow_mpq::Error::OperationNotSupported { .. }) => {
println!("Patch file correctly rejected (as expected before implementation)");
}
Ok(_) => panic!("Should not be able to read patch file directly"),
Err(e) => panic!("Unexpected error: {e}"),
}
}
#[test]
fn test_parse_ptch_header_synthetic() {
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes()); data.extend_from_slice(&1000u32.to_le_bytes()); data.extend_from_slice(&5000u32.to_le_bytes()); data.extend_from_slice(&6000u32.to_le_bytes());
data.extend_from_slice(&0x5f35444du32.to_le_bytes()); data.extend_from_slice(&40u32.to_le_bytes()); data.extend_from_slice(&[0x11u8; 16]); data.extend_from_slice(&[0x22u8; 16]);
data.extend_from_slice(&0x4d524658u32.to_le_bytes()); data.extend_from_slice(&112u32.to_le_bytes()); data.extend_from_slice(&0x59504f43u32.to_le_bytes());
data.extend_from_slice(&[0x42u8; 100]);
let patch = PatchFile::parse(&data).expect("Failed to parse PTCH file");
assert_eq!(patch.header.patch_data_size, 1000);
assert_eq!(patch.header.size_before, 5000);
assert_eq!(patch.header.size_after, 6000);
assert_eq!(patch.header.md5_before, [0x11u8; 16]);
assert_eq!(patch.header.md5_after, [0x22u8; 16]);
assert_eq!(patch.header.patch_type, PatchType::Copy);
assert_eq!(patch.header.xfrm_data_size, 100);
assert_eq!(patch.data.len(), 100);
}
#[test]
fn test_parse_bsd0_patch_type() {
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes());
data.extend_from_slice(&2000u32.to_le_bytes());
data.extend_from_slice(&10000u32.to_le_bytes());
data.extend_from_slice(&12000u32.to_le_bytes());
data.extend_from_slice(&0x5f35444du32.to_le_bytes());
data.extend_from_slice(&40u32.to_le_bytes());
data.extend_from_slice(&[0x33u8; 16]);
data.extend_from_slice(&[0x44u8; 16]);
data.extend_from_slice(&0x4d524658u32.to_le_bytes());
data.extend_from_slice(&212u32.to_le_bytes());
data.extend_from_slice(&0x30445342u32.to_le_bytes());
data.extend_from_slice(&[0x99u8; 200]);
let patch = PatchFile::parse(&data).expect("Failed to parse BSD0 patch");
assert_eq!(patch.header.patch_type, PatchType::Bsd0);
assert_eq!(patch.data.len(), 200);
}
#[test]
fn test_invalid_ptch_signature() {
let mut data = Vec::new();
data.extend_from_slice(&0xDEADBEEFu32.to_le_bytes()); data.extend_from_slice(&[0u8; 60]);
let result = PatchFile::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_invalid_md5_signature() {
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes());
data.extend_from_slice(&1000u32.to_le_bytes());
data.extend_from_slice(&5000u32.to_le_bytes());
data.extend_from_slice(&6000u32.to_le_bytes());
data.extend_from_slice(&0xBADBADBAu32.to_le_bytes());
data.extend_from_slice(&[0u8; 40]);
let result = PatchFile::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_invalid_xfrm_signature() {
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes());
data.extend_from_slice(&1000u32.to_le_bytes());
data.extend_from_slice(&5000u32.to_le_bytes());
data.extend_from_slice(&6000u32.to_le_bytes());
data.extend_from_slice(&0x5f35444du32.to_le_bytes());
data.extend_from_slice(&40u32.to_le_bytes());
data.extend_from_slice(&[0x11u8; 16]);
data.extend_from_slice(&[0x22u8; 16]);
data.extend_from_slice(&0xBADBADBAu32.to_le_bytes());
data.extend_from_slice(&[0u8; 8]);
let result = PatchFile::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_unknown_patch_type() {
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes());
data.extend_from_slice(&1000u32.to_le_bytes());
data.extend_from_slice(&5000u32.to_le_bytes());
data.extend_from_slice(&6000u32.to_le_bytes());
data.extend_from_slice(&0x5f35444du32.to_le_bytes());
data.extend_from_slice(&40u32.to_le_bytes());
data.extend_from_slice(&[0x11u8; 16]);
data.extend_from_slice(&[0x22u8; 16]);
data.extend_from_slice(&0x4d524658u32.to_le_bytes());
data.extend_from_slice(&112u32.to_le_bytes());
data.extend_from_slice(&0xDEADBEEFu32.to_le_bytes());
let result = PatchFile::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_file_too_small() {
let data = vec![0u8; 32];
let result = PatchFile::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_md5_verification() {
use md5::{Digest, Md5};
let base_data = b"Hello, World!";
let patched_data = b"Hello, Warcraft!";
let mut hasher = Md5::new();
hasher.update(base_data);
let md5_before: [u8; 16] = hasher.finalize().into();
let mut hasher = Md5::new();
hasher.update(patched_data);
let md5_after: [u8; 16] = hasher.finalize().into();
let mut data = Vec::new();
data.extend_from_slice(&0x48435450u32.to_le_bytes());
data.extend_from_slice(&100u32.to_le_bytes());
data.extend_from_slice(&(base_data.len() as u32).to_le_bytes());
data.extend_from_slice(&(patched_data.len() as u32).to_le_bytes());
data.extend_from_slice(&0x5f35444du32.to_le_bytes());
data.extend_from_slice(&40u32.to_le_bytes());
data.extend_from_slice(&md5_before);
data.extend_from_slice(&md5_after);
data.extend_from_slice(&0x4d524658u32.to_le_bytes());
data.extend_from_slice(&112u32.to_le_bytes());
data.extend_from_slice(&0x59504f43u32.to_le_bytes());
data.extend_from_slice(&[0u8; 100]);
let patch = PatchFile::parse(&data).expect("Failed to parse patch");
assert!(patch.verify_base(base_data).is_ok());
assert!(patch.verify_base(b"Wrong data").is_err());
assert!(patch.verify_patched(patched_data).is_ok());
assert!(patch.verify_patched(b"Wrong data").is_err());
}