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