1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
use super::BinArchive; use anyhow::{anyhow, Result}; use byteorder::{LittleEndian, ReadBytesExt}; use encoding_rs::SHIFT_JIS; use std::collections::HashMap; use std::io::{Cursor, Read, Seek, SeekFrom}; struct BinArchiveHeader { file_size: u32, data_size: u32, normal_pointer_count: u32, label_count: u32, } fn read_header(bytes: &[u8]) -> Result<BinArchiveHeader> { if bytes.len() < 0x20 { return Err(anyhow!( "File is not large enough to contain a valid bin archive header" )); } let mut reader = Cursor::new(bytes); let file_size = reader.read_u32::<LittleEndian>()?; let data_size = reader.read_u32::<LittleEndian>()?; let normal_pointer_count = reader.read_u32::<LittleEndian>()?; let label_count = reader.read_u32::<LittleEndian>()?; Ok(BinArchiveHeader { file_size, data_size, normal_pointer_count, label_count, }) } fn validate_header(header: &BinArchiveHeader, raw_archive: &[u8]) -> Result<()> { let min_archive_size = (header.data_size + header.normal_pointer_count * 4 + header.label_count * 8 + 0x20) as usize; if header.file_size as usize != raw_archive.len() { return Err(anyhow!( "The actual file and the archive header disagree on the archive length" )); } if min_archive_size > raw_archive.len() { return Err(anyhow!( "File is not large enough to support size indicated in the archive header" )); } Ok(()) } fn read_string(reader: &mut Cursor<&[u8]>, addr: u64) -> Result<String> { let end_addr = reader.position(); reader.seek(SeekFrom::Start(addr))?; let mut buffer: Vec<u8> = Vec::new(); let mut next = reader.read_u8()?; while next != 0 { buffer.push(next); next = reader.read_u8()?; } let (result, _enc, errors) = SHIFT_JIS.decode(buffer.as_slice()); if errors { return Err(anyhow!("Unable to decode shift-jis string.")); } reader.seek(SeekFrom::Start(end_addr))?; Ok(result.into()) } fn deserialize_normal_pointers( archive: &mut BinArchive, header: &BinArchiveHeader, reader: &mut Cursor<&[u8]>, ) -> Result<()> { for i in 0..header.normal_pointer_count { let ptr = reader.read_u32::<LittleEndian>()? as usize; let end_addr = reader.position(); if ptr >= archive.size() { return Err(anyhow!("Bad pointer number {} to address 0x{:x}", i, ptr)); } reader.seek(SeekFrom::Start((ptr + 0x20) as u64))?; let data_ptr = reader.read_u32::<LittleEndian>()?; if data_ptr >= header.data_size { let text = read_string(reader, (data_ptr + 0x20) as u64)?; archive.put_text(ptr, Some(&text))?; } else { archive.put_pointer(ptr, data_ptr as usize)?; } reader.seek(SeekFrom::Start(end_addr))?; } Ok(()) } fn deserialize_labels( archive: &mut BinArchive, header: &BinArchiveHeader, reader: &mut Cursor<&[u8]>, ) -> Result<()> { let text_start = header.data_size + header.normal_pointer_count * 4 + header.label_count * 8 + 0x20; for i in 0..header.label_count { let ptr = reader.read_u32::<LittleEndian>()? as usize; if ptr >= archive.size() { return Err(anyhow!("Bad pointer number {} to address 0x{:x}", i, ptr)); } let text_ptr = text_start + reader.read_u32::<LittleEndian>()?; let text = read_string(reader, text_ptr as u64)?; archive.put_label(ptr, &text)?; } Ok(()) } pub fn from_bytes(raw_archive: &[u8]) -> Result<BinArchive> { let header = read_header(raw_archive)?; validate_header(&header, raw_archive)?; let mut reader = Cursor::new(raw_archive); reader.seek(SeekFrom::Start(0x20))?; let mut data = Vec::new(); data.resize(header.data_size as usize, 0); reader.read_exact(data.as_mut_slice())?; let mut archive = BinArchive { data, text_pointers: HashMap::new(), labels: HashMap::new(), internal_pointers: HashMap::new(), }; deserialize_normal_pointers(&mut archive, &header, &mut reader)?; deserialize_labels(&mut archive, &header, &mut reader)?; Ok(archive) }