1use super::header::{PatchFile, PatchType};
6use crate::{Error, Result};
7
8pub fn apply_patch(patch: &PatchFile, base_data: &[u8]) -> Result<Vec<u8>> {
27 patch.verify_base(base_data)?;
29
30 let patched_data = match patch.header.patch_type {
32 PatchType::Copy => apply_copy_patch(patch, base_data)?,
33 PatchType::Bsd0 => apply_bsd0_patch(patch, base_data)?,
34 };
35
36 patch.verify_patched(&patched_data)?;
38
39 Ok(patched_data)
40}
41
42fn apply_copy_patch(patch: &PatchFile, base_data: &[u8]) -> Result<Vec<u8>> {
56 if base_data.len() != patch.header.size_before as usize {
58 return Err(Error::invalid_format(format!(
59 "Base file size mismatch: expected {}, got {}",
60 patch.header.size_before,
61 base_data.len()
62 )));
63 }
64
65 if patch.data.len() != patch.header.size_after as usize {
67 return Err(Error::invalid_format(format!(
68 "Patch data size mismatch: expected {}, got {}",
69 patch.header.size_after,
70 patch.data.len()
71 )));
72 }
73
74 log::debug!(
75 "Applying COPY patch: {} -> {} bytes",
76 base_data.len(),
77 patch.data.len()
78 );
79
80 Ok(patch.data.clone())
82}
83
84fn apply_bsd0_patch(patch: &PatchFile, base_data: &[u8]) -> Result<Vec<u8>> {
98 use byteorder::{LittleEndian, ReadBytesExt};
99 use std::io::Cursor;
100
101 if base_data.len() != patch.header.size_before as usize {
103 return Err(Error::invalid_format(format!(
104 "Base file size mismatch: expected {}, got {}",
105 patch.header.size_before,
106 base_data.len()
107 )));
108 }
109
110 log::debug!(
113 "RLE decompressing BSD0 patch data: {} bytes compressed",
114 patch.data.len()
115 );
116
117 let bsdiff_data = crate::compression::rle::decompress(
118 &patch.data,
119 patch.header.patch_data_size as usize,
120 true, )?;
122
123 log::debug!(
124 "RLE decompression complete: {} bytes → {} bytes",
125 patch.data.len(),
126 bsdiff_data.len()
127 );
128
129 let mut reader = Cursor::new(&bsdiff_data);
130
131 let signature = reader.read_u64::<LittleEndian>()?;
133 if signature != 0x3034464649445342 {
134 return Err(Error::invalid_format(format!(
136 "Invalid BSDIFF40 signature: expected 0x3034464649445342, got 0x{signature:016X}"
137 )));
138 }
139
140 let ctrl_block_size = reader.read_u64::<LittleEndian>()? as usize;
141 let data_block_size = reader.read_u64::<LittleEndian>()? as usize;
142 let new_file_size = reader.read_u64::<LittleEndian>()? as usize;
143
144 if new_file_size != patch.header.size_after as usize {
146 return Err(Error::invalid_format(format!(
147 "BSD0 new file size mismatch: header says {}, bsdiff says {new_file_size}",
148 patch.header.size_after
149 )));
150 }
151
152 log::debug!(
153 "BSD0 patch: {} -> {} bytes (ctrl: {}, data: {})",
154 base_data.len(),
155 new_file_size,
156 ctrl_block_size,
157 data_block_size
158 );
159
160 let ctrl_start = 32; let data_start = ctrl_start + ctrl_block_size;
163 let extra_start = data_start + data_block_size;
164
165 if extra_start > bsdiff_data.len() {
167 return Err(Error::invalid_format(format!(
168 "BSD0 patch data too small: need {extra_start} bytes, have {}",
169 bsdiff_data.len()
170 )));
171 }
172
173 let ctrl_block = &bsdiff_data[ctrl_start..data_start];
175 let data_block = &bsdiff_data[data_start..extra_start];
176 let extra_block = &bsdiff_data[extra_start..];
177
178 let num_ctrl_blocks = ctrl_block_size / 12;
180
181 let mut new_data = vec![0u8; new_file_size];
183
184 let mut new_offset = 0usize;
186 let mut old_offset = 0usize;
187 let mut data_ptr = 0usize;
188 let mut extra_ptr = 0usize;
189
190 for i in 0..num_ctrl_blocks {
191 let ctrl_offset = i * 12;
192 if ctrl_offset + 12 > ctrl_block.len() {
193 return Err(Error::invalid_format(
194 "Control block extends beyond ctrl_block_size".to_string(),
195 ));
196 }
197
198 let mut ctrl_reader = Cursor::new(&ctrl_block[ctrl_offset..ctrl_offset + 12]);
200 let add_data_length = ctrl_reader.read_u32::<LittleEndian>()? as usize;
201 let mov_data_length = ctrl_reader.read_u32::<LittleEndian>()? as usize;
202 let old_move_length_raw = ctrl_reader.read_u32::<LittleEndian>()?;
203
204 if new_offset + add_data_length > new_file_size {
206 return Err(Error::invalid_format(format!(
207 "BSD0: add overflow at ctrl {i}: new_offset {new_offset} + add {add_data_length} > size {new_file_size}"
208 )));
209 }
210
211 if data_ptr + add_data_length > data_block.len() {
212 return Err(Error::invalid_format(format!(
213 "BSD0: data block overflow at ctrl {i}: ptr {data_ptr} + len {add_data_length} > size {}",
214 data_block.len()
215 )));
216 }
217
218 new_data[new_offset..new_offset + add_data_length]
220 .copy_from_slice(&data_block[data_ptr..data_ptr + add_data_length]);
221 data_ptr += add_data_length;
222
223 let combine_size = if old_offset + add_data_length >= base_data.len() {
225 base_data.len().saturating_sub(old_offset)
226 } else {
227 add_data_length
228 };
229
230 for j in 0..combine_size {
231 new_data[new_offset + j] =
232 new_data[new_offset + j].wrapping_add(base_data[old_offset + j]);
233 }
234
235 new_offset += add_data_length;
236 old_offset += add_data_length;
237
238 if new_offset + mov_data_length > new_file_size {
240 return Err(Error::invalid_format(format!(
241 "BSD0: mov overflow at ctrl {i}: new_offset {new_offset} + mov {mov_data_length} > size {new_file_size}"
242 )));
243 }
244
245 if extra_ptr + mov_data_length > extra_block.len() {
246 return Err(Error::invalid_format(format!(
247 "BSD0: extra block overflow at ctrl {i}: ptr {extra_ptr} + len {mov_data_length} > size {}",
248 extra_block.len()
249 )));
250 }
251
252 new_data[new_offset..new_offset + mov_data_length]
253 .copy_from_slice(&extra_block[extra_ptr..extra_ptr + mov_data_length]);
254 extra_ptr += mov_data_length;
255 new_offset += mov_data_length;
256
257 let old_move_length = if old_move_length_raw & 0x80000000 != 0 {
259 let neg_val = 0x80000000u32.wrapping_sub(old_move_length_raw);
261 old_offset = old_offset.saturating_sub(neg_val as usize);
262 0
263 } else {
264 old_move_length_raw as usize
265 };
266
267 old_offset += old_move_length;
268 }
269
270 if new_offset != new_file_size {
272 return Err(Error::invalid_format(format!(
273 "BSD0: final offset mismatch: got {new_offset}, expected {new_file_size}"
274 )));
275 }
276
277 log::debug!("BSD0 patch applied successfully: {} bytes", new_data.len());
278
279 Ok(new_data)
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 fn create_copy_patch(base_size: u32, new_data: Vec<u8>) -> PatchFile {
288 use md5::{Digest, Md5};
289
290 let base_data = vec![0u8; base_size as usize];
292 let mut hasher = Md5::new();
293 hasher.update(&base_data);
294 let md5_before: [u8; 16] = hasher.finalize().into();
295
296 let mut hasher = Md5::new();
298 hasher.update(&new_data);
299 let md5_after: [u8; 16] = hasher.finalize().into();
300
301 let mut data = Vec::new();
303
304 data.extend_from_slice(&0x48435450u32.to_le_bytes());
306 data.extend_from_slice(&(new_data.len() as u32).to_le_bytes());
307 data.extend_from_slice(&base_size.to_le_bytes());
308 data.extend_from_slice(&(new_data.len() as u32).to_le_bytes());
309
310 data.extend_from_slice(&0x5f35444du32.to_le_bytes());
312 data.extend_from_slice(&40u32.to_le_bytes());
313 data.extend_from_slice(&md5_before);
314 data.extend_from_slice(&md5_after);
315
316 data.extend_from_slice(&0x4d524658u32.to_le_bytes());
318 data.extend_from_slice(&(12 + new_data.len() as u32).to_le_bytes());
319 data.extend_from_slice(&0x59504f43u32.to_le_bytes()); data.extend_from_slice(&new_data);
323
324 PatchFile::parse(&data).expect("Failed to create test patch")
325 }
326
327 #[test]
328 fn test_apply_copy_patch_simple() {
329 let base_data = vec![0u8; 100];
330 let new_data = b"Hello, Warcraft!".to_vec();
331
332 let patch = create_copy_patch(100, new_data.clone());
333 let result = apply_patch(&patch, &base_data).expect("Failed to apply patch");
334
335 assert_eq!(result, new_data);
336 }
337
338 #[test]
339 fn test_copy_patch_size_increase() {
340 let base_data = vec![0u8; 50];
341 let new_data = vec![0x42u8; 200]; let patch = create_copy_patch(50, new_data.clone());
344 let result = apply_patch(&patch, &base_data).expect("Failed to apply patch");
345
346 assert_eq!(result.len(), 200);
347 assert_eq!(result, new_data);
348 }
349
350 #[test]
351 fn test_copy_patch_size_decrease() {
352 let base_data = vec![0u8; 200];
353 let new_data = vec![0x42u8; 50]; let patch = create_copy_patch(200, new_data.clone());
356 let result = apply_patch(&patch, &base_data).expect("Failed to apply patch");
357
358 assert_eq!(result.len(), 50);
359 assert_eq!(result, new_data);
360 }
361
362 #[test]
363 fn test_copy_patch_wrong_base_size() {
364 let base_data = vec![0u8; 100]; let new_data = b"Test".to_vec();
366
367 let patch = create_copy_patch(50, new_data); let result = apply_patch(&patch, &base_data);
369
370 assert!(result.is_err());
371 }
372
373 #[test]
374 fn test_copy_patch_wrong_base_md5() {
375 let base_data = vec![0x42u8; 100]; let new_data = b"Test".to_vec();
377
378 let patch = create_copy_patch(100, new_data); let result = apply_patch(&patch, &base_data);
380
381 assert!(result.is_err());
382 }
383
384 fn rle_compress_test(data: &[u8]) -> Vec<u8> {
387 let mut compressed = Vec::new();
388
389 compressed.extend_from_slice(&(data.len() as u32).to_le_bytes());
391
392 for chunk in data.chunks(128) {
394 let count = chunk.len() as u8;
395 compressed.push(0x80 | (count - 1)); compressed.extend_from_slice(chunk);
397 }
398
399 compressed
400 }
401
402 fn create_bsd0_patch(base_data: &[u8], new_data: &[u8], patch_data: Vec<u8>) -> PatchFile {
404 use md5::{Digest, Md5};
405
406 let mut hasher = Md5::new();
408 hasher.update(base_data);
409 let md5_before: [u8; 16] = hasher.finalize().into();
410
411 let mut hasher = Md5::new();
412 hasher.update(new_data);
413 let md5_after: [u8; 16] = hasher.finalize().into();
414
415 let compressed_patch_data = rle_compress_test(&patch_data);
417
418 let mut data = Vec::new();
420
421 data.extend_from_slice(&0x48435450u32.to_le_bytes());
423 data.extend_from_slice(&(patch_data.len() as u32).to_le_bytes()); data.extend_from_slice(&(base_data.len() as u32).to_le_bytes());
425 data.extend_from_slice(&(new_data.len() as u32).to_le_bytes());
426
427 data.extend_from_slice(&0x5f35444du32.to_le_bytes());
429 data.extend_from_slice(&40u32.to_le_bytes());
430 data.extend_from_slice(&md5_before);
431 data.extend_from_slice(&md5_after);
432
433 data.extend_from_slice(&0x4d524658u32.to_le_bytes());
435 data.extend_from_slice(&(12u32 + compressed_patch_data.len() as u32).to_le_bytes());
436 data.extend_from_slice(&0x30445342u32.to_le_bytes()); data.extend_from_slice(&compressed_patch_data);
440
441 PatchFile::parse(&data).expect("Failed to create test BSD0 patch")
442 }
443
444 #[test]
445 fn test_apply_bsd0_simple() {
446 let old_data = vec![0x10u8, 0x20, 0x30];
450 let new_data = vec![0x15u8, 0x27, 0xFF];
451
452 let mut patch_data = Vec::new();
454
455 patch_data.extend_from_slice(&0x3034464649445342u64.to_le_bytes()); patch_data.extend_from_slice(&12u64.to_le_bytes()); patch_data.extend_from_slice(&2u64.to_le_bytes()); patch_data.extend_from_slice(&3u64.to_le_bytes()); patch_data.extend_from_slice(&2u32.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.push(0x05);
469 patch_data.push(0x07);
470
471 patch_data.push(0xFF);
473
474 let patch = create_bsd0_patch(&old_data, &new_data, patch_data);
475 let result = apply_patch(&patch, &old_data).expect("Failed to apply BSD0 patch");
476
477 assert_eq!(result, new_data);
478 }
479
480 #[test]
481 fn test_bsd0_wrapping_addition() {
482 let old_data = vec![0xFFu8];
487 let new_data = vec![0x01u8];
488
489 let mut patch_data = Vec::new();
490 patch_data.extend_from_slice(&0x3034464649445342u64.to_le_bytes());
491 patch_data.extend_from_slice(&12u64.to_le_bytes());
492 patch_data.extend_from_slice(&1u64.to_le_bytes());
493 patch_data.extend_from_slice(&1u64.to_le_bytes());
494
495 patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.extend_from_slice(&0u32.to_le_bytes()); patch_data.extend_from_slice(&0u32.to_le_bytes()); patch_data.push(0x02); let patch = create_bsd0_patch(&old_data, &new_data, patch_data);
504 let result = apply_patch(&patch, &old_data).expect("Failed to apply BSD0 patch");
505
506 assert_eq!(result, new_data);
507 assert_eq!(result[0], 0x01);
508 }
509
510 #[test]
511 fn test_bsd0_multiple_control_blocks() {
512 let old_data = vec![0x10u8, 0x20, 0x30, 0x40];
516 let new_data = vec![0x15u8, 0x27, 0xFF, 0xAA];
517
518 let mut patch_data = Vec::new();
519 patch_data.extend_from_slice(&0x3034464649445342u64.to_le_bytes());
520 patch_data.extend_from_slice(&24u64.to_le_bytes()); patch_data.extend_from_slice(&2u64.to_le_bytes()); patch_data.extend_from_slice(&4u64.to_le_bytes()); patch_data.extend_from_slice(&2u32.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.extend_from_slice(&0u32.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes()); patch_data.extend_from_slice(&0u32.to_le_bytes()); patch_data.push(0x05); patch_data.push(0x07); patch_data.push(0xFF);
540 patch_data.push(0xAA);
541
542 let patch = create_bsd0_patch(&old_data, &new_data, patch_data);
543 let result = apply_patch(&patch, &old_data).expect("Failed to apply BSD0 patch");
544
545 assert_eq!(result, new_data);
546 }
547
548 #[test]
549 fn test_bsd0_invalid_signature() {
550 let old_data = vec![0x10u8];
551 let new_data = vec![0x11u8];
552
553 let mut patch_data = Vec::new();
554 patch_data.extend_from_slice(&0xDEADBEEFDEADBEEFu64.to_le_bytes()); patch_data.extend_from_slice(&12u64.to_le_bytes());
556 patch_data.extend_from_slice(&1u64.to_le_bytes());
557 patch_data.extend_from_slice(&1u64.to_le_bytes());
558 patch_data.extend_from_slice(&1u32.to_le_bytes());
559 patch_data.extend_from_slice(&0u32.to_le_bytes());
560 patch_data.extend_from_slice(&0u32.to_le_bytes());
561 patch_data.push(0x01);
562
563 let patch = create_bsd0_patch(&old_data, &new_data, patch_data);
564 let result = apply_patch(&patch, &old_data);
565
566 assert!(result.is_err());
567 }
568
569 #[test]
570 fn test_bsd0_size_mismatch() {
571 let old_data = vec![0x10u8, 0x20]; let new_data = vec![0x11u8];
574
575 let mut patch_data = Vec::new();
576 patch_data.extend_from_slice(&0x3034464649445342u64.to_le_bytes());
577 patch_data.extend_from_slice(&12u64.to_le_bytes());
578 patch_data.extend_from_slice(&1u64.to_le_bytes());
579 patch_data.extend_from_slice(&1u64.to_le_bytes()); patch_data.extend_from_slice(&1u32.to_le_bytes());
581 patch_data.extend_from_slice(&0u32.to_le_bytes());
582 patch_data.extend_from_slice(&0u32.to_le_bytes());
583 patch_data.push(0x01);
584
585 let patch = create_bsd0_patch(&[0x10], &new_data, patch_data);
587 let result = apply_patch(&patch, &old_data);
588
589 assert!(result.is_err());
590 }
591}