physis/
exh.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#![allow(clippy::unnecessary_fallible_conversions)] // This wrongly trips on binrw code
5
6use std::io::Cursor;
7
8use binrw::BinRead;
9use binrw::binrw;
10
11use crate::ByteSpan;
12use crate::common::Language;
13
14#[binrw]
15#[brw(magic = b"EXHF")]
16#[brw(big)]
17#[allow(dead_code)]
18pub struct EXHHeader {
19    pub(crate) version: u16,
20
21    pub data_offset: u16,
22    pub(crate) column_count: u16,
23    pub(crate) page_count: u16,
24    pub(crate) language_count: u16,
25
26    #[br(pad_before = 6)]
27    #[br(pad_after = 8)]
28    pub row_count: u32,
29}
30
31#[binrw]
32#[brw(repr(u16))]
33#[repr(u16)]
34#[derive(PartialEq, Eq, Clone, Debug)]
35pub enum ColumnDataType {
36    String = 0x0,
37    Bool = 0x1,
38    Int8 = 0x2,
39    UInt8 = 0x3,
40    Int16 = 0x4,
41    UInt16 = 0x5,
42    Int32 = 0x6,
43    UInt32 = 0x7,
44    Float32 = 0x9,
45    Int64 = 0xA,
46    UInt64 = 0xB,
47
48    PackedBool0 = 0x19,
49    PackedBool1 = 0x1A,
50    PackedBool2 = 0x1B,
51    PackedBool3 = 0x1C,
52    PackedBool4 = 0x1D,
53    PackedBool5 = 0x1E,
54    PackedBool6 = 0x1F,
55    PackedBool7 = 0x20,
56}
57
58#[binrw]
59#[brw(big)]
60pub struct ExcelColumnDefinition {
61    pub data_type: ColumnDataType,
62    pub offset: u16,
63}
64
65#[binrw]
66#[brw(big)]
67#[allow(dead_code)]
68pub struct ExcelDataPagination {
69    pub start_id: u32,
70    pub row_count: u32,
71}
72
73#[binrw]
74#[brw(big)]
75#[allow(dead_code)]
76pub struct EXH {
77    pub header: EXHHeader,
78
79    #[br(count = header.column_count)]
80    pub column_definitions: Vec<ExcelColumnDefinition>,
81
82    #[br(count = header.page_count)]
83    pub pages: Vec<ExcelDataPagination>,
84
85    #[br(count = header.language_count)]
86    pub languages: Vec<Language>,
87}
88
89impl EXH {
90    pub fn from_existing(buffer: ByteSpan) -> Option<EXH> {
91        EXH::read(&mut Cursor::new(&buffer)).ok()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::fs::read;
98    use std::path::PathBuf;
99
100    use super::*;
101
102    #[test]
103    fn test_invalid() {
104        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
105        d.push("resources/tests");
106        d.push("random");
107
108        // Feeding it invalid data should not panic
109        EXH::from_existing(&read(d).unwrap());
110    }
111}