commonlibsse_ng\rel\id\id_database/
unpack.rs

1use crate::rel::id::id_database::byte_reader::{read_le_u16, read_le_u32, read_le_u64, read_u8};
2use crate::rel::id::Mapping;
3use std::io::Read;
4
5/// Unpacks the ID database from the binary file and writes it into the memory map(sorted by ID).
6///
7/// # Errors
8/// - If the memory allocated as `MemoryMap` is not consistent as the length of the mapping data array.
9/// - Returns an error if the binary data cannot be properly parsed.
10pub(crate) fn unpack_file<R>(
11    mem_map: &mut [Mapping],
12    reader: &mut R,
13    ptr_size: u64,
14) -> Result<(), UnpackError>
15where
16    R: Read,
17{
18    // TODO: Parse With `winnow` crate, we can know the exact binary position at the time of the error.
19    let mut offset: u64;
20    let mut prev_id: u64 = 0;
21    let mut prev_offset: u64 = 0;
22
23    let mappings = mem_map;
24    for mapping in &mut *mappings {
25        let type_byte = read_u8(reader)?;
26
27        let low = type_byte & 0xF;
28        let high = type_byte >> 4;
29
30        let id = parse_id(low, reader, prev_id)?;
31
32        let tmp = if (high & 8) != 0 {
33            prev_offset / ptr_size
34        } else {
35            prev_offset
36        };
37
38        offset = parse_offset(high, reader, tmp)?;
39
40        if (high & 8) != 0 {
41            offset *= ptr_size;
42        }
43
44        *mapping = Mapping { id, offset };
45        prev_id = id;
46        prev_offset = offset;
47    }
48
49    mappings.sort_by(|a, b| a.id.cmp(&b.id));
50
51    Ok(())
52}
53
54fn parse_id<R>(low: u8, reader: &mut R, prev_id: u64) -> Result<u64, UnpackError>
55where
56    R: Read,
57{
58    Ok(match low {
59        0 => read_le_u64(reader)?,
60        1 => prev_id + 1,
61        2 => prev_id + read_u8(reader)? as u64,
62        3 => prev_id - read_u8(reader)? as u64,
63        4 => prev_id + read_le_u16(reader)? as u64,
64        5 => prev_id - read_le_u16(reader)? as u64,
65        6 => read_le_u16(reader)? as u64,
66        7 => read_le_u32(reader)? as u64,
67        _ => return Err(UnpackError::InvalidId { id: low }),
68    })
69}
70
71fn parse_offset<R>(high: u8, reader: &mut R, prev_offset: u64) -> Result<u64, UnpackError>
72where
73    R: Read,
74{
75    Ok(match high & 7 {
76        0 => read_le_u64(reader)?,
77        1 => prev_offset + 1,
78        2 => prev_offset + read_u8(reader)? as u64,
79        3 => prev_offset - read_u8(reader)? as u64,
80        4 => prev_offset + read_le_u16(reader)? as u64,
81        5 => prev_offset - read_le_u16(reader)? as u64,
82        6 => read_le_u16(reader)? as u64,
83        7 => read_le_u32(reader)? as u64,
84        _ => {
85            return Err(UnpackError::InvalidOffset {
86                offset: prev_offset,
87            })
88        }
89    })
90}
91
92#[derive(Debug, snafu::Snafu)]
93pub enum UnpackError {
94    /// Invalid ID encountered
95    #[snafu(display("Invalid ID encountered: {}", id))]
96    InvalidId { id: u8 },
97
98    /// Invalid offset encountered
99    #[snafu(display("Invalid offset encountered: {}", offset))]
100    InvalidOffset { offset: u64 },
101
102    /// Inherited IO Error
103    #[snafu(transparent)]
104    Io { source: std::io::Error },
105}
106
107// io::Error doesn't have `Clone`. Therefore, implement manually.
108impl Clone for UnpackError {
109    fn clone(&self) -> Self {
110        match self {
111            Self::InvalidId { id } => Self::InvalidId { id: *id },
112            Self::InvalidOffset { offset } => Self::InvalidOffset { offset: *offset },
113            Self::Io { source: err } => Self::Io {
114                source: std::io::Error::new(err.kind(), err.to_string()),
115            },
116        }
117    }
118}