From 8a3e8dad645ecfdcc19075c4249b1706d7f5e596 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 10 May 2025 23:51:22 -0400 Subject: [PATCH] Move EXD parsing functions into their own module It was getting hard to work in here, so I moved all of the private-API-parsing-things into their own file. --- src/exd.rs | 372 ++----------------------------------- src/exd_file_operations.rs | 348 ++++++++++++++++++++++++++++++++++ src/gamedata.rs | 2 +- src/lib.rs | 1 + src/patchlist.rs | 2 +- 5 files changed, 370 insertions(+), 355 deletions(-) create mode 100644 src/exd_file_operations.rs diff --git a/src/exd.rs b/src/exd.rs index 8a8bd02..93f8b29 100644 --- a/src/exd.rs +++ b/src/exd.rs @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later -use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}; +use std::io::{BufWriter, Cursor}; -use binrw::{BinRead, Endian}; -use binrw::{BinResult, BinWrite, binrw}; +use binrw::BinRead; +use binrw::{BinWrite, binrw}; use crate::common::Language; -use crate::exh::{ColumnDataType, EXH, ExcelColumnDefinition, ExcelDataPagination}; +use crate::exd_file_operations::{parse_rows, read_data_sections, write_rows}; +use crate::exh::{EXH, ExcelDataPagination}; use crate::{ByteBuffer, ByteSpan}; #[binrw] @@ -15,262 +16,37 @@ use crate::{ByteBuffer, ByteSpan}; #[brw(big)] #[allow(dead_code)] #[derive(Debug)] -struct EXDHeader { +pub(crate) struct EXDHeader { /// Usually 2, I don't think I've seen any other version - version: u16, + pub(crate) version: u16, /// Seems to be 0? - unk1: u16, + pub(crate) unk1: u16, /// Size of the data offsets in bytes - data_offset_size: u32, + pub(crate) data_offset_size: u32, #[brw(pad_after = 16)] // padding /// Size of the data sections in bytes - data_section_size: u32, + pub(crate) data_section_size: u32, } #[binrw] #[brw(big)] #[derive(Debug)] -struct ExcelDataOffset { +pub(crate) struct ExcelDataOffset { /// The row ID associated with this data offset - row_id: u32, + pub(crate) row_id: u32, /// Offset to it's data section in bytes from the start of the file. - pub offset: u32, -} - -#[binrw::parser(reader)] -fn read_data_sections(header: &EXDHeader) -> BinResult> { - 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)] -fn parse_rows(exh: &EXH, data_offsets: &Vec) -> BinResult> { - 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 { - let mut subrow = ExcelSingleRow { - columns: Vec::with_capacity(exh.column_definitions.len()), - }; - - for column in &exh.column_definitions { - reader - .seek(SeekFrom::Start((row_offset + column.offset as u32).into())) - .ok()?; - - subrow - .columns - .push(EXD::read_column(reader, exh, row_offset, column).unwrap()); - } - - Some(subrow) - }; - - let new_row = if row_count > 1 { - let mut rows = Vec::new(); - 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()); - } - ExcelRowKind::SubRows(rows) - } else { - ExcelRowKind::SingleRow(read_row(data_offset).unwrap()) - }; - rows.push(ExcelRow { - row_id: offset.row_id, - kind: new_row, - }); - } - - Ok(rows) -} - -#[binrw::writer(writer)] -fn write_rows(rows: &Vec, 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::() * 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 - .column_definitions - .clone() - .into_iter() - .zip(row.columns.clone().into_iter()) - .collect::>(); - - // we need to sort them by offset - column_definitions.sort_by(|(a, _), (b, _)| a.offset.cmp(&b.offset)); - - for (definition, column) in &column_definitions { - EXD::write_column(writer, &column, &definition); - - // 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 - { - 0u32.write_le(writer).unwrap(); - } - } - - // handle packed bools - let mut packed_byte = 0u8; - let mut byte_offset = 0; - - let mut write_packed_bool = - |definition: &ExcelColumnDefinition, shift: i32, boolean: &bool| { - byte_offset = definition.offset; // NOTE: it looks like there is only one byte for all of the packed booleans - - if *boolean { - let bit = 1 << shift; - packed_byte |= bit; - } - }; - - for (definition, column) in &column_definitions { - match &column { - ColumnData::Bool(val) => match definition.data_type { - ColumnDataType::PackedBool0 => write_packed_bool(definition, 0, val), - ColumnDataType::PackedBool1 => write_packed_bool(definition, 1, val), - ColumnDataType::PackedBool2 => write_packed_bool(definition, 2, val), - ColumnDataType::PackedBool3 => write_packed_bool(definition, 3, val), - ColumnDataType::PackedBool4 => write_packed_bool(definition, 4, val), - ColumnDataType::PackedBool5 => write_packed_bool(definition, 5, val), - ColumnDataType::PackedBool6 => write_packed_bool(definition, 6, val), - ColumnDataType::PackedBool7 => write_packed_bool(definition, 7, val), - _ => {} // not relevant - }, - _ => {} // not relevant - } - } - - // write the new packed boolean byte - // NOTE: This is a terrible way to check if there are packed booleans - // NOTE: Assumption: the packed boolean is always at the end of the row - if byte_offset != 0 { - packed_byte.write_le(writer).unwrap(); - } - }; - - match &row.kind { - ExcelRowKind::SingleRow(excel_single_row) => write_row(excel_single_row), - ExcelRowKind::SubRows(excel_single_rows) => { - for row in excel_single_rows { - write_row(row); - } - } - } - } - - // write strings at the end of column data - { - let mut write_row_strings = |row: &ExcelSingleRow| { - for column in &row.columns { - match column { - ColumnData::String(val) => { - let bytes = val.as_bytes(); - bytes.write(writer).unwrap(); - - // nul terminator - 0u8.write_le(writer).unwrap(); - } - _ => {} - } - } - }; - - match &row.kind { - ExcelRowKind::SingleRow(excel_single_row) => write_row_strings(excel_single_row), - ExcelRowKind::SubRows(excel_single_rows) => { - for row in excel_single_rows { - write_row_strings(row); - } - } - } - } - - // aligned to the next 4 byte boundary - let boundary_pos = writer.stream_position().unwrap(); - let remainder = (boundary_pos + 4 - 1) / 4 * 4; - for _ in 0..remainder - boundary_pos { - 0u8.write_le(writer).unwrap(); - } - - let new_pos = writer.stream_position().unwrap(); - - // write row header - writer.seek(SeekFrom::Start(row_header_pos)).unwrap(); - - let row_header = DataSection { - size: (new_pos - old_pos) as u32, - row_count: 1, // TODO: hardcoded - }; - row_header.write(writer).unwrap(); - - // restore pos - writer.seek(SeekFrom::Start(new_pos)).unwrap(); - } - - // now write the data offsets - writer.seek(SeekFrom::Start(data_offsets_pos)).unwrap(); - data_offsets.write(writer).unwrap(); - - Ok(()) + pub(crate) offset: u32, } #[binrw] #[brw(big)] #[allow(dead_code)] #[derive(Debug)] -struct DataSection { +pub(crate) struct DataSection { /// Size of the data section in bytes. - size: u32, + pub(crate) size: u32, /// The number of rows in this data section. - 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)] @@ -296,8 +72,8 @@ pub struct EXD { data: Vec, /// The rows contained in this EXD. - #[br(parse_with = parse_rows, args(&exh, &data_offsets))] - #[bw(write_with = write_rows, args(&exh))] + #[br(parse_with = parse_rows, args(exh, &data_offsets))] + #[bw(write_with = write_rows, args(exh))] pub rows: Vec, } @@ -440,117 +216,7 @@ impl EXD { } } - return None; - } - - fn read_data_raw = ()>>(cursor: &mut T) -> Option { - Z::read_options(cursor, Endian::Big, ()).ok() - } - - fn read_column( - cursor: &mut T, - exh: &EXH, - row_offset: u32, - column: &ExcelColumnDefinition, - ) -> Option { - let mut read_packed_bool = |shift: i32| -> bool { - let bit = 1 << shift; - let bool_data: u8 = Self::read_data_raw(cursor).unwrap_or(0); - - (bool_data & bit) == bit - }; - - match column.data_type { - ColumnDataType::String => { - let string_offset: u32 = Self::read_data_raw(cursor).unwrap(); - - cursor - .seek(SeekFrom::Start( - (row_offset + exh.header.data_offset as u32 + string_offset).into(), - )) - .ok()?; - - let mut string = String::new(); - - let mut byte: u8 = Self::read_data_raw(cursor).unwrap(); - while byte != 0 { - string.push(byte as char); - byte = Self::read_data_raw(cursor).unwrap(); - } - - Some(ColumnData::String(string)) - } - ColumnDataType::Bool => { - // FIXME: i believe Bool is int8? - let bool_data: i32 = Self::read_data_raw(cursor).unwrap(); - - Some(ColumnData::Bool(bool_data == 1)) - } - ColumnDataType::Int8 => Some(ColumnData::Int8(Self::read_data_raw(cursor).unwrap())), - ColumnDataType::UInt8 => Some(ColumnData::UInt8(Self::read_data_raw(cursor).unwrap())), - ColumnDataType::Int16 => Some(ColumnData::Int16(Self::read_data_raw(cursor).unwrap())), - ColumnDataType::UInt16 => { - Some(ColumnData::UInt16(Self::read_data_raw(cursor).unwrap())) - } - ColumnDataType::Int32 => Some(ColumnData::Int32(Self::read_data_raw(cursor).unwrap())), - ColumnDataType::UInt32 => { - Some(ColumnData::UInt32(Self::read_data_raw(cursor).unwrap())) - } - ColumnDataType::Float32 => { - Some(ColumnData::Float32(Self::read_data_raw(cursor).unwrap())) - } - ColumnDataType::Int64 => Some(ColumnData::Int64(Self::read_data_raw(cursor).unwrap())), - ColumnDataType::UInt64 => { - Some(ColumnData::UInt64(Self::read_data_raw(cursor).unwrap())) - } - ColumnDataType::PackedBool0 => Some(ColumnData::Bool(read_packed_bool(0))), - ColumnDataType::PackedBool1 => Some(ColumnData::Bool(read_packed_bool(1))), - ColumnDataType::PackedBool2 => Some(ColumnData::Bool(read_packed_bool(2))), - ColumnDataType::PackedBool3 => Some(ColumnData::Bool(read_packed_bool(3))), - ColumnDataType::PackedBool4 => Some(ColumnData::Bool(read_packed_bool(4))), - ColumnDataType::PackedBool5 => Some(ColumnData::Bool(read_packed_bool(5))), - ColumnDataType::PackedBool6 => Some(ColumnData::Bool(read_packed_bool(6))), - ColumnDataType::PackedBool7 => Some(ColumnData::Bool(read_packed_bool(7))), - } - } - - fn write_data_raw = ()>>(cursor: &mut T, value: &Z) { - value.write_options(cursor, Endian::Big, ()).unwrap() - } - - fn write_column( - cursor: &mut T, - column: &ColumnData, - column_definition: &ExcelColumnDefinition, - ) { - match column { - ColumnData::String(_) => { - let string_offset = 0u32; // TODO, but 0 is fine for single string column data - Self::write_data_raw(cursor, &string_offset); - } - ColumnData::Bool(_) => match column_definition.data_type { - ColumnDataType::Bool => todo!(), - // packed bools are handled in write_rows - ColumnDataType::PackedBool0 => {} - ColumnDataType::PackedBool1 => {} - ColumnDataType::PackedBool2 => {} - ColumnDataType::PackedBool3 => {} - ColumnDataType::PackedBool4 => {} - ColumnDataType::PackedBool5 => {} - ColumnDataType::PackedBool6 => {} - ColumnDataType::PackedBool7 => {} - _ => panic!("This makes no sense!"), - }, - ColumnData::Int8(val) => Self::write_data_raw(cursor, val), - ColumnData::UInt8(val) => Self::write_data_raw(cursor, val), - ColumnData::Int16(val) => Self::write_data_raw(cursor, val), - ColumnData::UInt16(val) => Self::write_data_raw(cursor, val), - ColumnData::Int32(val) => Self::write_data_raw(cursor, val), - ColumnData::UInt32(val) => Self::write_data_raw(cursor, val), - ColumnData::Float32(val) => Self::write_data_raw(cursor, val), - ColumnData::Int64(val) => Self::write_data_raw(cursor, val), - ColumnData::UInt64(val) => Self::write_data_raw(cursor, val), - } + None } /// Calculate the filename of an EXD from the `name`, `language`, and `page`. diff --git a/src/exd_file_operations.rs b/src/exd_file_operations.rs new file mode 100644 index 0000000..c3efb1d --- /dev/null +++ b/src/exd_file_operations.rs @@ -0,0 +1,348 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +use std::io::{Read, Seek, SeekFrom, Write}; + +use binrw::{BinRead, BinResult, BinWrite, Endian}; + +use crate::{ + exd::{ + ColumnData, DataSection, EXD, EXDHeader, ExcelDataOffset, ExcelRow, ExcelRowKind, + ExcelSingleRow, + }, + exh::{ColumnDataType, EXH, ExcelColumnDefinition}, +}; + +#[binrw::parser(reader)] +pub fn read_data_sections(header: &EXDHeader) -> BinResult> { + 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)] +pub fn parse_rows(exh: &EXH, data_offsets: &Vec) -> BinResult> { + 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 { + let mut subrow = ExcelSingleRow { + columns: Vec::with_capacity(exh.column_definitions.len()), + }; + + for column in &exh.column_definitions { + reader + .seek(SeekFrom::Start((row_offset + column.offset as u32).into())) + .ok()?; + + subrow + .columns + .push(EXD::read_column(reader, exh, row_offset, column).unwrap()); + } + + Some(subrow) + }; + + let new_row = if row_count > 1 { + let mut rows = Vec::new(); + 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()); + } + ExcelRowKind::SubRows(rows) + } else { + ExcelRowKind::SingleRow(read_row(data_offset).unwrap()) + }; + rows.push(ExcelRow { + row_id: offset.row_id, + kind: new_row, + }); + } + + Ok(rows) +} + +#[binrw::writer(writer)] +pub fn write_rows(rows: &Vec, 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::() * 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 + .column_definitions + .clone() + .into_iter() + .zip(row.columns.clone().into_iter()) + .collect::>(); + + // we need to sort them by offset + column_definitions.sort_by(|(a, _), (b, _)| a.offset.cmp(&b.offset)); + + for (definition, column) in &column_definitions { + EXD::write_column(writer, column, definition); + + // 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 + { + 0u32.write_le(writer).unwrap(); + } + } + + // handle packed bools + let mut packed_byte = 0u8; + let mut byte_offset = 0; + + let mut write_packed_bool = + |definition: &ExcelColumnDefinition, shift: i32, boolean: &bool| { + byte_offset = definition.offset; // NOTE: it looks like there is only one byte for all of the packed booleans + + if *boolean { + let bit = 1 << shift; + packed_byte |= bit; + } + }; + + for (definition, column) in &column_definitions { + match &column { + ColumnData::Bool(val) => match definition.data_type { + ColumnDataType::PackedBool0 => write_packed_bool(definition, 0, val), + ColumnDataType::PackedBool1 => write_packed_bool(definition, 1, val), + ColumnDataType::PackedBool2 => write_packed_bool(definition, 2, val), + ColumnDataType::PackedBool3 => write_packed_bool(definition, 3, val), + ColumnDataType::PackedBool4 => write_packed_bool(definition, 4, val), + ColumnDataType::PackedBool5 => write_packed_bool(definition, 5, val), + ColumnDataType::PackedBool6 => write_packed_bool(definition, 6, val), + ColumnDataType::PackedBool7 => write_packed_bool(definition, 7, val), + _ => {} // not relevant + }, + _ => {} // not relevant + } + } + + // write the new packed boolean byte + // NOTE: This is a terrible way to check if there are packed booleans + // NOTE: Assumption: the packed boolean is always at the end of the row + if byte_offset != 0 { + packed_byte.write_le(writer).unwrap(); + } + }; + + match &row.kind { + ExcelRowKind::SingleRow(excel_single_row) => write_row(excel_single_row), + ExcelRowKind::SubRows(excel_single_rows) => { + for row in excel_single_rows { + write_row(row); + } + } + } + } + + // write strings at the end of column data + { + let mut write_row_strings = |row: &ExcelSingleRow| { + for column in &row.columns { + if let ColumnData::String(val) = column { + let bytes = val.as_bytes(); + bytes.write(writer).unwrap(); + + // nul terminator + 0u8.write_le(writer).unwrap(); + } + } + }; + + match &row.kind { + ExcelRowKind::SingleRow(excel_single_row) => write_row_strings(excel_single_row), + ExcelRowKind::SubRows(excel_single_rows) => { + for row in excel_single_rows { + write_row_strings(row); + } + } + } + } + + // aligned to the next 4 byte boundary + let boundary_pos = writer.stream_position().unwrap(); + let remainder = boundary_pos.div_ceil(4) * 4; + for _ in 0..remainder - boundary_pos { + 0u8.write_le(writer).unwrap(); + } + + let new_pos = writer.stream_position().unwrap(); + + // write row header + writer.seek(SeekFrom::Start(row_header_pos)).unwrap(); + + let row_header = DataSection { + size: (new_pos - old_pos) as u32, + row_count: 1, // TODO: hardcoded + }; + row_header.write(writer).unwrap(); + + // restore pos + writer.seek(SeekFrom::Start(new_pos)).unwrap(); + } + + // now write the data offsets + writer.seek(SeekFrom::Start(data_offsets_pos)).unwrap(); + data_offsets.write(writer).unwrap(); + + Ok(()) +} + +impl EXD { + fn read_data_raw = ()>>(cursor: &mut T) -> Option { + Z::read_options(cursor, Endian::Big, ()).ok() + } + + pub(crate) fn read_column( + cursor: &mut T, + exh: &EXH, + row_offset: u32, + column: &ExcelColumnDefinition, + ) -> Option { + let mut read_packed_bool = |shift: i32| -> bool { + let bit = 1 << shift; + let bool_data: u8 = Self::read_data_raw(cursor).unwrap_or(0); + + (bool_data & bit) == bit + }; + + match column.data_type { + ColumnDataType::String => { + let string_offset: u32 = Self::read_data_raw(cursor).unwrap(); + + cursor + .seek(SeekFrom::Start( + (row_offset + exh.header.data_offset as u32 + string_offset).into(), + )) + .ok()?; + + let mut string = String::new(); + + let mut byte: u8 = Self::read_data_raw(cursor).unwrap(); + while byte != 0 { + string.push(byte as char); + byte = Self::read_data_raw(cursor).unwrap(); + } + + Some(ColumnData::String(string)) + } + ColumnDataType::Bool => { + // FIXME: i believe Bool is int8? + let bool_data: i32 = Self::read_data_raw(cursor).unwrap(); + + Some(ColumnData::Bool(bool_data == 1)) + } + ColumnDataType::Int8 => Some(ColumnData::Int8(Self::read_data_raw(cursor).unwrap())), + ColumnDataType::UInt8 => Some(ColumnData::UInt8(Self::read_data_raw(cursor).unwrap())), + ColumnDataType::Int16 => Some(ColumnData::Int16(Self::read_data_raw(cursor).unwrap())), + ColumnDataType::UInt16 => { + Some(ColumnData::UInt16(Self::read_data_raw(cursor).unwrap())) + } + ColumnDataType::Int32 => Some(ColumnData::Int32(Self::read_data_raw(cursor).unwrap())), + ColumnDataType::UInt32 => { + Some(ColumnData::UInt32(Self::read_data_raw(cursor).unwrap())) + } + ColumnDataType::Float32 => { + Some(ColumnData::Float32(Self::read_data_raw(cursor).unwrap())) + } + ColumnDataType::Int64 => Some(ColumnData::Int64(Self::read_data_raw(cursor).unwrap())), + ColumnDataType::UInt64 => { + Some(ColumnData::UInt64(Self::read_data_raw(cursor).unwrap())) + } + ColumnDataType::PackedBool0 => Some(ColumnData::Bool(read_packed_bool(0))), + ColumnDataType::PackedBool1 => Some(ColumnData::Bool(read_packed_bool(1))), + ColumnDataType::PackedBool2 => Some(ColumnData::Bool(read_packed_bool(2))), + ColumnDataType::PackedBool3 => Some(ColumnData::Bool(read_packed_bool(3))), + ColumnDataType::PackedBool4 => Some(ColumnData::Bool(read_packed_bool(4))), + ColumnDataType::PackedBool5 => Some(ColumnData::Bool(read_packed_bool(5))), + ColumnDataType::PackedBool6 => Some(ColumnData::Bool(read_packed_bool(6))), + ColumnDataType::PackedBool7 => Some(ColumnData::Bool(read_packed_bool(7))), + } + } + + fn write_data_raw = ()>>(cursor: &mut T, value: &Z) { + value.write_options(cursor, Endian::Big, ()).unwrap() + } + + pub(crate) fn write_column( + cursor: &mut T, + column: &ColumnData, + column_definition: &ExcelColumnDefinition, + ) { + match column { + ColumnData::String(_) => { + let string_offset = 0u32; // TODO, but 0 is fine for single string column data + Self::write_data_raw(cursor, &string_offset); + } + ColumnData::Bool(_) => match column_definition.data_type { + ColumnDataType::Bool => todo!(), + // packed bools are handled in write_rows + ColumnDataType::PackedBool0 => {} + ColumnDataType::PackedBool1 => {} + ColumnDataType::PackedBool2 => {} + ColumnDataType::PackedBool3 => {} + ColumnDataType::PackedBool4 => {} + ColumnDataType::PackedBool5 => {} + ColumnDataType::PackedBool6 => {} + ColumnDataType::PackedBool7 => {} + _ => panic!("This makes no sense!"), + }, + ColumnData::Int8(val) => Self::write_data_raw(cursor, val), + ColumnData::UInt8(val) => Self::write_data_raw(cursor, val), + ColumnData::Int16(val) => Self::write_data_raw(cursor, val), + ColumnData::UInt16(val) => Self::write_data_raw(cursor, val), + ColumnData::Int32(val) => Self::write_data_raw(cursor, val), + ColumnData::UInt32(val) => Self::write_data_raw(cursor, val), + ColumnData::Float32(val) => Self::write_data_raw(cursor, val), + ColumnData::Int64(val) => Self::write_data_raw(cursor, val), + ColumnData::UInt64(val) => Self::write_data_raw(cursor, val), + } + } +} diff --git a/src/gamedata.rs b/src/gamedata.rs index 7d989ea..f1d8e20 100755 --- a/src/gamedata.rs +++ b/src/gamedata.rs @@ -292,7 +292,7 @@ impl GameData { let exd_file = self.extract(&exd_path)?; - EXD::from_existing(&exh, &exd_file) + EXD::from_existing(exh, &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. diff --git a/src/lib.rs b/src/lib.rs index 05d2965..6181f9a 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ pub mod layer; pub mod tera; mod common_file_operations; +mod exd_file_operations; /// Reading word dictionaries, such as the vulgar word list. pub mod dic; diff --git a/src/patchlist.rs b/src/patchlist.rs index 0d33349..4dd8b7d 100644 --- a/src/patchlist.rs +++ b/src/patchlist.rs @@ -190,7 +190,7 @@ impl PatchList { size += patch.length; } - return size; + size } }