1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-05-16 23:17:45 +00:00
physis/src/exh.rs

185 lines
4.2 KiB
Rust
Raw Normal View History

// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
2024-04-20 13:17:11 -04:00
#![allow(clippy::unnecessary_fallible_conversions)] // This wrongly trips on binrw code
use std::io::BufWriter;
use std::io::Cursor;
2024-04-20 13:18:03 -04:00
use binrw::BinRead;
use binrw::BinWrite;
use binrw::binrw;
use crate::ByteBuffer;
use crate::ByteSpan;
use crate::common::Language;
2022-07-21 19:58:58 -04:00
2023-08-02 16:27:28 -04:00
#[binrw]
#[brw(magic = b"EXHF")]
#[brw(big)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[derive(Debug)]
2022-07-21 19:58:58 -04:00
pub struct EXHHeader {
2024-04-16 21:52:14 -04:00
pub(crate) version: u16,
2022-07-21 19:58:58 -04:00
2023-08-02 16:27:28 -04:00
pub data_offset: u16,
2024-04-16 21:52:14 -04:00
pub(crate) column_count: u16,
pub(crate) page_count: u16,
pub(crate) language_count: u16,
2022-07-21 19:58:58 -04:00
pub unk1: u16,
#[br(temp)]
#[bw(calc = 0x010000)] // always this value??
pub unk2: u32,
#[brw(pad_after = 8)] // padding
2023-04-09 15:35:10 -04:00
pub row_count: u32,
2022-07-21 19:58:58 -04:00
}
2023-08-02 16:27:28 -04:00
#[binrw]
#[brw(repr(u16))]
2025-03-23 19:41:15 -04:00
#[repr(u16)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
2022-07-21 19:58:58 -04:00
pub enum ColumnDataType {
String = 0x0,
Bool = 0x1,
Int8 = 0x2,
UInt8 = 0x3,
Int16 = 0x4,
UInt16 = 0x5,
Int32 = 0x6,
UInt32 = 0x7,
Float32 = 0x9,
Int64 = 0xA,
UInt64 = 0xB,
PackedBool0 = 0x19,
PackedBool1 = 0x1A,
PackedBool2 = 0x1B,
PackedBool3 = 0x1C,
PackedBool4 = 0x1D,
PackedBool5 = 0x1E,
PackedBool6 = 0x1F,
PackedBool7 = 0x20,
}
2023-08-02 16:27:28 -04:00
#[binrw]
#[brw(big)]
#[derive(Debug, Copy, Clone)]
2022-07-21 19:58:58 -04:00
pub struct ExcelColumnDefinition {
2022-08-16 11:52:07 -04:00
pub data_type: ColumnDataType,
pub offset: u16,
2022-07-21 19:58:58 -04:00
}
2023-08-02 16:27:28 -04:00
#[binrw]
#[brw(big)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[derive(Debug)]
2022-07-21 19:58:58 -04:00
pub struct ExcelDataPagination {
2022-08-16 11:52:07 -04:00
pub start_id: u32,
2023-04-09 15:35:10 -04:00
pub row_count: u32,
2022-07-21 19:58:58 -04:00
}
2023-08-02 16:27:28 -04:00
#[binrw]
#[brw(big)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[derive(Debug)]
2022-07-21 19:58:58 -04:00
pub struct EXH {
2022-08-16 11:52:07 -04:00
pub header: EXHHeader,
2022-07-21 19:58:58 -04:00
#[br(count = header.column_count)]
2022-08-16 11:52:07 -04:00
pub column_definitions: Vec<ExcelColumnDefinition>,
2022-07-21 19:58:58 -04:00
#[br(count = header.page_count)]
2022-08-16 11:52:07 -04:00
pub pages: Vec<ExcelDataPagination>,
2022-07-21 19:58:58 -04:00
#[br(count = header.language_count)]
#[brw(pad_after = 1)] // \0
2023-04-09 15:35:10 -04:00
pub languages: Vec<Language>,
2022-07-21 19:58:58 -04:00
}
impl EXH {
pub fn from_existing(buffer: ByteSpan) -> Option<EXH> {
2022-08-16 11:50:18 -04:00
EXH::read(&mut Cursor::new(&buffer)).ok()
2022-07-21 19:58:58 -04:00
}
pub fn write_to_buffer(&self) -> Option<ByteBuffer> {
let mut buffer = ByteBuffer::new();
{
let cursor = Cursor::new(&mut buffer);
let mut writer = BufWriter::new(cursor);
self.write_args(&mut writer, ()).unwrap();
}
Some(buffer)
}
2022-08-16 11:52:07 -04:00
}
2024-04-16 21:52:36 -04:00
#[cfg(test)]
mod tests {
use std::fs::read;
use std::path::PathBuf;
use super::*;
#[test]
fn test_invalid() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests");
d.push("random");
// Feeding it invalid data should not panic
EXH::from_existing(&read(d).unwrap());
}
// simple EXH to read, just one page
#[test]
fn test_read() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests");
d.push("gcshop.exh");
let exh = EXH::from_existing(&read(d).unwrap()).unwrap();
// header
assert_eq!(exh.header.version, 3);
assert_eq!(exh.header.data_offset, 4);
assert_eq!(exh.header.column_count, 1);
assert_eq!(exh.header.page_count, 1);
assert_eq!(exh.header.language_count, 1);
assert_eq!(exh.header.row_count, 4);
// column definitions
assert_eq!(exh.column_definitions.len(), 1);
assert_eq!(exh.column_definitions[0].data_type, ColumnDataType::Int8);
assert_eq!(exh.column_definitions[0].offset, 0);
// pages
assert_eq!(exh.pages.len(), 1);
assert_eq!(exh.pages[0].start_id, 1441792);
assert_eq!(exh.pages[0].row_count, 4);
// languages
assert_eq!(exh.languages.len(), 1);
assert_eq!(exh.languages[0], Language::None);
}
// simple EXH to write, only one page
#[test]
fn test_write() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests");
d.push("gcshop.exh");
let expected_exh_bytes = read(d).unwrap();
let expected_exh = EXH::from_existing(&expected_exh_bytes).unwrap();
let actual_exh_bytes = expected_exh.write_to_buffer().unwrap();
assert_eq!(actual_exh_bytes, expected_exh_bytes);
}
2024-04-16 21:52:36 -04:00
}