1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-07-01 08:37:46 +00:00

Add support for reading subrows and their ids

This commit is contained in:
Joshua Goins 2025-06-30 19:59:25 -04:00
parent 029b1fb3f5
commit 6ebb7acfcd
2 changed files with 127 additions and 111 deletions

View file

@ -44,14 +44,30 @@ pub(crate) struct ExcelDataOffset {
#[brw(big)] #[brw(big)]
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct DataSection { pub(crate) struct DataSectionHeader {
/// Size of the data section in bytes. /// Size of the data section in bytes.
pub(crate) size: u32, pub(crate) size: u32,
/// The number of rows in this data section. /// The number of rows in this data section.
pub(crate) row_count: u16, pub(crate) row_count: u16,
/// The bytes of this data section. }
/// We currently don't use this in our parsing, see parse_rows.
#[br(temp, count = size)] #[binrw]
#[brw(big)]
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct SubRowHeader {
pub(crate) subrow_id: u16,
}
#[binrw]
#[brw(big)]
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct DataSection {
/// Header of the section.
pub(crate) header: DataSectionHeader,
/// Data part of this section.
#[br(temp, count = header.size)]
#[bw(ignore)] #[bw(ignore)]
data: Vec<u8>, data: Vec<u8>,
} }
@ -192,7 +208,7 @@ pub struct ExcelSingleRow {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ExcelRowKind { pub enum ExcelRowKind {
SingleRow(ExcelSingleRow), SingleRow(ExcelSingleRow),
SubRows(Vec<ExcelSingleRow>), SubRows(Vec<(u16, ExcelSingleRow)>),
} }
/// Represents an entry in the EXD. /// Represents an entry in the EXD.

View file

@ -10,8 +10,8 @@ use binrw::{BinRead, BinResult, BinWrite, Endian};
use crate::{ use crate::{
exd::{ exd::{
ColumnData, DataSection, EXD, EXDHeader, ExcelDataOffset, ExcelRow, ExcelRowKind, ColumnData, DataSection, DataSectionHeader, EXD, EXDHeader, ExcelDataOffset, ExcelRow,
ExcelSingleRow, ExcelRowKind, ExcelSingleRow, SubRowHeader,
}, },
exh::{ColumnDataType, EXH, ExcelColumnDefinition}, exh::{ColumnDataType, EXH, ExcelColumnDefinition},
}; };
@ -36,21 +36,7 @@ pub fn read_data_sections(header: &EXDHeader) -> BinResult<Vec<DataSection>> {
Ok(rows) Ok(rows)
} }
#[binrw::parser(reader)] fn read_row<T: Read + Seek>(reader: &mut T, exh: &EXH, row_offset: u32) -> Option<ExcelSingleRow> {
pub fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<Vec<ExcelRow>> {
let mut rows = Vec::new();
for offset in data_offsets {
reader.seek(SeekFrom::Start(offset.offset.into()))?;
// 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;
let mut read_row = |row_offset: u32| -> Option<ExcelSingleRow> {
let mut subrow = ExcelSingleRow { let mut subrow = ExcelSingleRow {
columns: Vec::with_capacity(exh.column_definitions.len()), columns: Vec::with_capacity(exh.column_definitions.len()),
}; };
@ -66,18 +52,33 @@ pub fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<V
} }
Some(subrow) Some(subrow)
}; }
let new_row = if row_count > 1 { #[binrw::parser(reader)]
pub fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<Vec<ExcelRow>> {
let mut rows = Vec::new(); let mut rows = Vec::new();
for i in 0..row_count {
for offset in data_offsets {
reader.seek(SeekFrom::Start(offset.offset.into()))?;
let row_header = DataSectionHeader::read(reader)?;
let data_offset = reader.stream_position().unwrap() as u32;
let new_row = if row_header.row_count > 1 {
let mut rows = Vec::new();
for i in 0..row_header.row_count {
let subrow_offset = data_offset + (i * exh.header.data_offset + 2 * (i + 1)) as u32; let subrow_offset = data_offset + (i * exh.header.data_offset + 2 * (i + 1)) as u32;
rows.push(read_row(subrow_offset).unwrap()); let subrow_header = SubRowHeader::read(reader)?;
rows.push((
subrow_header.subrow_id,
read_row(reader, &exh, subrow_offset).unwrap(),
));
} }
ExcelRowKind::SubRows(rows) ExcelRowKind::SubRows(rows)
} else { } else {
ExcelRowKind::SingleRow(read_row(data_offset).unwrap()) ExcelRowKind::SingleRow(read_row(reader, &exh, data_offset).unwrap())
}; };
rows.push(ExcelRow { rows.push(ExcelRow {
row_id: offset.row_id, row_id: offset.row_id,
@ -88,34 +89,7 @@ pub fn parse_rows(exh: &EXH, data_offsets: &Vec<ExcelDataOffset>) -> BinResult<V
Ok(rows) Ok(rows)
} }
#[binrw::writer(writer)] fn write_row<T: Write + Seek>(writer: &mut T, exh: &EXH, row: &ExcelSingleRow) {
pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
// seek past the data offsets, which we will write later
let data_offsets_pos = writer.stream_position().unwrap();
writer
.seek(SeekFrom::Current(
(core::mem::size_of::<ExcelDataOffset>() * rows.len()) as i64,
))
.unwrap();
let mut data_offsets = Vec::new();
for row in rows {
data_offsets.push(ExcelDataOffset {
row_id: row.row_id,
offset: writer.stream_position().unwrap() as u32,
});
// skip row header for now, because we don't know the size yet!
let row_header_pos = writer.stream_position().unwrap();
writer.seek(SeekFrom::Current(6)).unwrap(); // u32 + u16
let old_pos = writer.stream_position().unwrap();
// write column data
{
let mut write_row = |row: &ExcelSingleRow| {
let mut column_definitions: Vec<(ExcelColumnDefinition, ColumnData)> = exh let mut column_definitions: Vec<(ExcelColumnDefinition, ColumnData)> = exh
.column_definitions .column_definitions
.clone() .clone()
@ -129,8 +103,7 @@ pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
// handle packed bools // handle packed bools
let mut packed_bools: HashMap<u16, u8> = HashMap::new(); let mut packed_bools: HashMap<u16, u8> = HashMap::new();
let mut write_packed_bool = let mut write_packed_bool = |definition: &ExcelColumnDefinition, shift: i32, boolean: &bool| {
|definition: &ExcelColumnDefinition, shift: i32, boolean: &bool| {
if !packed_bools.contains_key(&definition.offset) { if !packed_bools.contains_key(&definition.offset) {
packed_bools.insert(definition.offset, 0u8); packed_bools.insert(definition.offset, 0u8);
} }
@ -170,8 +143,7 @@ pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
); );
// TODO: temporary workaround until i can figure out why it has 4 extra bytes in test_write's case // TODO: temporary workaround until i can figure out why it has 4 extra bytes in test_write's case
if definition.data_type == ColumnDataType::Int8 && column_definitions.len() == 1 if definition.data_type == ColumnDataType::Int8 && column_definitions.len() == 1 {
{
0u32.write_le(writer).unwrap(); 0u32.write_le(writer).unwrap();
} }
@ -183,14 +155,42 @@ pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
[0u8; 3].write_le(writer).unwrap(); [0u8; 3].write_le(writer).unwrap();
} }
} }
}; }
#[binrw::writer(writer)]
pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
// seek past the data offsets, which we will write later
let data_offsets_pos = writer.stream_position().unwrap();
writer
.seek(SeekFrom::Current(
(core::mem::size_of::<ExcelDataOffset>() * rows.len()) as i64,
))
.unwrap();
let mut data_offsets = Vec::new();
for row in rows {
data_offsets.push(ExcelDataOffset {
row_id: row.row_id,
offset: writer.stream_position().unwrap() as u32,
});
// skip row header for now, because we don't know the size yet!
let row_header_pos = writer.stream_position().unwrap();
writer.seek(SeekFrom::Current(6)).unwrap(); // u32 + u16
let old_pos = writer.stream_position().unwrap();
// write column data
match &row.kind { match &row.kind {
ExcelRowKind::SingleRow(excel_single_row) => write_row(excel_single_row), ExcelRowKind::SingleRow(excel_single_row) => write_row(writer, &exh, excel_single_row),
ExcelRowKind::SubRows(excel_single_rows) => { ExcelRowKind::SubRows(excel_single_rows) => {
for row in excel_single_rows { for (id, row) in excel_single_rows {
write_row(row); let subrow_header = SubRowHeader { subrow_id: *id };
} subrow_header.write_ne(writer)?;
write_row(writer, &exh, row);
} }
} }
} }
@ -212,7 +212,7 @@ pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
match &row.kind { match &row.kind {
ExcelRowKind::SingleRow(excel_single_row) => write_row_strings(excel_single_row), ExcelRowKind::SingleRow(excel_single_row) => write_row_strings(excel_single_row),
ExcelRowKind::SubRows(excel_single_rows) => { ExcelRowKind::SubRows(excel_single_rows) => {
for row in excel_single_rows { for (_, row) in excel_single_rows {
write_row_strings(row); write_row_strings(row);
} }
} }
@ -231,7 +231,7 @@ pub fn write_rows(rows: &Vec<ExcelRow>, exh: &EXH) -> BinResult<()> {
// write row header // write row header
writer.seek(SeekFrom::Start(row_header_pos)).unwrap(); writer.seek(SeekFrom::Start(row_header_pos)).unwrap();
let row_header = DataSection { let row_header = DataSectionHeader {
size: (new_pos - old_pos) as u32, size: (new_pos - old_pos) as u32,
row_count: 1, // TODO: hardcoded row_count: 1, // TODO: hardcoded
}; };