Skip to main content

cridecoder/hca/
hca_file.rs

1//! High-level HCA decoder with streaming capabilities
2
3use std::fs::File;
4use std::io::{self, Read, Seek, SeekFrom, Write};
5
6pub use super::decoder::HcaInfo;
7use super::decoder::{ClHca, HcaError};
8
9/// Key test parameters for testing HCA decryption keys
10#[derive(Debug, Clone, Default)]
11pub struct KeyTest {
12    pub key: u64,
13    pub subkey: u64,
14    pub start_offset: u32,
15    pub best_score: i32,
16    pub best_key: u64,
17}
18
19// Key testing constants
20const HCA_KEY_SCORE_SCALE: i32 = 10;
21const HCA_KEY_MAX_SKIP_BLANKS: i32 = 1200;
22const HCA_KEY_MIN_TEST_FRAMES: i32 = 3;
23const HCA_KEY_MAX_TEST_FRAMES: i32 = 7;
24const HCA_KEY_MAX_FRAME_SCORE: i32 = 600;
25const HCA_KEY_MAX_TOTAL_SCORE: i32 = HCA_KEY_MAX_TEST_FRAMES * 50 * HCA_KEY_SCORE_SCALE;
26
27/// High-level HCA decoder wrapping the low-level ClHCA decoder with streaming capabilities
28pub struct HcaDecoder<R: Read + Seek> {
29    reader: R,
30    info: HcaInfo,
31    handle: ClHca,
32    buf: Vec<u8>,
33    fbuf: Vec<f32>,
34    current_delay: i32,
35    current_block: u32,
36    owns_file: bool,
37}
38
39impl HcaDecoder<File> {
40    /// Create a new HCA decoder from a file path
41    pub fn from_file(filename: &str) -> Result<Self, HcaDecoderError> {
42        let file = File::open(filename)?;
43        let mut decoder = HcaDecoder::from_reader(file)?;
44        decoder.owns_file = true;
45        Ok(decoder)
46    }
47}
48
49impl<R: Read + Seek> HcaDecoder<R> {
50    /// Create a new HCA decoder from a reader
51    pub fn from_reader(mut reader: R) -> Result<Self, HcaDecoderError> {
52        // Test header
53        let mut header_buf = [0u8; 8];
54        reader.read_exact(&mut header_buf)?;
55
56        let header_size = ClHca::is_hca_file(&header_buf).ok_or(HcaDecoderError::InvalidHeader)?;
57
58        if header_size > 0x1000 {
59            return Err(HcaDecoderError::InvalidHeader);
60        }
61
62        // Read full header
63        let mut full_header = vec![0u8; header_size];
64        reader.seek(SeekFrom::Start(0))?;
65        reader.read_exact(&mut full_header)?;
66
67        // Initialize decoder
68        let mut handle = ClHca::new();
69        handle.decode_header(&full_header)?;
70
71        let info = handle.get_info()?;
72
73        // Allocate buffers
74        let buf = vec![0u8; info.block_size as usize];
75        let fbuf = vec![0.0f32; info.channel_count as usize * info.samples_per_block];
76
77        let current_delay = info.encoder_delay as i32;
78
79        Ok(Self {
80            reader,
81            info,
82            handle,
83            buf,
84            fbuf,
85            current_delay,
86            current_block: 0,
87            owns_file: false,
88        })
89    }
90
91    /// Reset the decoder to the beginning
92    pub fn reset(&mut self) {
93        self.handle.decode_reset();
94        self.current_block = 0;
95        self.current_delay = self.info.encoder_delay as i32;
96    }
97
98    /// Get the HCA file information
99    pub fn info(&self) -> &HcaInfo {
100        &self.info
101    }
102
103    /// Set the decryption key
104    pub fn set_encryption_key(&mut self, keycode: u64, subkey: u64) {
105        let key = if subkey != 0 {
106            keycode.wrapping_mul((subkey << 16) | (!subkey as u16 as u64).wrapping_add(2))
107        } else {
108            keycode
109        };
110        self.handle.set_key(key);
111    }
112
113    /// Read a single HCA frame/block
114    fn read_packet(&mut self) -> Result<(), HcaDecoderError> {
115        if self.current_block >= self.info.block_count {
116            return Err(HcaDecoderError::Eof);
117        }
118
119        let offset =
120            self.info.header_size as u64 + self.current_block as u64 * self.info.block_size as u64;
121        self.reader.seek(SeekFrom::Start(offset))?;
122        self.reader.read_exact(&mut self.buf)?;
123
124        self.current_block += 1;
125        Ok(())
126    }
127
128    /// Decode a single frame and return the samples
129    /// Returns (samples slice, num samples) or error
130    pub fn decode_frame(&mut self) -> Result<(&[f32], usize), HcaDecoderError> {
131        // Read packet
132        self.read_packet()?;
133
134        // Decode frame
135        self.handle.decode_block(&mut self.buf)?;
136
137        // Read samples
138        self.handle.read_samples(&mut self.fbuf);
139
140        let samples = self.info.samples_per_block as i32;
141        let mut discard = 0;
142
143        // Handle encoder delay
144        if self.current_delay > 0 {
145            if self.current_delay >= samples {
146                self.current_delay -= samples;
147                return Ok((&[], 0));
148            }
149            discard = self.current_delay;
150            self.current_delay = 0;
151        }
152
153        let start_idx = discard as usize * self.info.channel_count as usize;
154        let num_samples = (samples - discard) as usize;
155        Ok((&self.fbuf[start_idx..], num_samples))
156    }
157
158    /// Decode the entire HCA file and return all samples
159    pub fn decode_all(&mut self) -> Result<Vec<f32>, HcaDecoderError> {
160        self.reset();
161
162        let channel_count = self.info.channel_count as usize;
163        let total_samples = self.info.block_count as usize * self.info.samples_per_block;
164        let mut all_samples = Vec::with_capacity(total_samples * channel_count);
165
166        loop {
167            match self.decode_frame() {
168                Ok((samples, num_samples)) => {
169                    let samples_to_add = num_samples * channel_count;
170                    all_samples.extend_from_slice(&samples[..samples_to_add]);
171                }
172                Err(HcaDecoderError::Eof) => break,
173                Err(e) => return Err(e),
174            }
175        }
176
177        Ok(all_samples)
178    }
179
180    /// Seek to a specific sample position
181    pub fn seek(&mut self, sample_num: u32) {
182        let target_sample = sample_num + self.info.encoder_delay;
183        let loop_start_block = target_sample / self.info.samples_per_block as u32;
184        let loop_start_delay =
185            target_sample - (loop_start_block * self.info.samples_per_block as u32);
186
187        self.current_block = loop_start_block;
188        self.current_delay = loop_start_delay as i32;
189    }
190
191    /// Test if a key correctly decrypts the HCA file
192    pub fn test_key(&mut self, kt: &mut KeyTest) {
193        let score = self.test_hca_score(kt);
194
195        // Wrong key
196        if score < 0 {
197            return;
198        }
199
200        // Update if something better is found
201        if kt.best_score <= 0 || (score < kt.best_score && score > 0) {
202            kt.best_score = score;
203            kt.best_key = kt.key;
204        }
205    }
206
207    /// Test a number of frames to see if key decrypts correctly
208    fn test_hca_score(&mut self, kt: &mut KeyTest) -> i32 {
209        let mut test_frames = 0;
210        let mut current_frame = 0u32;
211        let mut blank_frames = 0;
212        let mut total_score = 0;
213
214        let mut offset = kt.start_offset;
215        if offset == 0 {
216            offset = self.info.header_size;
217        }
218
219        self.set_encryption_key(kt.key, kt.subkey);
220
221        while test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < self.info.block_count {
222            let (score, should_break, new_offset) =
223                self.test_single_frame(kt, offset, blank_frames);
224            offset = new_offset;
225
226            if should_break {
227                total_score = -1;
228                break;
229            }
230
231            if score < 0 {
232                break;
233            }
234
235            current_frame += 1;
236
237            if score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS {
238                blank_frames += 1;
239                continue;
240            }
241
242            test_frames += 1;
243            total_score += scale_frame_score(score);
244
245            if total_score > HCA_KEY_MAX_TOTAL_SCORE {
246                break;
247            }
248        }
249
250        self.handle.decode_reset();
251        finalize_score(total_score, test_frames)
252    }
253
254    fn test_single_frame(
255        &mut self,
256        kt: &mut KeyTest,
257        offset: u32,
258        _blank_frames: i32,
259    ) -> (i32, bool, u32) {
260        if self.reader.seek(SeekFrom::Start(offset as u64)).is_err() {
261            return (-1, false, offset);
262        }
263
264        if self.reader.read_exact(&mut self.buf).is_err() {
265            return (-1, false, offset);
266        }
267
268        let score = self.handle.test_block(&mut self.buf);
269
270        // Get first non-blank frame
271        if kt.start_offset == 0 && score != 0 {
272            kt.start_offset = offset;
273        }
274
275        let new_offset = offset + self.info.block_size;
276
277        if !(0..=HCA_KEY_MAX_FRAME_SCORE).contains(&score) {
278            return (0, true, new_offset);
279        }
280
281        (score, false, new_offset)
282    }
283
284    /// Decode the entire file to 16-bit WAV stream
285    pub fn decode_to_wav<W: Write>(&mut self, w: &mut W) -> Result<(), HcaDecoderError> {
286        self.reset();
287
288        let total_samples = (self.info.block_count * self.info.samples_per_block as u32)
289            .saturating_sub(self.info.encoder_delay) as usize;
290        let total_pcm_bytes = total_samples * self.info.channel_count as usize * 2;
291
292        // Write WAV header
293        let mut header = [0u8; 44];
294        header[0..4].copy_from_slice(b"RIFF");
295        header[4..8].copy_from_slice(&((36 + total_pcm_bytes) as u32).to_le_bytes());
296        header[8..12].copy_from_slice(b"WAVE");
297        header[12..16].copy_from_slice(b"fmt ");
298        header[16..20].copy_from_slice(&16u32.to_le_bytes()); // fmt chunk size
299        header[20..22].copy_from_slice(&1u16.to_le_bytes()); // PCM format
300        header[22..24].copy_from_slice(&(self.info.channel_count as u16).to_le_bytes());
301        header[24..28].copy_from_slice(&self.info.sampling_rate.to_le_bytes());
302        let byte_rate = self.info.sampling_rate * self.info.channel_count * 2;
303        header[28..32].copy_from_slice(&byte_rate.to_le_bytes());
304        let block_align = (self.info.channel_count * 2) as u16;
305        header[32..34].copy_from_slice(&block_align.to_le_bytes());
306        header[34..36].copy_from_slice(&16u16.to_le_bytes()); // bits per sample
307        header[36..40].copy_from_slice(b"data");
308        header[40..44].copy_from_slice(&(total_pcm_bytes as u32).to_le_bytes());
309
310        w.write_all(&header)?;
311
312        let mut pcm_buf =
313            vec![0i16; self.info.samples_per_block * self.info.channel_count as usize];
314
315        loop {
316            match self.read_packet() {
317                Ok(()) => {}
318                Err(HcaDecoderError::Eof) => break,
319                Err(e) => return Err(e),
320            }
321
322            self.handle.decode_block(&mut self.buf)?;
323            self.handle.read_samples_16(&mut pcm_buf);
324
325            let samples = self.info.samples_per_block as i32;
326            let mut discard = 0;
327
328            if self.current_delay > 0 {
329                if self.current_delay >= samples {
330                    self.current_delay -= samples;
331                    continue;
332                }
333                discard = self.current_delay;
334                self.current_delay = 0;
335            }
336
337            let start = discard as usize * self.info.channel_count as usize;
338            let end = samples as usize * self.info.channel_count as usize;
339
340            if start >= end || end > pcm_buf.len() {
341                return Err(HcaDecoderError::InvalidSampleRange);
342            }
343
344            // Write samples as little-endian bytes
345            let mut data = vec![0u8; (end - start) * 2];
346            for (i, &sample) in pcm_buf[start..end].iter().enumerate() {
347                data[i * 2..i * 2 + 2].copy_from_slice(&sample.to_le_bytes());
348            }
349            w.write_all(&data)?;
350        }
351
352        Ok(())
353    }
354}
355
356fn scale_frame_score(score: i32) -> i32 {
357    match score {
358        1 => 1,
359        0 => 3 * HCA_KEY_SCORE_SCALE,
360        _ => score * HCA_KEY_SCORE_SCALE,
361    }
362}
363
364fn finalize_score(total_score: i32, test_frames: i32) -> i32 {
365    // Signal best possible score
366    if test_frames > HCA_KEY_MIN_TEST_FRAMES && total_score > 0 && total_score <= test_frames {
367        return 1;
368    }
369    total_score
370}
371
372/// Errors that can occur during HCA decoding
373#[derive(Debug)]
374pub enum HcaDecoderError {
375    Io(io::Error),
376    Hca(HcaError),
377    InvalidHeader,
378    InvalidSampleRange,
379    Eof,
380}
381
382impl std::fmt::Display for HcaDecoderError {
383    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384        match self {
385            Self::Io(e) => write!(f, "IO error: {}", e),
386            Self::Hca(e) => write!(f, "HCA error: {}", e),
387            Self::InvalidHeader => write!(f, "Invalid HCA header"),
388            Self::InvalidSampleRange => write!(f, "Invalid sample range"),
389            Self::Eof => write!(f, "End of file"),
390        }
391    }
392}
393
394impl std::error::Error for HcaDecoderError {}
395
396impl From<io::Error> for HcaDecoderError {
397    fn from(e: io::Error) -> Self {
398        Self::Io(e)
399    }
400}
401
402impl From<HcaError> for HcaDecoderError {
403    fn from(e: HcaError) -> Self {
404        Self::Hca(e)
405    }
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411
412    #[test]
413    fn test_scale_frame_score() {
414        assert_eq!(scale_frame_score(1), 1);
415        assert_eq!(scale_frame_score(0), 3 * HCA_KEY_SCORE_SCALE); // 30
416        assert_eq!(scale_frame_score(5), 5 * HCA_KEY_SCORE_SCALE); // 50
417        assert_eq!(scale_frame_score(-1), -1 * HCA_KEY_SCORE_SCALE); // -10
418    }
419
420    #[test]
421    fn test_finalize_score() {
422        // Best possible: enough frames, small positive score
423        assert_eq!(finalize_score(4, 5), 1); // total_score(4) <= test_frames(5), frames > 3
424                                             // Not enough frames
425        assert_eq!(finalize_score(2, 2), 2); // test_frames(2) <= MIN_TEST_FRAMES(3)
426                                             // Score too high
427        assert_eq!(finalize_score(100, 5), 100);
428        // Negative
429        assert_eq!(finalize_score(-1, 5), -1);
430    }
431
432    #[test]
433    fn test_key_test_default() {
434        let kt = KeyTest::default();
435        assert_eq!(kt.key, 0);
436        assert_eq!(kt.subkey, 0);
437        assert_eq!(kt.start_offset, 0);
438        assert_eq!(kt.best_score, 0);
439        assert_eq!(kt.best_key, 0);
440    }
441
442    #[test]
443    fn test_hca_decoder_error_display() {
444        let err = HcaDecoderError::InvalidHeader;
445        assert_eq!(format!("{}", err), "Invalid HCA header");
446
447        let err = HcaDecoderError::Eof;
448        assert_eq!(format!("{}", err), "End of file");
449
450        let err = HcaDecoderError::InvalidSampleRange;
451        assert_eq!(format!("{}", err), "Invalid sample range");
452    }
453}