From d3918c13824f5cab6c7c41255437b69c4fb17ee2 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 15 Mar 2025 21:40:34 -0400 Subject: [PATCH] Actually use row ids in EXD files I'm an idiot, these row ids are here for a reason and are important for actually reading Excel sheets. The API has changed to a read_row() function that takes a row id, and I'll improve it before release. --- src/exd.rs | 81 ++++++++++++++++++++++++++----------------------- src/gamedata.rs | 2 +- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/exd.rs b/src/exd.rs index b5afc6b..5ce41b3 100644 --- a/src/exd.rs +++ b/src/exd.rs @@ -4,6 +4,7 @@ use std::io::{Cursor, Seek, SeekFrom}; use binrw::binrw; +use binrw::helpers::until_eof; use binrw::{BinRead, Endian}; use crate::ByteSpan; @@ -46,8 +47,8 @@ pub struct EXD { #[br(count = header.index_size / core::mem::size_of::() as u32)] data_offsets: Vec, - #[brw(ignore)] - pub rows: Vec, + #[br(seek_before = SeekFrom::Start(0), parse_with = until_eof)] + data: Vec, } #[derive(Debug)] @@ -65,65 +66,69 @@ pub enum ColumnData { UInt64(u64), } +#[derive(Debug)] pub struct ExcelRow { pub data: Vec, } impl EXD { - pub fn from_existing(exh: &EXH, buffer: ByteSpan) -> Option { - let mut cursor = Cursor::new(buffer); - let mut exd = EXD::read(&mut cursor).ok()?; + pub fn from_existing(buffer: ByteSpan) -> Option { + EXD::read(&mut Cursor::new(&buffer)).ok() + } - for i in 0..exh.header.row_count { - for offset in &exd.data_offsets { - if offset.row_id == i { - cursor.seek(SeekFrom::Start(offset.offset.into())).ok()?; + pub fn read_row(&self, exh: &EXH, id: u32) -> Option> { + let mut cursor = Cursor::new(&self.data); - let row_header = ExcelDataRowHeader::read(&mut cursor).ok()?; + for offset in &self.data_offsets { + if offset.row_id == id { + cursor.seek(SeekFrom::Start(offset.offset.into())).ok()?; - let header_offset = offset.offset + 6; // std::mem::size_of::() as u32; + let row_header = ExcelDataRowHeader::read(&mut cursor).ok()?; - let mut read_row = |row_offset: u32| -> Option { - let mut subrow = ExcelRow { - data: Vec::with_capacity(exh.column_definitions.len()), - }; + let header_offset = offset.offset + 6; // std::mem::size_of::() as u32; - for column in &exh.column_definitions { - cursor - .seek(SeekFrom::Start((row_offset + column.offset as u32).into())) - .ok()?; - - subrow.data.push( - Self::read_column(&mut cursor, exh, row_offset, column).unwrap(), - ); - } - - Some(subrow) + let mut read_row = |row_offset: u32| -> Option { + let mut subrow = ExcelRow { + data: Vec::with_capacity(exh.column_definitions.len()), }; - if row_header.row_count > 1 { - for i in 0..row_header.row_count { - let subrow_offset = - header_offset + (i * exh.header.data_offset + 2 * (i + 1)) as u32; + for column in &exh.column_definitions { + cursor + .seek(SeekFrom::Start((row_offset + column.offset as u32).into())) + .ok()?; - exd.rows.push(read_row(subrow_offset).unwrap()); - } - } else { - exd.rows.push(read_row(header_offset).unwrap()); + subrow + .data + .push(Self::read_column(&mut cursor, exh, row_offset, column).unwrap()); } - } + + Some(subrow) + }; + + return if row_header.row_count > 1 { + let mut rows = Vec::new(); + for i in 0..row_header.row_count { + let subrow_offset = + header_offset + (i * exh.header.data_offset + 2 * (i + 1)) as u32; + + rows.push(read_row(subrow_offset).unwrap()); + } + Some(rows) + } else { + Some(vec![read_row(header_offset).unwrap()]) + }; } } - Some(exd) + None } - fn read_data_raw = ()>>(cursor: &mut Cursor) -> Option { + fn read_data_raw = ()>>(cursor: &mut Cursor<&Vec>) -> Option { Z::read_options(cursor, Endian::Big, ()).ok() } fn read_column( - cursor: &mut Cursor, + cursor: &mut Cursor<&Vec>, exh: &EXH, row_offset: u32, column: &ExcelColumnDefinition, diff --git a/src/gamedata.rs b/src/gamedata.rs index db1b4fe..0976a21 100755 --- a/src/gamedata.rs +++ b/src/gamedata.rs @@ -291,7 +291,7 @@ impl GameData { let exd_file = self.extract(&exd_path)?; - EXD::from_existing(exh, &exd_file) + EXD::from_existing(&exd_file) } /// Applies the patch to game data and returns any errors it encounters. This function will not update the version in the GameData struct.