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)
}