Skip to main content

rar_stream/decompress/
vm.rs

1//! RAR3 VM filter implementation.
2//!
3//! RAR3 uses a virtual machine for post-processing decompressed data.
4//! In practice, only 6 standard filters are used, identified by CRC.
5
6use crate::crc32::crc32;
7
8use super::byte_search;
9
10/// VM memory size (256KB)
11pub const VM_MEMSIZE: usize = 0x40000;
12pub const VM_MEMMASK: u32 = (VM_MEMSIZE - 1) as u32;
13
14/// Maximum channels for audio/delta filters
15pub const MAX_UNPACK_CHANNELS: usize = 1024;
16
17/// Standard filter types (identified by CRC, not bytecode)
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum StandardFilter {
20    None,
21    /// x86 CALL (E8) address conversion
22    E8,
23    /// x86 CALL/JMP (E8/E9) address conversion
24    E8E9,
25    /// IA-64 Itanium preprocessing
26    Itanium,
27    /// Byte delta encoding (audio/images)
28    Delta,
29    /// RGB predictive filter
30    Rgb,
31    /// Audio sample predictor
32    Audio,
33}
34
35/// Known filter signatures
36struct FilterSignature {
37    length: u32,
38    crc: u32,
39    filter_type: StandardFilter,
40}
41
42const FILTER_SIGNATURES: &[FilterSignature] = &[
43    FilterSignature {
44        length: 53,
45        crc: 0xad576887,
46        filter_type: StandardFilter::E8,
47    },
48    FilterSignature {
49        length: 57,
50        crc: 0x3cd7e57e,
51        filter_type: StandardFilter::E8E9,
52    },
53    FilterSignature {
54        length: 120,
55        crc: 0x3769893f,
56        filter_type: StandardFilter::Itanium,
57    },
58    FilterSignature {
59        length: 29,
60        crc: 0x0e06077d,
61        filter_type: StandardFilter::Delta,
62    },
63    FilterSignature {
64        length: 149,
65        crc: 0x1c2c5dc8,
66        filter_type: StandardFilter::Rgb,
67    },
68    FilterSignature {
69        length: 216,
70        crc: 0xbc85e701,
71        filter_type: StandardFilter::Audio,
72    },
73];
74
75/// A prepared filter ready for execution
76#[derive(Debug, Clone)]
77pub struct PreparedFilter {
78    pub filter_type: StandardFilter,
79    /// Initial register values [R0-R6]
80    pub init_r: [u32; 7],
81    /// Block start position in output (absolute, for scheduling)
82    pub block_start: u64,
83    /// Block length
84    pub block_length: u32,
85    /// Window mask for indexing
86    pub window_mask: u32,
87}
88
89/// Stored filter definition (reusable)
90#[derive(Debug, Clone)]
91pub struct StoredFilter {
92    pub filter_type: StandardFilter,
93}
94
95/// RAR VM state
96pub struct RarVM {
97    /// VM memory buffer
98    mem: Vec<u8>,
99    /// Stored filter definitions (by index)
100    filters: Vec<StoredFilter>,
101    /// Filter execution stack
102    stack: Vec<PreparedFilter>,
103    /// Last used filter index
104    last_filter: usize,
105    /// Old filter block lengths (for reuse)
106    old_lengths: Vec<u32>,
107}
108
109impl RarVM {
110    pub fn new() -> Self {
111        Self {
112            mem: vec![0u8; VM_MEMSIZE + 4],
113            filters: Vec::new(),
114            stack: Vec::new(),
115            last_filter: 0,
116            old_lengths: Vec::new(),
117        }
118    }
119
120    /// Reset VM state
121    pub fn reset(&mut self) {
122        self.filters.clear();
123        self.stack.clear();
124        self.last_filter = 0;
125        self.old_lengths.clear();
126    }
127
128    /// Identify filter type from VM code using CRC
129    fn identify_filter(code: &[u8]) -> StandardFilter {
130        if code.is_empty() {
131            return StandardFilter::None;
132        }
133
134        // Verify XOR checksum
135        let mut xor_sum: u8 = 0;
136        for &b in &code[1..] {
137            xor_sum ^= b;
138        }
139        #[cfg(test)]
140        let checksum_ok = xor_sum == code[0];
141
142        if xor_sum != code[0] {
143            #[cfg(test)]
144            eprintln!(
145                "    identify_filter: XOR checksum FAILED (expected 0x{:02x}, got 0x{:02x})",
146                code[0], xor_sum
147            );
148            return StandardFilter::None;
149        }
150
151        // Calculate CRC and match against known filters
152        let code_crc = crc32(code);
153        let code_len = code.len() as u32;
154
155        #[cfg(test)]
156        eprintln!(
157            "    identify_filter: len={}, crc=0x{:08x}, checksum={}",
158            code_len, code_crc, checksum_ok
159        );
160
161        for sig in FILTER_SIGNATURES {
162            if sig.crc == code_crc && sig.length == code_len {
163                #[cfg(test)]
164                eprintln!("    identified: {:?}", sig.filter_type);
165                return sig.filter_type;
166            }
167        }
168
169        #[cfg(test)]
170        eprintln!("    identified: None (no matching signature)");
171
172        StandardFilter::None
173    }
174
175    /// Read variable-length data value from bit input
176    fn read_data(data: &[u8], bit_pos: &mut usize) -> u32 {
177        // We need at least 2 bits to determine the type, and up to 34 bits total for case 0xc000
178        // But we can handle cases where we have less data by checking as we go
179        let bits_available = (data.len() * 8).saturating_sub(*bit_pos);
180        if bits_available < 2 {
181            #[cfg(test)]
182            eprintln!(
183                "      read_data: EOF at bit_pos={}, data.len={}",
184                *bit_pos,
185                data.len()
186            );
187            return 0;
188        }
189
190        // Read up to 24 bits (3 bytes) for initial 16-bit value
191        let byte_pos = *bit_pos / 8;
192        let bit_off = *bit_pos % 8;
193
194        // unrar reads 3 bytes and shifts
195        let mut val: u32 = 0;
196        if byte_pos < data.len() {
197            val |= (data[byte_pos] as u32) << 16;
198        }
199        if byte_pos + 1 < data.len() {
200            val |= (data[byte_pos + 1] as u32) << 8;
201        }
202        if byte_pos + 2 < data.len() {
203            val |= data[byte_pos + 2] as u32;
204        }
205        val >>= 8 - bit_off;
206        val &= 0xffff;
207
208        #[cfg(test)]
209        if *bit_pos < 100 {
210            eprintln!("      read_data at bit {}: bytes[{}..{}]=[{:02x},{:02x},{:02x}], bit_off={}, val=0x{:04x}",
211                *bit_pos, byte_pos, byte_pos+3,
212                data.get(byte_pos).copied().unwrap_or(0),
213                data.get(byte_pos+1).copied().unwrap_or(0),
214                data.get(byte_pos+2).copied().unwrap_or(0),
215                bit_off, val);
216        }
217
218        match val & 0xc000 {
219            0 => {
220                *bit_pos += 6;
221                (val >> 10) & 0xf
222            }
223            0x4000 => {
224                if (val & 0x3c00) == 0 {
225                    *bit_pos += 14;
226                    0xffffff00 | ((val >> 2) & 0xff)
227                } else {
228                    *bit_pos += 10;
229                    (val >> 6) & 0xff
230                }
231            }
232            0x8000 => {
233                // 16-bit value follows (after 2-bit marker)
234                *bit_pos += 2;
235                // Read 16 bits aligned to current bit position
236                let byte_pos = *bit_pos / 8;
237                let bit_off = *bit_pos % 8;
238
239                let mut raw: u32 = 0;
240                if byte_pos < data.len() {
241                    raw |= (data[byte_pos] as u32) << 16;
242                }
243                if byte_pos + 1 < data.len() {
244                    raw |= (data[byte_pos + 1] as u32) << 8;
245                }
246                if byte_pos + 2 < data.len() {
247                    raw |= data[byte_pos + 2] as u32;
248                }
249                raw >>= 8 - bit_off;
250
251                *bit_pos += 16;
252                raw & 0xffff
253            }
254            _ => {
255                // 32-bit value follows (after 2-bit marker)
256                *bit_pos += 2;
257                // Read first 16 bits
258                let byte_pos = *bit_pos / 8;
259                let bit_off = *bit_pos % 8;
260
261                let mut raw1: u32 = 0;
262                if byte_pos < data.len() {
263                    raw1 |= (data[byte_pos] as u32) << 16;
264                }
265                if byte_pos + 1 < data.len() {
266                    raw1 |= (data[byte_pos + 1] as u32) << 8;
267                }
268                if byte_pos + 2 < data.len() {
269                    raw1 |= data[byte_pos + 2] as u32;
270                }
271                raw1 >>= 8 - bit_off;
272                let high16 = raw1 & 0xffff;
273
274                *bit_pos += 16;
275
276                // Read second 16 bits
277                let byte_pos = *bit_pos / 8;
278                let bit_off = *bit_pos % 8;
279
280                let mut raw2: u32 = 0;
281                if byte_pos < data.len() {
282                    raw2 |= (data[byte_pos] as u32) << 16;
283                }
284                if byte_pos + 1 < data.len() {
285                    raw2 |= (data[byte_pos + 1] as u32) << 8;
286                }
287                if byte_pos + 2 < data.len() {
288                    raw2 |= data[byte_pos + 2] as u32;
289                }
290                raw2 >>= 8 - bit_off;
291                let low16 = raw2 & 0xffff;
292
293                *bit_pos += 16;
294                (high16 << 16) | low16
295            }
296        }
297    }
298
299    /// Add VM code and create filter
300    /// `total_written` is the absolute total bytes written so far (not wrapped)
301    /// `window_mask` is used to wrap block_start for window access
302    pub fn add_code(
303        &mut self,
304        first_byte: u8,
305        code: &[u8],
306        total_written: u64,
307        window_mask: u32,
308    ) -> bool {
309        let mut bit_pos = 0;
310
311        #[cfg(test)]
312        eprintln!(
313            "  add_code: first_byte=0x{:02x}, code.len={}",
314            first_byte,
315            code.len()
316        );
317
318        // Determine filter position
319        let filt_pos = if (first_byte & 0x80) != 0 {
320            let pos = Self::read_data(code, &mut bit_pos);
321            if pos == 0 {
322                // Reset filters
323                self.filters.clear();
324                self.old_lengths.clear();
325            }
326            pos.saturating_sub(1) as usize
327        } else {
328            self.last_filter
329        };
330
331        if filt_pos > self.filters.len() || filt_pos > 1024 {
332            return false;
333        }
334
335        self.last_filter = filt_pos;
336        let new_filter = filt_pos == self.filters.len();
337
338        // Read block_start (offset from current position)
339        let mut block_start = Self::read_data(code, &mut bit_pos);
340        if (first_byte & 0x40) != 0 {
341            block_start = block_start.wrapping_add(258);
342        }
343
344        // Read block_length
345        let block_length = if (first_byte & 0x20) != 0 {
346            let len = Self::read_data(code, &mut bit_pos);
347            #[cfg(test)]
348            eprintln!("    block_length read from code: {}", len);
349            if filt_pos < self.old_lengths.len() {
350                self.old_lengths[filt_pos] = len;
351            } else if new_filter {
352                // Will be pushed below
353            }
354            len
355        } else if filt_pos < self.old_lengths.len() {
356            #[cfg(test)]
357            eprintln!(
358                "    block_length from old_lengths[{}]: {}",
359                filt_pos, self.old_lengths[filt_pos]
360            );
361            self.old_lengths[filt_pos]
362        } else {
363            #[cfg(test)]
364            eprintln!(
365                "    block_length: 0 (filt_pos {} >= old_lengths.len {})",
366                filt_pos,
367                self.old_lengths.len()
368            );
369            0
370        };
371
372        // Compute absolute block_start (where filter should execute in output stream)
373        // block_start from code is an offset from current total_written position
374        let absolute_block_start = total_written + block_start as u64;
375
376        // Read initial registers
377        let mut init_r = [0u32; 7];
378        init_r[3] = VM_MEMSIZE as u32;
379        init_r[4] = block_length;
380        init_r[5] = 0; // ExecCount
381        init_r[6] = (absolute_block_start & 0xFFFFFFFF) as u32; // FileOffset - position in output (truncated to u32)
382
383        if (first_byte & 0x10) != 0 {
384            // Read 7-bit init mask like unrar: fgetbits()>>9, then faddbits(7)
385            let byte_pos = bit_pos / 8;
386            let bit_off = bit_pos % 8;
387
388            // Read 3 bytes and form 16-bit value like getbits()
389            let mut val: u32 = 0;
390            if byte_pos < code.len() {
391                val |= (code[byte_pos] as u32) << 16;
392            }
393            if byte_pos + 1 < code.len() {
394                val |= (code[byte_pos + 1] as u32) << 8;
395            }
396            if byte_pos + 2 < code.len() {
397                val |= code[byte_pos + 2] as u32;
398            }
399            val >>= 8 - bit_off;
400            let init_mask = ((val >> 9) & 0x7f) as u8;
401            bit_pos += 7;
402
403            #[cfg(test)]
404            eprintln!("    init_mask=0x{:02x} at bit {}", init_mask, bit_pos - 7);
405
406            for i in 0..7 {
407                if (init_mask & (1 << i)) != 0 {
408                    init_r[i] = Self::read_data(code, &mut bit_pos);
409                    #[cfg(test)]
410                    eprintln!("    init_r[{}]={}", i, init_r[i]);
411                }
412            }
413        }
414
415        // For new filters, read VM bytecode and identify
416        let filter_type = if new_filter {
417            // Read VM code size
418            let vm_code_size = Self::read_data(code, &mut bit_pos) as usize;
419            #[cfg(test)]
420            eprintln!(
421                "    new_filter: vm_code_size={}, bit_pos={}, code.len={}",
422                vm_code_size,
423                bit_pos,
424                code.len()
425            );
426
427            if vm_code_size == 0 || vm_code_size >= 0x10000 {
428                return false;
429            }
430
431            // Read VM bytecode - bit aligned, reading each byte via getbits
432            let mut vm_code = vec![0u8; vm_code_size];
433            for i in 0..vm_code_size {
434                if bit_pos + 8 > code.len() * 8 {
435                    return false;
436                }
437                // Read 8 bits like unrar's (fgetbits()>>8)
438                let byte_idx = bit_pos / 8;
439                let bit_off = bit_pos % 8;
440
441                let mut val: u32 = 0;
442                if byte_idx < code.len() {
443                    val |= (code[byte_idx] as u32) << 16;
444                }
445                if byte_idx + 1 < code.len() {
446                    val |= (code[byte_idx + 1] as u32) << 8;
447                }
448                if byte_idx + 2 < code.len() {
449                    val |= code[byte_idx + 2] as u32;
450                }
451                val >>= 8 - bit_off;
452                vm_code[i] = ((val >> 8) & 0xff) as u8;
453                bit_pos += 8;
454            }
455
456            #[cfg(test)]
457            eprintln!(
458                "    vm_code first 4 bytes: {:02x} {:02x} {:02x} {:02x}",
459                vm_code.get(0).copied().unwrap_or(0),
460                vm_code.get(1).copied().unwrap_or(0),
461                vm_code.get(2).copied().unwrap_or(0),
462                vm_code.get(3).copied().unwrap_or(0)
463            );
464
465            Self::identify_filter(&vm_code)
466        } else if filt_pos < self.filters.len() {
467            self.filters[filt_pos].filter_type
468        } else {
469            StandardFilter::None
470        };
471
472        if new_filter {
473            self.filters.push(StoredFilter { filter_type });
474            self.old_lengths.push(block_length);
475        }
476
477        #[cfg(test)]
478        eprintln!(
479            "    filter: type={:?}, block_start={} (raw {}+total_written {}), len={}",
480            filter_type, absolute_block_start, block_start, total_written, block_length
481        );
482
483        let filter = PreparedFilter {
484            filter_type,
485            init_r,
486            block_start: absolute_block_start as u64,
487            block_length,
488            window_mask,
489        };
490
491        self.stack.push(filter);
492        true
493    }
494
495    /// Check if there are pending filters
496    pub fn has_pending_filters(&self) -> bool {
497        !self.stack.is_empty()
498    }
499
500    /// Find the earliest filter that is ready to execute (block_end <= total_written)
501    /// Returns the index and block_start of the earliest ready filter
502    pub fn find_ready_filter(&self, total_written: u64) -> Option<(usize, u64)> {
503        let mut earliest_idx = None;
504        let mut earliest_start = u64::MAX;
505
506        for (idx, filter) in self.stack.iter().enumerate() {
507            let block_length = (filter.block_length & VM_MEMMASK) as u64;
508            let block_end = filter.block_start + block_length;
509
510            // Filter is ready if we've written past its end
511            if block_end <= total_written && filter.block_start < earliest_start {
512                #[cfg(test)]
513                if filter.block_start < 1600000 && filter.block_start > 1400000 {
514                    eprintln!("  find_ready_filter: found candidate idx={}, start={}, end={}, total_written={}", 
515                        idx, filter.block_start, block_end, total_written);
516                }
517                earliest_start = filter.block_start;
518                earliest_idx = Some(idx);
519            }
520        }
521
522        earliest_idx.map(|idx| (idx, earliest_start))
523    }
524
525    /// Get the next filter's block start position
526    pub fn next_filter_pos(&self) -> Option<u64> {
527        self.stack.first().map(|f| f.block_start)
528    }
529
530    /// Get the earliest filter end position (block_start + block_length)
531    pub fn next_filter_end(&self) -> Option<u64> {
532        self.stack
533            .iter()
534            .map(|f| f.block_start + (f.block_length & VM_MEMMASK) as u64)
535            .min()
536    }
537
538    /// Peek at the next filter without removing it
539    pub fn peek_filter(&self) -> Option<&PreparedFilter> {
540        self.stack.first()
541    }
542
543    /// Execute pending filters on the sliding window buffer.
544    /// total_written is the absolute total bytes written so far.
545    /// window is the circular sliding window (read-only!).
546    /// window_mask is used for wrapping.
547    /// Returns (filter_end_position, filtered_data) if a filter was executed.
548    /// The filtered data should be written directly to output, NOT back to window.
549    pub fn execute_filter_at_index(
550        &mut self,
551        filter_idx: usize,
552        window: &[u8],
553        window_mask: usize,
554        total_written: u64,
555    ) -> Option<(u64, &[u8])> {
556        if filter_idx >= self.stack.len() {
557            return None;
558        }
559
560        // Remove and execute the filter at the specified index
561        let filter = self.stack.remove(filter_idx);
562        let block_start = filter.block_start;
563        let block_length = (filter.block_length & VM_MEMMASK) as usize;
564        let block_end = block_start + block_length as u64;
565
566        #[cfg(test)]
567        eprintln!(
568            "EXECUTE filter {:?}: start={}, len={}, total_written={}",
569            filter.filter_type, block_start, block_length, total_written
570        );
571        #[cfg(not(test))]
572        let _ = total_written;
573
574        // Copy data from window to VM memory using bulk copy when possible
575        let copy_len = block_length.min(VM_MEMSIZE);
576        let window_start = (block_start as usize) & window_mask;
577
578        // Check if we can do a contiguous copy (no wrap in window)
579        if window_start + copy_len <= window.len() {
580            self.mem[..copy_len].copy_from_slice(&window[window_start..window_start + copy_len]);
581        } else {
582            // Slow path: wrapping copy
583            let first_part = window.len() - window_start;
584            self.mem[..first_part].copy_from_slice(&window[window_start..]);
585            self.mem[first_part..copy_len].copy_from_slice(&window[..copy_len - first_part]);
586        }
587
588        #[cfg(test)]
589        {
590            if block_start == 46592 {
591                // Show window contents at filter start
592                eprintln!(
593                    "  WINDOW at filter start: window[46592..46608] = {:02x?}",
594                    (46592..46608)
595                        .map(|p| window[p & window_mask])
596                        .collect::<Vec<_>>()
597                );
598                // Show input bytes
599                eprintln!("  INPUT to filter: mem[0..16] = {:02x?}", &self.mem[0..16]);
600                eprintln!(
601                    "  INPUT to filter: mem[9456..9472] = {:02x?}",
602                    &self.mem[9456..9472]
603                );
604            }
605        }
606
607        #[cfg(test)]
608        {
609            eprintln!("  init_r: {:?}", filter.init_r);
610        }
611
612        // Execute filter
613        let (filtered_data_offset, filtered_size) = self.execute_filter(&filter, block_length);
614
615        #[cfg(test)]
616        eprintln!(
617            "  result: filtered_data_offset={}, filtered_size={}",
618            filtered_data_offset, filtered_size
619        );
620
621        // Return a slice into VM memory — no allocation needed.
622        // The caller must use this before the next execute_filter call.
623        let output_size = filtered_size.max(block_length);
624        let data = if filtered_size > 0 && filtered_size <= output_size {
625            &self.mem[filtered_data_offset..filtered_data_offset + filtered_size]
626        } else {
627            // Filter failed or no output - return original data
628            &self.mem[0..block_length]
629        };
630
631        Some((block_end, data))
632    }
633
634    /// Execute pending filters on the output buffer
635    /// write_pos is the absolute total bytes written so far
636    /// The buffer is the full output buffer, not the sliding window
637    pub fn execute_filters(
638        &mut self,
639        buffer: &mut [u8],
640        _write_pos: u64,
641    ) -> Option<(usize, usize)> {
642        if self.stack.is_empty() {
643            return None;
644        }
645
646        let filter = &self.stack[0];
647        let block_start = filter.block_start as usize;
648        let block_length = (filter.block_length & VM_MEMMASK) as usize;
649
650        // Check if we've written enough data to cover this filter's range
651        if block_start + block_length > buffer.len() {
652            return None;
653        }
654
655        // Now safe to remove and execute
656        let filter = self.stack.remove(0);
657
658        #[cfg(test)]
659        eprintln!(
660            "EXECUTE filter {:?}: start={}, len={}, buffer.len={}",
661            filter.filter_type,
662            block_start,
663            block_length,
664            buffer.len()
665        );
666
667        // Copy data to VM memory
668        let copy_len = block_length.min(VM_MEMSIZE);
669        self.mem[..copy_len].copy_from_slice(&buffer[block_start..block_start + copy_len]);
670
671        #[cfg(test)]
672        {
673            eprintln!("  init_r: {:?}", filter.init_r);
674            if block_start <= 4096 && block_start + block_length > 4096 {
675                let _offset = 4096 - block_start;
676                eprintln!(
677                    "  BEFORE buffer[4096..4104]: {:02x?}",
678                    &buffer[4096..4104.min(buffer.len())]
679                );
680            }
681        }
682
683        // Execute filter
684        let (filtered_data, filtered_size) = self.execute_filter(&filter, block_length);
685
686        #[cfg(test)]
687        eprintln!(
688            "  result: filtered_data={}, filtered_size={}",
689            filtered_data, filtered_size
690        );
691
692        if filtered_size > 0 && filtered_size <= block_length {
693            // Copy filtered data back
694            buffer[block_start..block_start + filtered_size]
695                .copy_from_slice(&self.mem[filtered_data..filtered_data + filtered_size]);
696
697            #[cfg(test)]
698            if block_start <= 4096 && block_start + block_length > 4096 {
699                eprintln!(
700                    "  AFTER buffer[4096..4104]: {:02x?}",
701                    &buffer[4096..4104.min(buffer.len())]
702                );
703            }
704        }
705
706        Some((block_start, filtered_size.max(block_length)))
707    }
708
709    /// Execute a single filter
710    fn execute_filter(&mut self, filter: &PreparedFilter, data_size: usize) -> (usize, usize) {
711        let r = filter.init_r;
712
713        match filter.filter_type {
714            StandardFilter::None => (0, data_size),
715            StandardFilter::E8 | StandardFilter::E8E9 => self.filter_e8e9(
716                r[4] as usize,
717                r[6],
718                filter.filter_type == StandardFilter::E8E9,
719            ),
720            StandardFilter::Itanium => self.filter_itanium(r[4] as usize, r[6]),
721            StandardFilter::Delta => self.filter_delta(r[4] as usize, r[0] as usize),
722            StandardFilter::Rgb => self.filter_rgb(r[4] as usize, r[0] as usize, r[1] as usize),
723            StandardFilter::Audio => self.filter_audio(r[4] as usize, r[0] as usize),
724        }
725    }
726
727    /// E8/E8E9 filter - x86 CALL/JMP address conversion
728    fn filter_e8e9(
729        &mut self,
730        data_size: usize,
731        file_offset: u32,
732        include_e9: bool,
733    ) -> (usize, usize) {
734        if !(4..=VM_MEMSIZE).contains(&data_size) {
735            return (0, 0);
736        }
737
738        const FILE_SIZE: u32 = 0x1000000;
739
740        // Word-at-a-time search for E8/E9 bytes (SWAR, ~8x faster than byte-by-byte)
741        let search_end = data_size - 4;
742        let mut cur_pos: usize = 0;
743
744        while cur_pos < search_end {
745            let found = if include_e9 {
746                byte_search::find_byte2(&self.mem[cur_pos..search_end], 0xe8, 0xe9)
747            } else {
748                byte_search::find_byte(&self.mem[cur_pos..search_end], 0xe8)
749            };
750            if let Some(offset) = found {
751                cur_pos += offset;
752                let addr_pos = cur_pos + 1;
753                let offset_val = addr_pos as u32 + file_offset;
754                let addr = u32::from_le_bytes([
755                    self.mem[addr_pos],
756                    self.mem[addr_pos + 1],
757                    self.mem[addr_pos + 2],
758                    self.mem[addr_pos + 3],
759                ]);
760                Self::transform_e8e9_addr(
761                    &mut self.mem[addr_pos..addr_pos + 4],
762                    addr,
763                    offset_val,
764                    FILE_SIZE,
765                );
766                cur_pos = addr_pos + 4;
767            } else {
768                break;
769            }
770        }
771
772        (0, data_size)
773    }
774
775    /// Transform an E8/E9 address in place
776    #[inline(always)]
777    fn transform_e8e9_addr(dest: &mut [u8], addr: u32, offset: u32, file_size: u32) {
778        if (addr & 0x80000000) != 0 {
779            // addr < 0
780            if (addr.wrapping_add(offset) & 0x80000000) == 0 {
781                let new_addr = addr.wrapping_add(file_size);
782                dest.copy_from_slice(&new_addr.to_le_bytes());
783            }
784        } else {
785            // addr >= 0
786            if (addr.wrapping_sub(file_size) & 0x80000000) != 0 {
787                let new_addr = addr.wrapping_sub(offset);
788                dest.copy_from_slice(&new_addr.to_le_bytes());
789            }
790        }
791    }
792
793    /// Itanium filter - IA-64 address conversion
794    fn filter_itanium(&mut self, data_size: usize, file_offset: u32) -> (usize, usize) {
795        if !(21..=VM_MEMSIZE).contains(&data_size) {
796            return (0, 0);
797        }
798
799        static MASKS: [u8; 16] = [4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0];
800
801        let mut cur_pos: usize = 0;
802        let mut file_off = file_offset >> 4;
803
804        while cur_pos < data_size - 21 {
805            let byte_val = (self.mem[cur_pos] & 0x1f) as i32 - 0x10;
806            if byte_val >= 0 {
807                let cmd_mask = MASKS[byte_val as usize];
808                if cmd_mask != 0 {
809                    for i in 0..=2 {
810                        if (cmd_mask & (1 << i)) != 0 {
811                            let start_pos = i * 41 + 5;
812                            let op_type = self.itanium_get_bits(cur_pos, start_pos + 37, 4);
813                            if op_type == 5 {
814                                let offset = self.itanium_get_bits(cur_pos, start_pos + 13, 20);
815                                self.itanium_set_bits(
816                                    cur_pos,
817                                    (offset.wrapping_sub(file_off)) & 0xfffff,
818                                    start_pos + 13,
819                                    20,
820                                );
821                            }
822                        }
823                    }
824                }
825            }
826            cur_pos += 16;
827            file_off = file_off.wrapping_add(1);
828        }
829
830        (0, data_size)
831    }
832
833    fn itanium_get_bits(&self, base: usize, bit_pos: usize, bit_count: usize) -> u32 {
834        let in_addr = base + bit_pos / 8;
835        let in_bit = bit_pos & 7;
836
837        let mut bit_field: u32 = 0;
838        if in_addr < self.mem.len() {
839            bit_field |= self.mem[in_addr] as u32;
840        }
841        if in_addr + 1 < self.mem.len() {
842            bit_field |= (self.mem[in_addr + 1] as u32) << 8;
843        }
844        if in_addr + 2 < self.mem.len() {
845            bit_field |= (self.mem[in_addr + 2] as u32) << 16;
846        }
847        if in_addr + 3 < self.mem.len() {
848            bit_field |= (self.mem[in_addr + 3] as u32) << 24;
849        }
850
851        bit_field >>= in_bit;
852        bit_field & (0xffffffff >> (32 - bit_count))
853    }
854
855    fn itanium_set_bits(&mut self, base: usize, bit_field: u32, bit_pos: usize, bit_count: usize) {
856        let in_addr = base + bit_pos / 8;
857        let in_bit = bit_pos & 7;
858
859        let and_mask = !(((1u32 << bit_count) - 1) << in_bit);
860        let bit_field = bit_field << in_bit;
861
862        for i in 0..4 {
863            if in_addr + i < self.mem.len() {
864                self.mem[in_addr + i] &= (and_mask >> (i * 8)) as u8;
865                self.mem[in_addr + i] |= (bit_field >> (i * 8)) as u8;
866            }
867        }
868    }
869
870    /// Delta filter - byte delta encoding
871    fn filter_delta(&mut self, data_size: usize, channels: usize) -> (usize, usize) {
872        if data_size > VM_MEMSIZE / 2 || channels > MAX_UNPACK_CHANNELS || channels == 0 {
873            return (0, 0);
874        }
875
876        let border = data_size * 2;
877        let mut src_pos = 0;
878
879        for cur_channel in 0..channels {
880            let mut prev_byte: u8 = 0;
881            let mut dest_pos = data_size + cur_channel;
882            while dest_pos < border {
883                prev_byte = prev_byte.wrapping_sub(self.mem[src_pos]);
884                self.mem[dest_pos] = prev_byte;
885                src_pos += 1;
886                dest_pos += channels;
887            }
888        }
889
890        (data_size, data_size)
891    }
892
893    /// RGB filter - predictive color filter
894    fn filter_rgb(&mut self, data_size: usize, width: usize, pos_r: usize) -> (usize, usize) {
895        let width = width.saturating_sub(3);
896        if !(3..=VM_MEMSIZE / 2).contains(&data_size) || width > data_size || pos_r > 2 {
897            return (0, 0);
898        }
899
900        const CHANNELS: usize = 3;
901        let mut src_idx = 0;
902
903        for cur_channel in 0..CHANNELS {
904            let mut prev_byte: u32 = 0;
905
906            let mut i = cur_channel;
907            while i < data_size {
908                let predicted = if i >= width + 3 {
909                    let upper_idx = data_size + i - width;
910                    let upper_byte = self.mem[upper_idx] as u32;
911                    let upper_left_byte = self.mem[upper_idx - 3] as u32;
912
913                    let mut pred = prev_byte
914                        .wrapping_add(upper_byte)
915                        .wrapping_sub(upper_left_byte);
916                    let pa = (pred as i32 - prev_byte as i32).unsigned_abs();
917                    let pb = (pred as i32 - upper_byte as i32).unsigned_abs();
918                    let pc = (pred as i32 - upper_left_byte as i32).unsigned_abs();
919
920                    if pa <= pb && pa <= pc {
921                        pred = prev_byte;
922                    } else if pb <= pc {
923                        pred = upper_byte;
924                    } else {
925                        pred = upper_left_byte;
926                    }
927                    pred
928                } else {
929                    prev_byte
930                };
931
932                prev_byte = predicted.wrapping_sub(self.mem[src_idx] as u32) & 0xff;
933                self.mem[data_size + i] = prev_byte as u8;
934                src_idx += 1;
935                i += CHANNELS;
936            }
937        }
938
939        // Apply RGB correlation
940        let border = data_size - 2;
941        let mut i = pos_r;
942        while i < border {
943            let g = self.mem[data_size + i + 1];
944            self.mem[data_size + i] = self.mem[data_size + i].wrapping_add(g);
945            self.mem[data_size + i + 2] = self.mem[data_size + i + 2].wrapping_add(g);
946            i += 3;
947        }
948
949        (data_size, data_size)
950    }
951
952    /// Audio filter - audio sample predictor
953    fn filter_audio(&mut self, data_size: usize, channels: usize) -> (usize, usize) {
954        if data_size > VM_MEMSIZE / 2 || channels > 128 || channels == 0 {
955            return (0, 0);
956        }
957
958        let mut src_idx = 0;
959
960        for cur_channel in 0..channels {
961            let mut prev_byte: u32 = 0;
962            let mut prev_delta: i32 = 0;
963            let mut dif = [0u32; 7];
964            let mut d1: i32 = 0;
965            let mut d2: i32 = 0;
966            let mut k1: i32 = 0;
967            let mut k2: i32 = 0;
968            let mut k3: i32 = 0;
969
970            let mut i = cur_channel;
971            let mut byte_count = 0u32;
972            while i < data_size {
973                let d3 = d2;
974                d2 = prev_delta - d1;
975                d1 = prev_delta;
976
977                let predicted = (8i32 * prev_byte as i32 + k1 * d1 + k2 * d2 + k3 * d3) >> 3;
978                let predicted = (predicted as u32) & 0xff;
979
980                let cur_byte = self.mem[src_idx] as u32;
981                src_idx += 1;
982
983                let result = predicted.wrapping_sub(cur_byte) & 0xff;
984                self.mem[data_size + i] = result as u8;
985                // unrar: PrevDelta=(signed char)(Predicted-PrevByte);
986                // Compute difference as unsigned, then cast to signed char
987                prev_delta = (result.wrapping_sub(prev_byte) & 0xff) as u8 as i8 as i32;
988                prev_byte = result;
989
990                let d = ((cur_byte as i8) as i32) << 3;
991
992                dif[0] = dif[0].wrapping_add(d.unsigned_abs());
993                dif[1] = dif[1].wrapping_add((d - d1).unsigned_abs());
994                dif[2] = dif[2].wrapping_add((d + d1).unsigned_abs());
995                dif[3] = dif[3].wrapping_add((d - d2).unsigned_abs());
996                dif[4] = dif[4].wrapping_add((d + d2).unsigned_abs());
997                dif[5] = dif[5].wrapping_add((d - d3).unsigned_abs());
998                dif[6] = dif[6].wrapping_add((d + d3).unsigned_abs());
999
1000                if (byte_count & 0x1f) == 0 {
1001                    let mut min_dif = dif[0];
1002                    let mut num_min_dif = 0;
1003                    dif[0] = 0;
1004
1005                    for j in 1..7 {
1006                        if dif[j] < min_dif {
1007                            min_dif = dif[j];
1008                            num_min_dif = j;
1009                        }
1010                        dif[j] = 0;
1011                    }
1012
1013                    match num_min_dif {
1014                        1 => {
1015                            if k1 >= -16 {
1016                                k1 -= 1;
1017                            }
1018                        }
1019                        2 => {
1020                            if k1 < 16 {
1021                                k1 += 1;
1022                            }
1023                        }
1024                        3 => {
1025                            if k2 >= -16 {
1026                                k2 -= 1;
1027                            }
1028                        }
1029                        4 => {
1030                            if k2 < 16 {
1031                                k2 += 1;
1032                            }
1033                        }
1034                        5 => {
1035                            if k3 >= -16 {
1036                                k3 -= 1;
1037                            }
1038                        }
1039                        6 => {
1040                            if k3 < 16 {
1041                                k3 += 1;
1042                            }
1043                        }
1044                        _ => {}
1045                    }
1046                }
1047
1048                i += channels;
1049                byte_count += 1;
1050            }
1051        }
1052
1053        (data_size, data_size)
1054    }
1055}
1056
1057impl Default for RarVM {
1058    fn default() -> Self {
1059        Self::new()
1060    }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065    use super::*;
1066
1067    #[test]
1068    fn test_filter_identification() {
1069        // Test that filter identification works with known CRCs
1070        assert_eq!(RarVM::identify_filter(&[]), StandardFilter::None);
1071    }
1072
1073    #[test]
1074    fn test_delta_filter() {
1075        let mut vm = RarVM::new();
1076
1077        // Simple delta test: 3 channels, 6 bytes
1078        vm.mem[0] = 10;
1079        vm.mem[1] = 20;
1080        vm.mem[2] = 30;
1081        vm.mem[3] = 5;
1082        vm.mem[4] = 10;
1083        vm.mem[5] = 15;
1084
1085        let (offset, size) = vm.filter_delta(6, 3);
1086        assert_eq!(offset, 6);
1087        assert_eq!(size, 6);
1088    }
1089
1090    #[test]
1091    fn test_e8_filter() {
1092        let mut vm = RarVM::new();
1093
1094        // E8 filter test
1095        vm.mem[0] = 0xe8;
1096        vm.mem[1] = 0x00;
1097        vm.mem[2] = 0x00;
1098        vm.mem[3] = 0x10;
1099        vm.mem[4] = 0x00;
1100
1101        let (offset, size) = vm.filter_e8e9(5, 0, false);
1102        assert_eq!(offset, 0);
1103        assert_eq!(size, 5);
1104    }
1105}