mirror of
https://github.com/redstrate/Physis.git
synced 2025-05-15 22:47:46 +00:00
Finish basic EXD write support, add tests for GCShop
Expanding upon the EXH tests of this same data, we can successfully write it back (hence why it's testdata now!) A lot of the writing logic is specific for this EXD, but it's a start.
This commit is contained in:
parent
497549e0c6
commit
36304a2e57
3 changed files with 154 additions and 21 deletions
BIN
resources/tests/gcshop_1441792.exd
Normal file
BIN
resources/tests/gcshop_1441792.exd
Normal file
Binary file not shown.
172
src/exd.rs
172
src/exd.rs
|
@ -17,11 +17,10 @@ use crate::{ByteBuffer, ByteSpan};
|
|||
#[derive(Debug)]
|
||||
struct EXDHeader {
|
||||
version: u16,
|
||||
|
||||
#[brw(pad_before = 2)] // empty
|
||||
unk1: u16,
|
||||
/// Size of the ExcelDataOffset array
|
||||
index_size: u32,
|
||||
#[brw(pad_after = 16)] // empty
|
||||
#[brw(pad_after = 16)] // padding
|
||||
/// Total size of the string data?
|
||||
data_section_size: u32,
|
||||
}
|
||||
|
@ -31,15 +30,27 @@ struct EXDHeader {
|
|||
#[derive(Debug)]
|
||||
struct ExcelDataOffset {
|
||||
row_id: u32,
|
||||
pub offset: u32,
|
||||
pub offset: u32, // offset to it's data section in bytes from the start of the file
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(big)]
|
||||
#[allow(dead_code)]
|
||||
struct ExcelDataRowHeader {
|
||||
data_size: u32,
|
||||
row_count: u16,
|
||||
#[binrw::parser(reader)]
|
||||
fn read_data_sections(header: &EXDHeader) -> BinResult<Vec<DataSection>> {
|
||||
let mut rows = Vec::new();
|
||||
|
||||
// we have to do this annoying thing because they specified it in bytes,
|
||||
// not an actual count of data sections
|
||||
let begin_pos = reader.stream_position().unwrap();
|
||||
loop {
|
||||
let current_pos = reader.stream_position().unwrap();
|
||||
if current_pos - begin_pos >= header.data_section_size as u64 {
|
||||
break;
|
||||
}
|
||||
|
||||
let data_section = DataSection::read_be(reader).unwrap();
|
||||
rows.push(data_section);
|
||||
}
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
#[binrw::parser(reader)]
|
||||
|
@ -49,7 +60,10 @@ fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<Vec<E
|
|||
for offset in data_offsets {
|
||||
reader.seek(SeekFrom::Start(offset.offset.into()))?;
|
||||
|
||||
let row_header = ExcelDataRowHeader::read(reader)?;
|
||||
// TODO: use DataSection here
|
||||
let size: u32 = u32::read_be(reader).unwrap();
|
||||
let row_count: u16 = u16::read_be(reader).unwrap();
|
||||
//let row_header = DataSection::read(reader)?;
|
||||
|
||||
let data_offset = reader.stream_position().unwrap() as u32;
|
||||
|
||||
|
@ -71,9 +85,9 @@ fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<Vec<E
|
|||
Some(subrow)
|
||||
};
|
||||
|
||||
let new_row = if row_header.row_count > 1 {
|
||||
let new_row = if row_count > 1 {
|
||||
let mut rows = Vec::new();
|
||||
for i in 0..row_header.row_count {
|
||||
for i in 0..row_count {
|
||||
let subrow_offset = data_offset + (i * exh.header.data_offset + 2 * (i + 1)) as u32;
|
||||
|
||||
rows.push(read_row(subrow_offset).unwrap());
|
||||
|
@ -109,9 +123,10 @@ fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
|
|||
offset: writer.stream_position().unwrap() as u32,
|
||||
});
|
||||
|
||||
let row_header = ExcelDataRowHeader {
|
||||
data_size: 0,
|
||||
row_count: 0,
|
||||
let row_header = DataSection {
|
||||
size: 6, // TODO: hardcoded
|
||||
row_count: 1, // TODO: hardcoded
|
||||
data: Vec::new(), // TOOD: not used here
|
||||
};
|
||||
row_header.write(writer).unwrap();
|
||||
|
||||
|
@ -131,6 +146,11 @@ fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: temporary padding for now
|
||||
for _ in 0..4 {
|
||||
0u8.write_le(writer).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// write strings at the end of column data
|
||||
|
@ -168,24 +188,44 @@ fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(big)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct DataSection {
|
||||
#[br(dbg)]
|
||||
size: u32,
|
||||
row_count: u16,
|
||||
#[br(count = size)]
|
||||
#[bw(ignore)]
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(big)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[brw(import(exh: &EXH))]
|
||||
pub struct EXD {
|
||||
#[br(dbg)]
|
||||
header: EXDHeader,
|
||||
|
||||
#[br(count = header.index_size / core::mem::size_of::<ExcelDataOffset>() as u32)]
|
||||
#[bw(ignore)]
|
||||
#[bw(ignore)] // write_rows handles writing this
|
||||
#[br(dbg)]
|
||||
data_offsets: Vec<ExcelDataOffset>,
|
||||
|
||||
#[br(dbg)]
|
||||
#[br(parse_with = read_data_sections, args(&header))]
|
||||
#[bw(ignore)] // write_rows handles writing this
|
||||
data: Vec<DataSection>,
|
||||
|
||||
#[br(parse_with = parse_rows, args(&exh, &data_offsets))]
|
||||
#[bw(write_with = write_rows, args(&exh))]
|
||||
pub rows: Vec<ExcelRow>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ColumnData {
|
||||
String(String),
|
||||
Bool(bool),
|
||||
|
@ -290,12 +330,12 @@ impl ColumnData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExcelSingleRow {
|
||||
pub columns: Vec<ColumnData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExcelRowKind {
|
||||
SingleRow(ExcelSingleRow),
|
||||
SubRows(Vec<ExcelSingleRow>),
|
||||
|
@ -499,4 +539,96 @@ mod tests {
|
|||
// Feeding it invalid data should not panic
|
||||
EXD::from_existing(&exh, &read(d).unwrap());
|
||||
}
|
||||
|
||||
// super simple EXD to read, it's just a few rows of only int8's
|
||||
#[test]
|
||||
fn test_read() {
|
||||
// exh
|
||||
let exh;
|
||||
{
|
||||
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
d.push("resources/tests");
|
||||
d.push("gcshop.exh");
|
||||
|
||||
exh = EXH::from_existing(&read(d).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// exd
|
||||
let exd;
|
||||
{
|
||||
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
d.push("resources/tests");
|
||||
d.push("gcshop_1441792.exd");
|
||||
|
||||
exd = EXD::from_existing(&exh, &read(d).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(exd.rows.len(), 4);
|
||||
|
||||
// row 0
|
||||
assert_eq!(exd.rows[0].row_id, 1441792);
|
||||
assert_eq!(
|
||||
exd.rows[0].kind,
|
||||
ExcelRowKind::SingleRow(ExcelSingleRow {
|
||||
columns: vec![ColumnData::Int8(0)]
|
||||
})
|
||||
);
|
||||
|
||||
// row 1
|
||||
assert_eq!(exd.rows[1].row_id, 1441793);
|
||||
assert_eq!(
|
||||
exd.rows[1].kind,
|
||||
ExcelRowKind::SingleRow(ExcelSingleRow {
|
||||
columns: vec![ColumnData::Int8(1)]
|
||||
})
|
||||
);
|
||||
|
||||
// row 2
|
||||
assert_eq!(exd.rows[2].row_id, 1441794);
|
||||
assert_eq!(
|
||||
exd.rows[2].kind,
|
||||
ExcelRowKind::SingleRow(ExcelSingleRow {
|
||||
columns: vec![ColumnData::Int8(2)]
|
||||
})
|
||||
);
|
||||
|
||||
// row 3
|
||||
assert_eq!(exd.rows[3].row_id, 1441795);
|
||||
assert_eq!(
|
||||
exd.rows[3].kind,
|
||||
ExcelRowKind::SingleRow(ExcelSingleRow {
|
||||
columns: vec![ColumnData::Int8(3)]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// super simple EXD to write, it's just a few rows of only int8's
|
||||
#[test]
|
||||
fn test_write() {
|
||||
// exh
|
||||
let exh;
|
||||
{
|
||||
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
d.push("resources/tests");
|
||||
d.push("gcshop.exh");
|
||||
|
||||
exh = EXH::from_existing(&read(d).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// exd
|
||||
let expected_exd_bytes;
|
||||
let expected_exd;
|
||||
{
|
||||
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
d.push("resources/tests");
|
||||
d.push("gcshop_1441792.exd");
|
||||
|
||||
expected_exd_bytes = read(d).unwrap();
|
||||
expected_exd = EXD::from_existing(&exh, &expected_exd_bytes).unwrap();
|
||||
}
|
||||
|
||||
let actual_exd_bytes = expected_exd.write_to_buffer(&exh).unwrap();
|
||||
|
||||
assert_eq!(actual_exd_bytes, expected_exd_bytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,12 @@ use crate::common::Language;
|
|||
pub struct EXHHeader {
|
||||
pub(crate) version: u16,
|
||||
|
||||
pub data_offset: u16,
|
||||
pub data_offset: u16, // TODO: might not be an offset
|
||||
pub(crate) column_count: u16,
|
||||
pub(crate) page_count: u16,
|
||||
pub(crate) language_count: u16,
|
||||
|
||||
#[br(dbg)]
|
||||
pub unk1: u16,
|
||||
|
||||
#[br(temp)]
|
||||
|
|
Loading…
Add table
Reference in a new issue