|
| 1 | +use std::collections::HashMap; |
| 2 | +use std::fs::File; |
| 3 | +use std::io::{BufRead, BufReader, Cursor, Seek, SeekFrom}; |
| 4 | +use std::path::PathBuf; |
| 5 | + |
| 6 | +use byteorder::{LittleEndian, ReadBytesExt}; |
| 7 | + |
| 8 | +use crate::common::*; |
| 9 | + |
| 10 | +fn read_file_name<T>(rdr: &mut T) -> Result<String, KArchiveError> |
| 11 | +where |
| 12 | + T: BufRead + Seek, |
| 13 | +{ |
| 14 | + let mut buf = Vec::<u8>::new(); |
| 15 | + let size = rdr.read_until(0, &mut buf)?; |
| 16 | + rdr.seek(SeekFrom::Current(256 - size as i64))?; |
| 17 | + Ok(String::from_utf8( |
| 18 | + buf.strip_suffix(&[0]) |
| 19 | + .ok_or(KArchiveError::Other( |
| 20 | + "Failed to strip suffix (malformed or incomplete archive)", |
| 21 | + ))? |
| 22 | + .to_vec(), |
| 23 | + )? |
| 24 | + .trim_start_matches(['.', '\\']) |
| 25 | + .replace('\\', "/") |
| 26 | + .to_string()) |
| 27 | +} |
| 28 | + |
| 29 | +pub(crate) fn parse(path: PathBuf) -> Result<KArchive, KArchiveError> { |
| 30 | + let buffer = benchmark(&path)?; |
| 31 | + let mut file = match &buffer { |
| 32 | + Some(buf) => BufReader::new(InternalFile::Buffer(Cursor::new(buf))), |
| 33 | + None => BufReader::new(InternalFile::RealFile(File::open(&path)?)), |
| 34 | + }; |
| 35 | + let mut files: HashMap<PathBuf, KFileInfo> = HashMap::new(); |
| 36 | + // Skip the first 10 bytes |
| 37 | + file.seek_relative(10)?; |
| 38 | + let file_count = file.read_u16::<LittleEndian>()?; |
| 39 | + let parse_result = (0..file_count).try_for_each(|_| { |
| 40 | + let name = read_file_name(&mut file)?; |
| 41 | + // bar files are weird. in M39A bars, the filename takes 252 bytes rather than 256 |
| 42 | + // So let's check if we just read one of those |
| 43 | + if file.read_i32::<LittleEndian>()? == -1 { |
| 44 | + file.seek_relative(-8)?; |
| 45 | + } else { |
| 46 | + file.seek_relative(-4)?; |
| 47 | + } |
| 48 | + let magic1 = file.read_i32::<LittleEndian>()?; |
| 49 | + let magic2 = file.read_i32::<LittleEndian>()?; |
| 50 | + if magic1 != 3 || magic2 != -1 { |
| 51 | + return Err(KArchiveError::ParseError(format!( |
| 52 | + "magic numbers are wrong: {} {}", |
| 53 | + magic1, magic2 |
| 54 | + ))); |
| 55 | + } |
| 56 | + let size = file.read_u32::<LittleEndian>()? as u64; |
| 57 | + file.seek_relative(4)?; |
| 58 | + let offset = file.stream_position()?; |
| 59 | + file.seek_relative(size as i64)?; |
| 60 | + |
| 61 | + files.insert( |
| 62 | + name.into(), |
| 63 | + KFileInfo { |
| 64 | + size, |
| 65 | + offset, |
| 66 | + cipher: None, |
| 67 | + }, |
| 68 | + ); |
| 69 | + Ok(()) |
| 70 | + }); |
| 71 | + match parse_result { |
| 72 | + Ok(_) => {} |
| 73 | + Err(e) => { |
| 74 | + eprintln!("k_archives: Error in archive parsing: {}", e); |
| 75 | + eprintln!("k_archives: Continuing with {} files parsed", files.len()); |
| 76 | + } |
| 77 | + } |
| 78 | + Ok(KArchive::new(path, files, buffer)) |
| 79 | +} |
| 80 | + |
| 81 | +#[cfg(test)] |
| 82 | +mod tests { |
| 83 | + use super::*; |
| 84 | + use std::io::Cursor; |
| 85 | + #[test] |
| 86 | + fn test_filename() { |
| 87 | + let cursor = Cursor::new(vec![ |
| 88 | + 92, 74, 69, 65, 50, 48, 50, 52, 48, 52, 49, 53, 48, 48, 99, 111, 110, 116, 101, 110, |
| 89 | + 116, 115, 92, 53, 92, 102, 92, 56, 92, 54, 52, 52, 102, 48, 52, 99, 57, 102, 52, 48, |
| 90 | + 49, 50, 100, 100, 55, 50, 53, 102, 57, 50, 49, 52, 51, 54, 55, 54, 98, 97, 99, 99, 55, |
| 91 | + 51, 52, 50, 52, 54, 0, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 92 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 93 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 94 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 95 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 96 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 97 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 98 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 99 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 100 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 101 | + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, |
| 102 | + 254, 254, 254, 254, 254, 254, |
| 103 | + ]); |
| 104 | + let mut filename = BufReader::new(cursor); |
| 105 | + assert_eq!( |
| 106 | + read_file_name(&mut filename).unwrap(), |
| 107 | + "JEA2024041500contents/5/f/8/644f04c9f4012dd725f92143676bacc734246" |
| 108 | + ) |
| 109 | + } |
| 110 | +} |
0 commit comments