From fe6712b5bf8d22661438f17a2ec837d8a0ad9173 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 6 Mar 2025 19:09:25 -0500 Subject: [PATCH] Prepare for file entry writing, add test for index1 entries --- Cargo.toml | 3 -- src/index.rs | 101 ++++++++++++++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 07e55ed..1c00293 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,9 +62,6 @@ tracing = { version = "0.1", features = ["std"], default-features = false } # used for zlib compression in sqpack files libz-sys = { version = "1.1" } -# nice to have features rust is lacking at the moment -modular-bitfield = "0.11" - # needed for half-float support which FFXIV uses in its model data half = { version = "2", optional = true } diff --git a/src/index.rs b/src/index.rs index 6eb1521..ffd6b8f 100755 --- a/src/index.rs +++ b/src/index.rs @@ -4,12 +4,16 @@ #![allow(clippy::identity_op)] #![allow(unused_variables)] // for br(temp), meh +use std::io::Read; +use std::io::Seek; use std::io::SeekFrom; use crate::common::Platform; use crate::common::Region; use crate::crc::Jamcrc; use binrw::BinRead; +use binrw::BinResult; +use binrw::Endian; use binrw::binrw; /// The type of this SqPack file. @@ -93,7 +97,7 @@ pub struct SqPackIndexHeader { #[binrw] #[br(import(index_type: &IndexType))] -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] pub enum Hash { #[br(pre_assert(*index_type == IndexType::Index1))] SplitPath { name: u32, path: u32 }, @@ -101,57 +105,42 @@ pub enum Hash { FullPath(u32), } +pub struct FileEntryData { + pub is_synonym: bool, + pub data_file_id: u8, + pub offset: u64, +} + +impl BinRead for FileEntryData { + type Args<'a> = (); + + fn read_options( + reader: &mut R, + _options: Endian, + (): Self::Args<'_>, + ) -> BinResult { + let data = ::read(reader)?; + Ok(Self { + is_synonym: (data & 0b1) == 0b1, + data_file_id: (data & 0b1110) >> 1 as u8, + offset: (data & !0xF) as u64 * 0x08, + }) + } +} + #[binrw] #[br(import(index_type: &IndexType))] -pub struct IndexHashTableEntry { +pub struct FileEntry { #[br(args(index_type))] pub hash: Hash, - #[br(temp)] #[bw(ignore)] - data: u32, + pub data: FileEntryData, #[br(temp)] #[bw(calc = 0)] #[br(if(*index_type == IndexType::Index1))] padding: u32, - - #[br(calc = (data & 0b1) == 0b1)] - #[bw(ignore)] - pub is_synonym: bool, - - #[br(calc = ((data & 0b1110) >> 1) as u8)] - #[bw(ignore)] - pub data_file_id: u8, - - #[br(calc = (data & !0xF) as u64 * 0x08)] - #[bw(ignore)] - pub offset: u64, -} - -// The only difference between index and index2 is how the path hash is stored. -// The folder name and the filename are split in index1 (hence why it's 64-bits and not 32-bit) -// But in index2, its both the file and folder name in one single CRC hash. -#[binrw] -#[derive(Debug)] -pub struct Index2HashTableEntry { - pub hash: u32, - - #[br(temp)] - #[bw(ignore)] - data: u32, - - #[br(calc = (data & 0b1) == 0b1)] - #[bw(ignore)] - pub is_synonym: bool, - - #[br(calc = ((data & 0b1110) >> 1) as u8)] - #[bw(ignore)] - pub data_file_id: u8, - - #[br(calc = (data & !0xF) as u64 * 0x08)] - #[bw(ignore)] - pub offset: u64, } #[binrw] @@ -187,7 +176,7 @@ pub struct IndexFile { index_header: SqPackIndexHeader, #[br(seek_before = SeekFrom::Start(index_header.file_descriptor.offset.into()), count = index_header.file_descriptor.size / 16, args { inner: (&index_header.index_type,) })] - pub entries: Vec, + pub entries: Vec, #[br(seek_before = SeekFrom::Start(index_header.data_descriptor.offset.into()))] #[br(count = index_header.data_descriptor.size / 256)] @@ -260,8 +249,8 @@ impl IndexFile { }; return Some(IndexEntry { hash: 0, - data_file_id: entry.data_file_id, - offset: entry.offset, + data_file_id: entry.data.data_file_id, + offset: entry.data.offset, }); } @@ -271,7 +260,7 @@ impl IndexFile { #[cfg(test)] mod tests { - use std::path::PathBuf; + use std::{io::Cursor, path::PathBuf}; use super::*; @@ -284,4 +273,26 @@ mod tests { // Feeding it invalid data should not panic IndexFile::from_existing(d.to_str().unwrap()); } + + #[test] + fn read_index1_file_entry() { + let data = [ + 0xEF, 0x02, 0x50, 0x1C, 0x68, 0xCF, 0x4E, 0x00, 0x60, 0x01, 0x6E, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + + let mut cursor = Cursor::new(&data); + + let file_entry = + FileEntry::read_options(&mut cursor, Endian::Little, (&IndexType::Index1,)).unwrap(); + + let expected_hash = Hash::SplitPath { + name: 475005679, + path: 5164904, + }; + assert_eq!(file_entry.hash, expected_hash); + assert_eq!(file_entry.data.is_synonym, false); + assert_eq!(file_entry.data.data_file_id, 0); + assert_eq!(file_entry.data.offset, 768); + } }