diff --git a/README.md b/README.md index 917b89b..2ce1b50 100755 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@ Physis is a [Rust](https://www.rust-lang.org/learn/get-started) library for read ```rust,no_run use physis::{ - Error, + Result, resource::{SqPackResource, Resource}, model::MDL, }; -fn main() -> Result<(), Error> { +fn main() -> Result<()> { // Construct a resource to read from SqPack: let mut resource = SqPackResource::from_existing("game"); // Read the raw data of this file, our resource takes care of decompressing it: - let bytes = resource.read(".mdl").ok_or(Error::Unknown)?; + let bytes = resource.read(".mdl")?; // Or read and parse it: let mdl = resource.parsed::("test.mdl")?; diff --git a/examples/extractor.rs b/examples/extractor.rs index f9453c0..743465a 100644 --- a/examples/extractor.rs +++ b/examples/extractor.rs @@ -23,7 +23,7 @@ fn main() { let mut game_data = SqPackResource::from_existing(game_dir); // Extract said file: - let Some(game_file) = game_data.read(file_path) else { + let Ok(game_file) = game_data.read(file_path) else { println!("File {} not found!", file_path); return; }; diff --git a/src/amb.rs b/src/amb.rs index 175ead6..62a62c2 100644 --- a/src/amb.rs +++ b/src/amb.rs @@ -77,25 +77,24 @@ pub struct AmbEntry { } impl ReadableFile for Amb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); - Amb::read_options(&mut cursor, endianness, ()).ok() + Ok(Amb::read_options(&mut cursor, endianness, ())?) } } impl WritableFile for Amb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/atch.rs b/src/atch.rs index 3552913..9215a46 100644 --- a/src/atch.rs +++ b/src/atch.rs @@ -40,25 +40,24 @@ impl Atch { } impl ReadableFile for Atch { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); - Self::read_options(&mut cursor, endianness, ()).ok() + Ok(Self::read_options(&mut cursor, endianness, ())?) } } impl WritableFile for Atch { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/avfx.rs b/src/avfx.rs index dc9132f..5920425 100644 --- a/src/avfx.rs +++ b/src/avfx.rs @@ -403,16 +403,15 @@ where } impl ReadableFile for Avfx { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - let header = - AvfxBlock::::read_options(&mut cursor, platform.endianness(), ()).ok()?; - Some(header.data) + let header = AvfxBlock::::read_options(&mut cursor, platform.endianness(), ())?; + Ok(header.data) } } impl WritableFile for Avfx { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -422,12 +421,10 @@ impl WritableFile for Avfx { size: 0, data: self.clone(), }; - block - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + block.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/cmp.rs b/src/cmp.rs index d314fbc..99df988 100644 --- a/src/cmp.rs +++ b/src/cmp.rs @@ -59,10 +59,10 @@ pub struct CMP { } impl ReadableFile for CMP { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - cursor.seek(SeekFrom::Start(0x2a800)).ok()?; + cursor.seek(SeekFrom::Start(0x2a800))?; let rem = buffer.len() - cursor.position() as usize; let entries = rem / std::mem::size_of::(); @@ -70,13 +70,14 @@ impl ReadableFile for CMP { let mut parameters = vec![]; for _ in 0..entries { - parameters.push( - RacialScalingParameters::read_options(&mut cursor, platform.endianness(), ()) - .ok()?, - ); + parameters.push(RacialScalingParameters::read_options( + &mut cursor, + platform.endianness(), + (), + )?); } - Some(CMP { parameters }) + Ok(CMP { parameters }) } } diff --git a/src/common.rs b/src/common.rs index 31f8632..66224bd 100755 --- a/src/common.rs +++ b/src/common.rs @@ -168,7 +168,7 @@ impl Ord for Version<'_> { /// This should be implemented for all types readable from SqPack. pub trait ReadableFile: Sized { /// Read an existing file. - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option; + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result; } /// A file that can be written back to its serialized byte form. @@ -176,7 +176,7 @@ pub trait ReadableFile: Sized { /// This should be implemented for all types readable from SqPack, on a best-effort basis. pub trait WritableFile: Sized { /// Writes data back to a buffer. - fn write_to_buffer(&self, platform: Platform) -> Option; + fn write_to_buffer(&self, platform: Platform) -> crate::Result; } /// Used for basic sanity checking tests in other modules. @@ -188,7 +188,7 @@ pub fn pass_random_invalid() { // Feeding it invalid data should not panic // Note that we don't check the Option currently, because some types like Hwc return Some regardless. - T::from_existing( + let _ = T::from_existing( Platform::Win32, &std::fs::read(d).expect("Could not read random test file"), ); diff --git a/src/compression.rs b/src/compression.rs index 2bd34b0..55ec3c1 100755 --- a/src/compression.rs +++ b/src/compression.rs @@ -6,7 +6,7 @@ use std::ptr::null_mut; use libz_rs_sys::*; /// Decompress ZLib data that has no header. -pub fn no_header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> bool { +pub fn no_header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> crate::Result<()> { unsafe { let mut strm = z_stream { next_in: null_mut(), @@ -32,7 +32,7 @@ pub fn no_header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> bool { core::mem::size_of::() as i32, ); if ret != Z_OK { - return false; + return Err(crate::Error::Zlib(ret)); } strm.next_in = in_data.as_mut_ptr(); @@ -41,17 +41,17 @@ pub fn no_header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> bool { let ret = inflate(&mut strm, Z_NO_FLUSH); if ret != Z_STREAM_END { - return false; + return Err(crate::Error::Zlib(ret)); } inflateEnd(&mut strm); - true + Ok(()) } } /// Decompress zlib data that has a header. -pub fn header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> Option { +pub fn header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> crate::Result { unsafe { let mut strm = z_stream { next_in: null_mut(), @@ -76,7 +76,7 @@ pub fn header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> Option() as i32, ); if ret != Z_OK { - return None; + return Err(crate::Error::Zlib(ret)); } strm.next_in = in_data.as_mut_ptr(); @@ -85,11 +85,11 @@ pub fn header_decompress(in_data: &mut [u8], out_data: &mut [u8]) -> Option Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(0); - Cutscene::read_options(&mut cursor, platform.endianness(), (&string_heap,)).ok() + Ok(Cutscene::read_options( + &mut cursor, + platform.endianness(), + (&string_heap,), + )?) } } impl WritableFile for Cutscene { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); let mut string_heap = StringHeap::from(0); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/dic.rs b/src/dic.rs index 0887fd3..d0f5d1b 100644 --- a/src/dic.rs +++ b/src/dic.rs @@ -72,10 +72,9 @@ pub struct Dictionary { } impl ReadableFile for Dictionary { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - let mut dict = - DictionaryHeader::read_options(&mut cursor, platform.endianness(), ()).unwrap(); + let mut dict = DictionaryHeader::read_options(&mut cursor, platform.endianness(), ())?; let map_start = 0x8750u32; let map_size = 0x200u32; @@ -87,32 +86,32 @@ impl ReadableFile for Dictionary { for i in 0..dict.block_lengths[0] / 2 { let offset = dict.block_offsets[0] + i * 2; - cursor.seek(SeekFrom::Start(offset as u64)).ok()?; - dict.begin_node.push(cursor.read_le::().ok()?); + cursor.seek(SeekFrom::Start(offset as u64))?; + dict.begin_node.push(cursor.read_le::()?); } for i in 0..dict.block_lengths[1] / 2 { let offset = dict.block_offsets[1] + i * 2; - cursor.seek(SeekFrom::Start(offset as u64)).ok()?; - dict.inner_node.push(cursor.read_le::().ok()?); + cursor.seek(SeekFrom::Start(offset as u64))?; + dict.inner_node.push(cursor.read_le::()?); } for i in 0..dict.block_lengths[2] / 2 { let offset = dict.block_offsets[2] + i * 2; - cursor.seek(SeekFrom::Start(offset as u64)).ok()?; - dict.chara.push(cursor.read_le::().ok()?); + cursor.seek(SeekFrom::Start(offset as u64))?; + dict.chara.push(cursor.read_le::()?); } for i in 0..dict.block_lengths[3] / 2 { let offset = dict.block_offsets[3] + i * 2; - cursor.seek(SeekFrom::Start(offset as u64)).ok()?; - dict.word.push(cursor.read_le::().ok()?); + cursor.seek(SeekFrom::Start(offset as u64))?; + dict.word.push(cursor.read_le::()?); } for i in 0..dict.block_lengths[4] / 16 { let offset = dict.block_offsets[4] + i * 16; - cursor.seek(SeekFrom::Start(offset as u64)).ok()?; - dict.entries.push(cursor.read_le::().ok()?); + cursor.seek(SeekFrom::Start(offset as u64))?; + dict.entries.push(cursor.read_le::()?); } let mut dict = Dictionary { @@ -121,14 +120,14 @@ impl ReadableFile for Dictionary { }; // TODO: lol - dict.words = dict.list_words()?; + dict.words = dict.list_words(); - Some(dict) + Ok(dict) } } impl Dictionary { - fn list_words(&self) -> Option> { + fn list_words(&self) -> Vec { let mut result = Vec::new(); let lut = self.generate_index_rune_lookup_table(); for (id, v) in self.header.begin_node.iter().enumerate() { @@ -142,7 +141,7 @@ impl Dictionary { } } - Some(result) + result } fn generate_index_rune_lookup_table(&self) -> HashMap { diff --git a/src/eid.rs b/src/eid.rs index 57bd0a0..3091bd6 100644 --- a/src/eid.rs +++ b/src/eid.rs @@ -57,25 +57,24 @@ pub struct Eid { } impl ReadableFile for Eid { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); - Self::read_options(&mut cursor, endianness, ()).ok() + Ok(Self::read_options(&mut cursor, endianness, ())?) } } impl WritableFile for Eid { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/envb.rs b/src/envb.rs index b5ab5fe..e755470 100644 --- a/src/envb.rs +++ b/src/envb.rs @@ -34,17 +34,21 @@ pub struct Envb { } impl ReadableFile for Envb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(cursor.position() as i64); - Envb::read_options(&mut cursor, endianness, (&string_heap,)).ok() + Ok(Envb::read_options( + &mut cursor, + endianness, + (&string_heap,), + )?) } } impl WritableFile for Envb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -53,18 +57,15 @@ impl WritableFile for Envb { // TODO: need dual pass let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; - string_heap - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + string_heap.write_options(&mut cursor, platform.endianness(), ())?; let unk_ending = &[0x0; 8]; - cursor.write_all(unk_ending).ok()?; + cursor.write_all(unk_ending)?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/error.rs b/src/error.rs index 79b3a87..ba8ce58 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,31 +1,60 @@ // SPDX-FileCopyrightText: 2025 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later +use std::path::PathBuf; + /// The error type used throughout this crate. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Error { /// The specified path was not found in any resources. FileNotFound { /// The path to the file that wasn't found. path: String, }, - /// There was an error while parsing this file. - FileParsingFailed { - /// The path to the file that failed to parse. - path: String, - }, - // TODO: Remove before release - Unknown, + /// I/O error. + Io(std::io::Error), + /// Binrw error. + Binrw(binrw::Error), + /// zlib error. + Zlib(i32), + /// The file header loaded correctly, but considered invalid for some other reason e.g. there are no chunks in a file that is supposed to have at least one. + InvalidFile, + /// While patching, if we encounted a chunk that requires TargetInfo to be present but isn't at that point in time. + TargetInfoMissing, + /// The hash wasn't found in any index file. + HashNotFound { hash: crate::sqpack::Hash }, + /// Right now, this is only used when trying to find the parent during patching of this path but couldn't.' + InvalidFilename { path: PathBuf }, + /// Right now is this a catch-all error when a resolver function fails. + ResolverFailed, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Error::FileNotFound { path } => write!(f, "file not found: {path}"), - Error::FileParsingFailed { path } => write!(f, "file parsing failed: {path}"), - Error::Unknown => write!(f, "unknown error"), + Error::ResolverFailed => write!(f, "resolver failed"), + Error::Io(error) => write!(f, "io: {error}"), + Error::Binrw(error) => write!(f, "binrw: {error}"), + Error::Zlib(error) => write!(f, "zlib: {error}"), + Error::InvalidFile => write!(f, "invalid file"), + Error::TargetInfoMissing => write!(f, "target info missing"), + Error::HashNotFound { hash } => write!(f, "hash {hash:?} not found"), + Error::InvalidFilename { path } => write!(f, "invalid filename: {path:?}"), } } } impl std::error::Error for Error {} + +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + +impl From for Error { + fn from(value: binrw::Error) -> Self { + Self::Binrw(value) + } +} diff --git a/src/essb.rs b/src/essb.rs index a021a26..2a6b4fa 100644 --- a/src/essb.rs +++ b/src/essb.rs @@ -34,17 +34,21 @@ pub struct Essb { } impl ReadableFile for Essb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(cursor.position() as i64); - Essb::read_options(&mut cursor, endianness, (&string_heap,)).ok() + Ok(Essb::read_options( + &mut cursor, + endianness, + (&string_heap,), + )?) } } impl WritableFile for Essb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -53,18 +57,15 @@ impl WritableFile for Essb { // TODO: need dual pass let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; - string_heap - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + string_heap.write_options(&mut cursor, platform.endianness(), ())?; let unk_ending = &[0x0; 8]; - cursor.write_all(unk_ending).ok()?; + cursor.write_all(unk_ending)?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/exd.rs b/src/exd.rs index 9f04227..b14ee35 100644 --- a/src/exd.rs +++ b/src/exd.rs @@ -113,8 +113,8 @@ impl EXD { } impl ReadableFile for EXD { - fn from_existing(_platform: Platform, buffer: ByteSpan) -> Option { - EXD::read_args(&mut Cursor::new(&buffer), ()).ok() + fn from_existing(_platform: Platform, buffer: ByteSpan) -> crate::Result { + Ok(EXD::read_args(&mut Cursor::new(&buffer), ())?) } } diff --git a/src/exh.rs b/src/exh.rs index 694cd97..188885b 100644 --- a/src/exh.rs +++ b/src/exh.rs @@ -160,23 +160,23 @@ impl Default for EXH { } impl ReadableFile for EXH { - fn from_existing(_platform: Platform, buffer: ByteSpan) -> Option { - Self::read(&mut Cursor::new(&buffer)).ok() + fn from_existing(_platform: Platform, buffer: ByteSpan) -> crate::Result { + Ok(Self::read(&mut Cursor::new(&buffer))?) } } impl WritableFile for EXH { - fn write_to_buffer(&self, _platform: Platform) -> Option { + fn write_to_buffer(&self, _platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let cursor = Cursor::new(&mut buffer); let mut writer = BufWriter::new(cursor); - self.write_args(&mut writer, ()).unwrap(); + self.write_args(&mut writer, ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/exl.rs b/src/exl.rs index 6414837..0dab32f 100755 --- a/src/exl.rs +++ b/src/exl.rs @@ -17,7 +17,7 @@ pub struct EXL { } impl ReadableFile for EXL { - fn from_existing(_platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(_platform: Platform, buffer: ByteSpan) -> crate::Result { let mut exl = Self { version: 0, entries: Vec::new(), @@ -39,28 +39,26 @@ impl ReadableFile for EXL { } } - Some(exl) + Ok(exl) } } impl WritableFile for EXL { - fn write_to_buffer(&self, _platform: Platform) -> Option { + fn write_to_buffer(&self, _platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let cursor = Cursor::new(&mut buffer); let mut writer = BufWriter::new(cursor); - writer - .write_all(format!("EXLT,{}", self.version).as_ref()) - .ok()?; + writer.write_all(format!("EXLT,{}", self.version).as_ref())?; for (key, value) in &self.entries { - writer.write_all(format!("\n{key},{value}").as_ref()).ok()?; + writer.write_all(format!("\n{key},{value}").as_ref())?; } } - Some(buffer) + Ok(buffer) } } diff --git a/src/fdt.rs b/src/fdt.rs index 26a120e..bd7315f 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -22,23 +22,22 @@ pub struct Fdt { } impl ReadableFile for Fdt { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Self::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Self::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Fdt { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/hwc.rs b/src/hwc.rs index e7e8f3b..7f8f1dd 100644 --- a/src/hwc.rs +++ b/src/hwc.rs @@ -17,28 +17,27 @@ pub struct Hwc { } impl ReadableFile for Hwc { - fn from_existing(_platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(_platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let mut rgba = vec![0; Self::WIDTH * Self::HEIGHT * 4]; - cursor.read_exact(&mut rgba).ok()?; + cursor.read_exact(&mut rgba)?; - Some(Self { rgba }) + Ok(Self { rgba }) } } impl WritableFile for Hwc { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); self.rgba - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + .write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/iwc.rs b/src/iwc.rs index 4241131..cf31d8c 100644 --- a/src/iwc.rs +++ b/src/iwc.rs @@ -26,23 +26,22 @@ pub struct Iwc { } impl ReadableFile for Iwc { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Iwc::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Iwc::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Iwc { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/layer/mod.rs b/src/layer/mod.rs index b2110eb..f235bbf 100644 --- a/src/layer/mod.rs +++ b/src/layer/mod.rs @@ -7,7 +7,7 @@ use std::io::{Read, Seek, SeekFrom}; use crate::common_file_operations::{read_bool_from, write_bool_as}; use crate::string_heap::{HeapPointer, HeapString, StringHeap}; -use binrw::{BinRead, BinReaderExt}; +use binrw::{BinRead, BinReaderExt, BinResult}; use binrw::{Endian, binrw}; mod aetheryte; @@ -606,37 +606,36 @@ impl Layer { cursor: &mut T, data_heap: &StringHeap, string_heap: &StringHeap, - ) -> Option { - let old_pos = cursor.stream_position().unwrap(); + ) -> BinResult { + let old_pos = cursor.stream_position()?; let header = - LayerHeader::read_options(cursor, endianness, (endianness, data_heap, string_heap)) - .unwrap(); + LayerHeader::read_options(cursor, endianness, (endianness, data_heap, string_heap))?; let mut objects = Vec::new(); // read instance objects { let mut instance_offsets = vec![0i32; header.instance_object_count as usize]; for i in 0..header.instance_object_count { - instance_offsets[i as usize] = - cursor.read_type_args::(endianness, ()).unwrap(); + instance_offsets[i as usize] = cursor.read_type_args::(endianness, ())?; } for i in 0..header.instance_object_count { - cursor - .seek(SeekFrom::Start( - old_pos - + header.instance_object_offset as u64 - + instance_offsets[i as usize] as u64, - )) - .unwrap(); - - objects - .push(InstanceObject::read_options(cursor, endianness, (string_heap,)).ok()?); + cursor.seek(SeekFrom::Start( + old_pos + + header.instance_object_offset as u64 + + instance_offsets[i as usize] as u64, + ))?; + + objects.push(InstanceObject::read_options( + cursor, + endianness, + (string_heap,), + )?); } } - Some(Layer { header, objects }) + Ok(Layer { header, objects }) } } diff --git a/src/lcb.rs b/src/lcb.rs index f3ee2cb..099cceb 100644 --- a/src/lcb.rs +++ b/src/lcb.rs @@ -63,23 +63,22 @@ pub struct LccEntry { } impl ReadableFile for Lcb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Lcb::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Lcb::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Lcb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/lgb.rs b/src/lgb.rs index 9fbbca4..7aa219e 100644 --- a/src/lgb.rs +++ b/src/lgb.rs @@ -72,46 +72,44 @@ pub struct Lgb { } impl ReadableFile for Lgb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let endianness = platform.endianness(); let string_heap = StringHeap::from(cursor.position() as i64); let data_heap = StringHeap::from(cursor.position() as i64); - let file_header = LgbHeader::read_options(&mut cursor, endianness, ()).ok()?; + let file_header = LgbHeader::read_options(&mut cursor, endianness, ())?; if file_header.file_size <= 0 || file_header.total_chunk_count <= 0 { - return None; + return Err(crate::Error::InvalidFile); } // This actually matches client behavior, because of course they have padding here. loop { - let magic = <[u8; 4]>::read_options(&mut cursor, endianness, ()).ok()?; + let magic = <[u8; 4]>::read_options(&mut cursor, endianness, ())?; if &magic == b"LGP1" { - cursor.seek(SeekFrom::Current(-4)).ok()?; + cursor.seek(SeekFrom::Current(-4))?; break; } } let chunk_header = - LayerChunkHeader::read_options(&mut cursor, endianness, (&string_heap,)).unwrap(); + LayerChunkHeader::read_options(&mut cursor, endianness, (&string_heap,))?; if chunk_header.chunk_size <= 0 { - return Some(Lgb { chunks: Vec::new() }); + return Ok(Lgb { chunks: Vec::new() }); } let old_pos = cursor.position(); let mut layer_offsets = vec![0i32; chunk_header.layer_count as usize]; for i in 0..chunk_header.layer_count { - layer_offsets[i as usize] = cursor.read_type_args::(endianness, ()).ok()?; + layer_offsets[i as usize] = cursor.read_type_args::(endianness, ())?; } let mut layers = Vec::new(); for i in 0..chunk_header.layer_count { - cursor - .seek(SeekFrom::Start(old_pos + layer_offsets[i as usize] as u64)) - .unwrap(); + cursor.seek(SeekFrom::Start(old_pos + layer_offsets[i as usize] as u64))?; let layer = Layer::read(endianness, &mut cursor, &data_heap, &string_heap)?; layers.push(layer); @@ -123,41 +121,35 @@ impl ReadableFile for Lgb { layers, }; - Some(Lgb { + Ok(Lgb { chunks: vec![layer_chunk], }) } } impl WritableFile for Lgb { - fn write_to_buffer(&self, _platform: Platform) -> Option { + fn write_to_buffer(&self, _platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); // skip header, will be writing it later - cursor - .seek(SeekFrom::Start(LgbHeader::SIZE as u64)) - .unwrap(); + cursor.seek(SeekFrom::Start(LgbHeader::SIZE as u64))?; // we don't store the positions for this pass, so the defaults are fine. let mut chunk_data_heap = StringHeap::default(); let mut chunk_string_heap = StringHeap::default(); // we will write this later, when we have a working string heap - let layer_chunk_header_pos = cursor.stream_position().unwrap(); - cursor - .seek(SeekFrom::Current(LayerChunkHeader::SIZE as i64)) - .unwrap(); + let layer_chunk_header_pos = cursor.stream_position()?; + cursor.seek(SeekFrom::Current(LayerChunkHeader::SIZE as i64))?; // skip offsets for now, they will be written later let offset_pos = cursor.position(); - cursor - .seek(SeekFrom::Current( - (std::mem::size_of::() * self.chunks[0].layers.len()) as i64, - )) - .ok()?; + cursor.seek(SeekFrom::Current( + (std::mem::size_of::() * self.chunks[0].layers.len()) as i64, + ))?; let mut layer_offsets: Vec = Vec::new(); let mut object_offsets: Vec = Vec::new(); @@ -172,29 +164,25 @@ impl WritableFile for Lgb { layer .header - .write_le_args(&mut cursor, (&mut chunk_data_heap, &mut chunk_string_heap)) - .ok()?; + .write_le_args(&mut cursor, (&mut chunk_data_heap, &mut chunk_string_heap))?; - let object_offset_base = cursor.stream_position().ok()? as i32; + let object_offset_base = cursor.stream_position()? as i32; // Skip offsets that will be written later. - cursor - .seek(SeekFrom::Current( - layer.objects.len() as i64 * std::mem::size_of::() as i64, - )) - .ok()?; + cursor.seek(SeekFrom::Current( + layer.objects.len() as i64 * std::mem::size_of::() as i64, + ))?; for obj in &layer.objects { - object_offsets.push(cursor.stream_position().ok()? as i32 - object_offset_base); + object_offsets.push(cursor.stream_position()? as i32 - object_offset_base); - obj.write_le_args(&mut cursor, (&mut chunk_string_heap,)) - .ok()?; + obj.write_le_args(&mut cursor, (&mut chunk_string_heap,))?; } } // make sure the heaps are at the end of the layer data: // TODO: this logic doesn't make much sense... - let data_offset = cursor.stream_position().ok()? + let data_offset = cursor.stream_position()? + (if !layer_offsets.is_empty() { LgbHeader::SIZE as u64 } else { @@ -212,9 +200,7 @@ impl WritableFile for Lgb { }; // write header now, because it has a string - cursor - .seek(SeekFrom::Start(layer_chunk_header_pos)) - .unwrap(); + cursor.seek(SeekFrom::Start(layer_chunk_header_pos))?; // TODO: support multiple layer chunks let layer_chunk = LayerChunkHeader { @@ -226,12 +212,10 @@ impl WritableFile for Lgb { layer_offset: 16, // lol layer_count: self.chunks[0].layers.len() as i32, }; - layer_chunk - .write_le_args(&mut cursor, (&mut chunk_string_heap,)) - .ok()?; + layer_chunk.write_le_args(&mut cursor, (&mut chunk_string_heap,))?; // now write the layer data for the final time - cursor.seek(SeekFrom::Start(layer_data_offset)).unwrap(); + cursor.seek(SeekFrom::Start(layer_data_offset))?; for layer in &self.chunks[0].layers { // Write the correct amount of objects and their offsets now let mut new_header = layer.header.clone(); @@ -239,27 +223,25 @@ impl WritableFile for Lgb { new_header.instance_object_offset = 52; // TODO: placeholder new_header - .write_le_args(&mut cursor, (&mut chunk_data_heap, &mut chunk_string_heap)) - .ok()?; + .write_le_args(&mut cursor, (&mut chunk_data_heap, &mut chunk_string_heap))?; - object_offsets.write_le(&mut cursor).ok()?; + object_offsets.write_le(&mut cursor)?; for obj in &layer.objects { - obj.write_le_args(&mut cursor, (&mut chunk_string_heap,)) - .ok()?; + obj.write_le_args(&mut cursor, (&mut chunk_string_heap,))?; } } // write the heaps - chunk_data_heap.write_le(&mut cursor).ok()?; - chunk_string_heap.write_le(&mut cursor).ok()?; + chunk_data_heap.write_le(&mut cursor)?; + chunk_string_heap.write_le(&mut cursor)?; // write offsets assert_eq!(layer_offsets.len(), self.chunks[0].layers.len()); - cursor.seek(SeekFrom::Start(offset_pos)).ok()?; + cursor.seek(SeekFrom::Start(offset_pos))?; for offset in layer_offsets { // TODO: im probably subtracting from the wrong offset - (offset - offset_pos as i32).write_le(&mut cursor).ok()?; + (offset - offset_pos as i32).write_le(&mut cursor)?; } } @@ -269,15 +251,15 @@ impl WritableFile for Lgb { let mut cursor = Cursor::new(&mut buffer); // write the header, now that we now the file size - cursor.seek(SeekFrom::Start(0)).ok()?; + cursor.seek(SeekFrom::Start(0))?; let lgb_header = LgbHeader { file_size, total_chunk_count: self.chunks.len() as i32, }; - lgb_header.write_le(&mut cursor).ok()?; + lgb_header.write_le(&mut cursor)?; } - Some(buffer) + Ok(buffer) } } @@ -325,7 +307,7 @@ mod tests { // NOTE: It would be nice to read these eventually, but I'm pretty sure all cases are empty and useless. // So the best we could do right now is not panic while reading them. - assert!(Lgb::from_existing(Platform::Win32, &read(d).unwrap()).is_none()); + assert!(Lgb::from_existing(Platform::Win32, &read(d).unwrap()).is_err()); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 0a94273..7939525 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,9 @@ mod bcn; mod error; pub use error::Error; +/// A type alias for `Result`. +pub type Result = std::result::Result; + /// Find existing installation directories pub mod existing_dirs; diff --git a/src/lvb.rs b/src/lvb.rs index 1b59765..8f2370d 100644 --- a/src/lvb.rs +++ b/src/lvb.rs @@ -41,16 +41,20 @@ pub struct Lvb { } impl ReadableFile for Lvb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(cursor.position() as i64); - Lvb::read_options(&mut cursor, platform.endianness(), (&string_heap,)).ok() + Ok(Lvb::read_options( + &mut cursor, + platform.endianness(), + (&string_heap,), + )?) } } impl WritableFile for Lvb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -59,15 +63,12 @@ impl WritableFile for Lvb { // TODO: need dual pass let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; - string_heap - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + string_heap.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/model/file_operations.rs b/src/model/file_operations.rs index cfa8c69..6c62a69 100644 --- a/src/model/file_operations.rs +++ b/src/model/file_operations.rs @@ -11,12 +11,12 @@ use std::io::Cursor; const MAX_BYTE_FLOAT: f32 = u8::MAX as f32; impl MDL { - pub(crate) fn read_byte_float4(cursor: &mut Cursor) -> Option<[f32; 4]> { - Some([ - (f32::from(cursor.read_le::().ok()?) / MAX_BYTE_FLOAT), - (f32::from(cursor.read_le::().ok()?) / MAX_BYTE_FLOAT), - (f32::from(cursor.read_le::().ok()?) / MAX_BYTE_FLOAT), - (f32::from(cursor.read_le::().ok()?) / MAX_BYTE_FLOAT), + pub(crate) fn read_byte_float4(cursor: &mut Cursor) -> BinResult<[f32; 4]> { + Ok([ + (f32::from(cursor.read_le::()?) / MAX_BYTE_FLOAT), + (f32::from(cursor.read_le::()?) / MAX_BYTE_FLOAT), + (f32::from(cursor.read_le::()?) / MAX_BYTE_FLOAT), + (f32::from(cursor.read_le::()?) / MAX_BYTE_FLOAT), ]) } @@ -44,12 +44,12 @@ impl MDL { ]) } - pub(crate) fn read_tangent(cursor: &mut Cursor) -> Option<[f32; 4]> { - Some([ - (f32::from(cursor.read_le::().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), - (f32::from(cursor.read_le::().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), - (f32::from(cursor.read_le::().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), - if (f32::from(cursor.read_le::().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0) == 1.0 { + pub(crate) fn read_tangent(cursor: &mut Cursor) -> BinResult<[f32; 4]> { + Ok([ + (f32::from(cursor.read_le::()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), + (f32::from(cursor.read_le::()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), + (f32::from(cursor.read_le::()?) * 2.0 / MAX_BYTE_FLOAT - 1.0), + if (f32::from(cursor.read_le::()?) * 2.0 / MAX_BYTE_FLOAT - 1.0) == 1.0 { 1.0 } else { -1.0 @@ -66,12 +66,12 @@ impl MDL { ]) // SqEx uses 0 as -1, not 1 } - pub(crate) fn read_half4(cursor: &mut Cursor, endian: Endian) -> Option<[f32; 4]> { - Some([ - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), + pub(crate) fn read_half4(cursor: &mut Cursor, endian: Endian) -> BinResult<[f32; 4]> { + Ok([ + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), ]) } @@ -92,10 +92,10 @@ impl MDL { ) } - pub(crate) fn read_half2(cursor: &mut Cursor, endian: Endian) -> Option<[f32; 2]> { - Some([ - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), - f16::from_bits(cursor.read_type_args::(endian, ()).ok()?).to_f32(), + pub(crate) fn read_half2(cursor: &mut Cursor, endian: Endian) -> BinResult<[f32; 2]> { + Ok([ + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), + f16::from_bits(cursor.read_type_args::(endian, ())?).to_f32(), ]) } diff --git a/src/model/mod.rs b/src/model/mod.rs index 2a8d0ef..116adfd 100755 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -266,7 +266,7 @@ struct BoneTablesV2 { } fn pad_to_alignment(reader: &mut T) -> BinResult<()> { - let position = reader.stream_position().unwrap() as i64; + let position = reader.stream_position()? as i64; // pad to 4 byte alignment let padding = if position % 4 == 0 { @@ -813,12 +813,12 @@ impl MDL { } impl ReadableFile for MDL { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let endianness = platform.endianness(); - let model_file_header = ModelFileHeader::read_options(&mut cursor, endianness, ()).ok()?; + let model_file_header = ModelFileHeader::read_options(&mut cursor, endianness, ())?; - let model = ModelData::read_options(&mut cursor, endianness, (&model_file_header,)).ok()?; + let model = ModelData::read_options(&mut cursor, endianness, (&model_file_header,))?; let mut affected_bone_names = vec![]; @@ -854,34 +854,32 @@ impl ReadableFile for MDL { for k in 0..vertex_count { for element in &declaration.elements { - cursor - .seek(SeekFrom::Start( - (model.lods[i as usize].vertex_data_offset - + model.meshes[j as usize].vertex_buffer_offsets - [element.stream as usize] - + element.offset as u32 - + model.meshes[j as usize].vertex_buffer_strides - [element.stream as usize] - as u32 - * k as u32) as u64, - )) - .ok()?; + cursor.seek(SeekFrom::Start( + (model.lods[i as usize].vertex_data_offset + + model.meshes[j as usize].vertex_buffer_offsets + [element.stream as usize] + + element.offset as u32 + + model.meshes[j as usize].vertex_buffer_strides + [element.stream as usize] + as u32 + * k as u32) as u64, + ))?; match element.vertex_usage { VertexUsage::Position => match element.vertex_type { VertexType::Single4 => { vertices[k as usize].position.clone_from_slice( - &MDL::read_single4(&mut cursor, endianness).unwrap()[0..3], + &MDL::read_single4(&mut cursor, endianness)?[0..3], ); } VertexType::Half4 => { vertices[k as usize].position.clone_from_slice( - &MDL::read_half4(&mut cursor, endianness).unwrap()[0..3], + &MDL::read_half4(&mut cursor, endianness)?[0..3], ); } VertexType::Single3 => { vertices[k as usize].position = - MDL::read_single3(&mut cursor, endianness).unwrap(); + MDL::read_single3(&mut cursor, endianness)?; } _ => { panic!( @@ -893,15 +891,14 @@ impl ReadableFile for MDL { VertexUsage::BlendWeights => match element.vertex_type { VertexType::ByteFloat4 => { vertices[k as usize].bone_weight = - MDL::read_byte_float4(&mut cursor).unwrap(); + MDL::read_byte_float4(&mut cursor)?; } VertexType::Byte4 => { vertices[k as usize].bone_weight = - MDL::read_byte_float4(&mut cursor).unwrap(); + MDL::read_byte_float4(&mut cursor)?; } VertexType::UnsignedShort4 => { - let bytes = - MDL::read_unsigned_short4(&mut cursor, endianness).unwrap(); + let bytes = MDL::read_unsigned_short4(&mut cursor, endianness)?; vertices[k as usize].bone_weight = [ f32::from(bytes[0]), f32::from(bytes[1]), @@ -918,12 +915,11 @@ impl ReadableFile for MDL { }, VertexUsage::BlendIndices => match element.vertex_type { VertexType::Byte4 => { - vertices[k as usize].bone_id = - MDL::read_byte4(&mut cursor).unwrap(); + vertices[k as usize].bone_id = MDL::read_byte4(&mut cursor)?; } VertexType::UnsignedShort4 => { let shorts = - MDL::read_unsigned_short4(&mut cursor, endianness).unwrap(); + MDL::read_unsigned_short4(&mut cursor, endianness)?; vertices[k as usize].bone_id = [ shorts[0] as u8, shorts[1] as u8, @@ -941,17 +937,17 @@ impl ReadableFile for MDL { VertexUsage::Normal => match element.vertex_type { VertexType::Half4 => { vertices[k as usize].normal.clone_from_slice( - &MDL::read_half4(&mut cursor, endianness).unwrap()[0..3], + &MDL::read_half4(&mut cursor, endianness)?[0..3], ); } VertexType::Single3 => { vertices[k as usize].normal = - MDL::read_single3(&mut cursor, endianness).unwrap(); + MDL::read_single3(&mut cursor, endianness)?; } VertexType::UnkPS3 => { // TODO: unsure vertices[k as usize].normal.clone_from_slice( - &MDL::read_byte_float4(&mut cursor).unwrap()[0..3], + &MDL::read_byte_float4(&mut cursor)?[0..3], ); } _ => { @@ -963,28 +959,25 @@ impl ReadableFile for MDL { }, VertexUsage::UV => match element.vertex_type { VertexType::ByteFloat4 => { - let combined = MDL::read_byte_float4(&mut cursor).unwrap(); + let combined = MDL::read_byte_float4(&mut cursor)?; vertices[k as usize].uv0.clone_from_slice(&combined[0..2]); vertices[k as usize].uv1.clone_from_slice(&combined[2..4]); } VertexType::Half4 => { - let combined = - MDL::read_half4(&mut cursor, endianness).unwrap(); + let combined = MDL::read_half4(&mut cursor, endianness)?; vertices[k as usize].uv0.clone_from_slice(&combined[0..2]); vertices[k as usize].uv1.clone_from_slice(&combined[2..4]); } VertexType::Single4 => { - let combined = - MDL::read_single4(&mut cursor, endianness).unwrap(); + let combined = MDL::read_single4(&mut cursor, endianness)?; vertices[k as usize].uv0.clone_from_slice(&combined[0..2]); vertices[k as usize].uv1.clone_from_slice(&combined[2..4]); } VertexType::Half2 => { - let combined = - MDL::read_half2(&mut cursor, endianness).unwrap(); + let combined = MDL::read_half2(&mut cursor, endianness)?; vertices[k as usize].uv0.clone_from_slice(&combined[0..2]); } @@ -998,7 +991,7 @@ impl ReadableFile for MDL { VertexUsage::BiTangent => match element.vertex_type { VertexType::ByteFloat4 => { vertices[k as usize].bitangent = - MDL::read_tangent(&mut cursor).unwrap(); + MDL::read_tangent(&mut cursor)?; } _ => { panic!( @@ -1022,7 +1015,7 @@ impl ReadableFile for MDL { VertexUsage::Color => match element.vertex_type { VertexType::ByteFloat4 => { vertices[k as usize].color = - MDL::read_byte_float4(&mut cursor).unwrap(); + MDL::read_byte_float4(&mut cursor)?; } _ => { panic!( @@ -1035,18 +1028,15 @@ impl ReadableFile for MDL { } } - cursor - .seek(SeekFrom::Start( - (model_file_header.index_offsets[i as usize] - + (model.meshes[j as usize].start_index * size_of::() as u32)) - as u64, - )) - .ok()?; + cursor.seek(SeekFrom::Start( + (model_file_header.index_offsets[i as usize] + + (model.meshes[j as usize].start_index * size_of::() as u32)) + as u64, + ))?; let index_count = model.meshes[j as usize].index_count as usize; let indices: Vec = cursor - .read_type_args(endianness, VecArgs::builder().count(index_count).finalize()) - .ok()?; + .read_type_args(endianness, VecArgs::builder().count(index_count).finalize())?; let mut submeshes: Vec = Vec::with_capacity(model.meshes[j as usize].submesh_count as usize); @@ -1136,18 +1126,14 @@ impl ReadableFile for MDL { for z in 0..mesh.vertex_count { // TODO: read the entire vertex data into a buffer // Handle the offsets within Novus itself - cursor - .seek(SeekFrom::Start( - (model.lods[i as usize].vertex_data_offset - + model.meshes[j as usize].vertex_buffer_offsets - [stream as usize] - + (z as u32 * stride as u32)) - as u64, - )) - .ok()?; + cursor.seek(SeekFrom::Start( + (model.lods[i as usize].vertex_data_offset + + model.meshes[j as usize].vertex_buffer_offsets[stream as usize] + + (z as u32 * stride as u32)) as u64, + ))?; for _ in 0..stride { - vertex_data.push(cursor.read_le::().ok()?); + vertex_data.push(cursor.read_le::()?); } } @@ -1172,7 +1158,7 @@ impl ReadableFile for MDL { lods.push(Lod { parts }); } - Some(MDL { + Ok(MDL { file_header: model_file_header, model_data: model, lods, @@ -1195,7 +1181,7 @@ fn get_part_type(lod: &MeshLod, index: u16) -> Option { } impl WritableFile for MDL { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); let endianness = platform.endianness(); @@ -1204,12 +1190,10 @@ impl WritableFile for MDL { // write file header self.file_header - .write_options(&mut cursor, endianness, ()) - .ok()?; + .write_options(&mut cursor, endianness, ())?; self.model_data - .write_options(&mut cursor, endianness, (&self.file_header,)) - .ok()?; + .write_options(&mut cursor, endianness, (&self.file_header,))?; for (l, lod) in self.lods.iter().enumerate() { for part in lod.parts.iter() { @@ -1218,20 +1202,18 @@ impl WritableFile for MDL { for (k, vert) in part.vertices.iter().enumerate() { for element in &declaration.elements { - cursor - .seek(SeekFrom::Start( - (self.model_data.lods[l].vertex_data_offset - + self.model_data.meshes[part.mesh_index as usize] - .vertex_buffer_offsets - [element.stream as usize] - + element.offset as u32 - + self.model_data.meshes[part.mesh_index as usize] - .vertex_buffer_strides - [element.stream as usize] - as u32 - * k as u32) as u64, - )) - .ok()?; + cursor.seek(SeekFrom::Start( + (self.model_data.lods[l].vertex_data_offset + + self.model_data.meshes[part.mesh_index as usize] + .vertex_buffer_offsets + [element.stream as usize] + + element.offset as u32 + + self.model_data.meshes[part.mesh_index as usize] + .vertex_buffer_strides + [element.stream as usize] + as u32 + * k as u32) as u64, + ))?; match element.vertex_usage { VertexUsage::Position => match element.vertex_type { @@ -1240,20 +1222,21 @@ impl WritableFile for MDL { &mut cursor, endianness, &MDL::pad_slice(&vert.position, 1.0), - ) - .ok()?; + )?; } VertexType::Half4 => { MDL::write_half4( &mut cursor, endianness, &MDL::pad_slice(&vert.position, 1.0), - ) - .ok()?; + )?; } VertexType::Single3 => { - MDL::write_single3(&mut cursor, endianness, &vert.position) - .ok()?; + MDL::write_single3( + &mut cursor, + endianness, + &vert.position, + )?; } _ => { panic!( @@ -1264,12 +1247,10 @@ impl WritableFile for MDL { }, VertexUsage::BlendWeights => match element.vertex_type { VertexType::ByteFloat4 => { - MDL::write_byte_float4(&mut cursor, &vert.bone_weight) - .ok()?; + MDL::write_byte_float4(&mut cursor, &vert.bone_weight)?; } VertexType::Byte4 => { - MDL::write_byte_float42(&mut cursor, &vert.bone_weight) - .ok()?; // TODO: WRONG! + MDL::write_byte_float42(&mut cursor, &vert.bone_weight)?; // TODO: WRONG! } _ => { panic!( @@ -1280,7 +1261,7 @@ impl WritableFile for MDL { }, VertexUsage::BlendIndices => match element.vertex_type { VertexType::Byte4 => { - MDL::write_byte4(&mut cursor, &vert.bone_id).ok()?; + MDL::write_byte4(&mut cursor, &vert.bone_id)?; } _ => { panic!( @@ -1295,20 +1276,17 @@ impl WritableFile for MDL { &mut cursor, endianness, &MDL::pad_slice(&vert.normal, 0.0), - ) - .ok()?; + )?; } VertexType::Single3 => { - MDL::write_single3(&mut cursor, endianness, &vert.normal) - .ok()?; + MDL::write_single3(&mut cursor, endianness, &vert.normal)?; } VertexType::UnkPS3 => { // TODO: unsure MDL::write_byte_float4( &mut cursor, &MDL::pad_slice(&vert.normal, 0.0), - ) - .ok()?; + )?; } _ => { panic!( @@ -1319,28 +1297,25 @@ impl WritableFile for MDL { }, VertexUsage::UV => match element.vertex_type { VertexType::Half2 => { - MDL::write_half2(&mut cursor, endianness, &vert.uv0) - .ok()?; + MDL::write_half2(&mut cursor, endianness, &vert.uv0)?; } VertexType::Half4 => { let combined = [vert.uv0[0], vert.uv0[1], vert.uv1[0], vert.uv1[1]]; - MDL::write_half4(&mut cursor, endianness, &combined) - .ok()?; + MDL::write_half4(&mut cursor, endianness, &combined)?; } VertexType::Single4 => { let combined = [vert.uv0[0], vert.uv0[1], vert.uv1[0], vert.uv1[1]]; - MDL::write_single4(&mut cursor, endianness, &combined) - .ok()?; + MDL::write_single4(&mut cursor, endianness, &combined)?; } VertexType::ByteFloat4 => { let combined = [vert.uv0[0], vert.uv0[1], vert.uv1[0], vert.uv1[1]]; - MDL::write_tangent(&mut cursor, &combined).ok()?; + MDL::write_tangent(&mut cursor, &combined)?; } _ => { panic!( @@ -1351,7 +1326,7 @@ impl WritableFile for MDL { }, VertexUsage::BiTangent => match element.vertex_type { VertexType::ByteFloat4 => { - MDL::write_tangent(&mut cursor, &vert.bitangent).ok()?; + MDL::write_tangent(&mut cursor, &vert.bitangent)?; } _ => { panic!( @@ -1365,9 +1340,8 @@ impl WritableFile for MDL { match element.vertex_type { VertexType::ByteFloat4 => { // TODO: restore - //MDL::write_tangent(&mut cursor, &vert.binormal).ok()?; - MDL::write_tangent(&mut cursor, &[0.0, 0.0, 0.0, 0.0]) - .ok()?; + //MDL::write_tangent(&mut cursor, &vert.binormal)?; + MDL::write_tangent(&mut cursor, &[0.0, 0.0, 0.0, 0.0])?; } _ => { panic!( @@ -1379,7 +1353,7 @@ impl WritableFile for MDL { } VertexUsage::Color => match element.vertex_type { VertexType::ByteFloat4 => { - MDL::write_byte_float4(&mut cursor, &vert.color).ok()?; + MDL::write_byte_float4(&mut cursor, &vert.color)?; } _ => { panic!( @@ -1392,20 +1366,18 @@ impl WritableFile for MDL { } } - cursor - .seek(SeekFrom::Start( - (self.file_header.index_offsets[l] - + (self.model_data.meshes[part.mesh_index as usize].start_index - * size_of::() as u32)) as u64, - )) - .ok()?; + cursor.seek(SeekFrom::Start( + (self.file_header.index_offsets[l] + + (self.model_data.meshes[part.mesh_index as usize].start_index + * size_of::() as u32)) as u64, + ))?; - cursor.write_le(&part.indices).ok()?; + cursor.write_le(&part.indices)?; } } } - Some(buffer) + Ok(buffer) } } diff --git a/src/mtrl.rs b/src/mtrl.rs index bc40465..ca1464c 100644 --- a/src/mtrl.rs +++ b/src/mtrl.rs @@ -473,15 +473,15 @@ pub struct Material { } impl ReadableFile for Material { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - let mat_data = MaterialData::read_options(&mut cursor, platform.endianness(), ()).ok()?; + let mat_data = MaterialData::read_options(&mut cursor, platform.endianness(), ())?; let mut texture_paths = vec![]; // Right now lets consider an empty string buffer to be an indicator for an invalid MTRL file. if mat_data.strings.is_empty() { - return None; + return Err(crate::Error::InvalidFile); } let mut offset = 0; @@ -523,7 +523,7 @@ impl ReadableFile for Material { let color_table = mat_data.color_table.clone(); let color_dye_table = mat_data.color_dye_table.clone(); - Some(Material { + Ok(Material { mat_data, shader_package_name, texture_paths, @@ -537,17 +537,16 @@ impl ReadableFile for Material { } impl WritableFile for Material { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); self.mat_data - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + .write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/obsb.rs b/src/obsb.rs index ee8172f..5535233 100644 --- a/src/obsb.rs +++ b/src/obsb.rs @@ -33,17 +33,21 @@ pub struct Obsb { } impl ReadableFile for Obsb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(cursor.position() as i64); - Obsb::read_options(&mut cursor, endianness, (&string_heap,)).ok() + Ok(Obsb::read_options( + &mut cursor, + endianness, + (&string_heap,), + )?) } } impl WritableFile for Obsb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -52,18 +56,15 @@ impl WritableFile for Obsb { // TODO: need dual pass let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; - string_heap - .write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + string_heap.write_options(&mut cursor, platform.endianness(), ())?; let unk_ending = &[0x0; 8]; - cursor.write_all(unk_ending).ok()?; + cursor.write_all(unk_ending)?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/pap.rs b/src/pap.rs index e80991a..f399f47 100644 --- a/src/pap.rs +++ b/src/pap.rs @@ -65,7 +65,7 @@ fn read_tmbs(num_animations: i16, start_position: u64) -> BinResult> { reader.seek(SeekFrom::Current(-4))?; let string_heap = StringHeap::from(-8); // FIXME: a hack i guess - tmbs.push(Tmb::read_options(reader, endian, (&string_heap,)).unwrap()); + tmbs.push(Tmb::read_options(reader, endian, (&string_heap,))?); } } @@ -100,23 +100,22 @@ pub struct Pap { } impl ReadableFile for Pap { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Pap::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Pap::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Pap { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/patch.rs b/src/patch.rs index 5e527a4..a7f8897 100755 --- a/src/patch.rs +++ b/src/patch.rs @@ -298,8 +298,7 @@ fn read_file_operation_data(file_size: u64) -> BinResult> { while data.len() < file_size as usize { data.append( - &mut read_data_block_patch(reader, Endian::Little) // TODO: don't hardcode to little - .unwrap(), + &mut read_data_block_patch(reader, Endian::Little)?, // TODO: don't hardcode to little, ); } @@ -424,7 +423,7 @@ pub struct EntryChunk { static WIPE_BUFFER: [u8; 1 << 16] = [0; 1 << 16]; -fn wipe(mut file: &File, length: usize) -> Result<(), PatchError> { +fn wipe(mut file: &File, length: usize) -> crate::Result<()> { let mut length: usize = length; while length > 0 { let num_bytes = min(WIPE_BUFFER.len(), length); @@ -435,16 +434,12 @@ fn wipe(mut file: &File, length: usize) -> Result<(), PatchError> { Ok(()) } -fn wipe_from_offset(mut file: &File, length: usize, offset: u64) -> Result<(), PatchError> { +fn wipe_from_offset(mut file: &File, length: usize, offset: u64) -> crate::Result<()> { file.seek(SeekFrom::Start(offset))?; wipe(file, length) } -fn write_empty_file_block_at( - mut file: &File, - offset: u64, - block_number: u64, -) -> Result<(), PatchError> { +fn write_empty_file_block_at(mut file: &File, offset: u64, block_number: u64) -> crate::Result<()> { wipe_from_offset(file, (block_number << 7) as usize, offset)?; file.seek(SeekFrom::Start(offset))?; @@ -458,10 +453,7 @@ fn write_empty_file_block_at( let file_size: i32 = 0; file.write_all(file_size.to_le_bytes().as_slice())?; - let num_blocks: i32 = (block_number - 1) - .try_into() - .ok() - .ok_or(PatchError::ParseError)?; + let num_blocks: i32 = (block_number - 1).try_into().unwrap_or_default(); file.write_all(num_blocks.to_le_bytes().as_slice())?; let used_blocks: i32 = 0; @@ -483,28 +475,6 @@ fn get_expansion_folder(id: u16) -> String { } } -#[derive(Debug)] -/// Errors emitted in the patching process -pub enum PatchError { - /// Failed to read parts of the file - InvalidPatchFile, - /// Failed to parse the patch format - ParseError, -} - -impl From for PatchError { - // TODO: implement specific PatchErrors for stuff like out of storage space. invalidpatchfile is a bad name for this - fn from(_io: std::io::Error) -> Self { - PatchError::InvalidPatchFile - } -} - -impl From for PatchError { - fn from(_: binrw::Error) -> Self { - PatchError::ParseError - } -} - fn recurse(path: impl AsRef) -> Vec { let Ok(entries) = read_dir(path) else { return vec![]; @@ -533,7 +503,7 @@ pub struct ZiPatch { impl ZiPatch { /// Applies a boot or a game patch to the specified _data_dir_. - pub fn apply(data_dir: &str, patch_path: &str) -> Result<(), PatchError> { + pub fn apply(data_dir: &str, patch_path: &str) -> crate::Result<()> { let mut file = File::open(patch_path)?; let file_length = file.metadata()?.len(); @@ -557,7 +527,9 @@ impl ZiPatch { let filename: PathBuf = [ PathBuf::from(data_dir), Self::dat_path( - target_info.as_ref().ok_or(PatchError::ParseError)?, + target_info + .as_ref() + .ok_or(crate::Error::TargetInfoMissing)?, // TODO: give more information for this error add.main_id, add.sub_id, add.file_id, @@ -566,7 +538,11 @@ impl ZiPatch { .iter() .collect(); - fs::create_dir_all(filename.parent().ok_or(PatchError::ParseError)?)?; + fs::create_dir_all(filename.parent().ok_or( + crate::Error::InvalidFilename { + path: filename.clone(), + }, + )?)?; let mut new_file = OpenOptions::new() .write(true) @@ -584,7 +560,9 @@ impl ZiPatch { let filename: PathBuf = [ PathBuf::from(data_dir), Self::dat_path( - target_info.as_ref().ok_or(PatchError::ParseError)?, + target_info + .as_ref() + .ok_or(crate::Error::TargetInfoMissing)?, delete.main_id, delete.sub_id, delete.file_id, @@ -609,7 +587,9 @@ impl ZiPatch { let filename: PathBuf = [ PathBuf::from(data_dir), Self::dat_path( - target_info.as_ref().ok_or(PatchError::ParseError)?, + target_info + .as_ref() + .ok_or(crate::Error::TargetInfoMissing)?, expand.main_id, expand.sub_id, expand.file_id, @@ -618,7 +598,11 @@ impl ZiPatch { .iter() .collect(); - fs::create_dir_all(filename.parent().ok_or(PatchError::ParseError)?)?; + fs::create_dir_all(filename.parent().ok_or( + crate::Error::InvalidFilename { + path: filename.clone(), + }, + )?)?; let new_file = OpenOptions::new() .write(true) @@ -637,13 +621,17 @@ impl ZiPatch { PathBuf::from(data_dir), match header.file_kind { TargetFileKind::Dat => Self::dat_path( - target_info.as_ref().ok_or(PatchError::ParseError)?, + target_info + .as_ref() + .ok_or(crate::Error::TargetInfoMissing)?, header.main_id, header.sub_id, header.file_id, ), TargetFileKind::Index => Self::index_path( - target_info.as_ref().ok_or(PatchError::ParseError)?, + target_info + .as_ref() + .ok_or(crate::Error::TargetInfoMissing)?, header.main_id, header.sub_id, header.file_id, @@ -653,7 +641,11 @@ impl ZiPatch { .iter() .collect(); - fs::create_dir_all(file_path.parent().ok_or(PatchError::ParseError)?)?; + fs::create_dir_all(file_path.parent().ok_or( + crate::Error::InvalidFilename { + path: file_path.clone(), + }, + )?)?; let mut new_file = OpenOptions::new() .write(true) @@ -671,7 +663,9 @@ impl ZiPatch { let file_path: PathBuf = [data_dir, &fop.path].iter().collect(); let parent_directory = - file_path.parent().ok_or(PatchError::ParseError)?; + file_path.parent().ok_or(crate::Error::InvalidFilename { + path: file_path.clone(), + })?; match fop.operation { SqpkFileOperation::AddFile => { @@ -766,8 +760,7 @@ impl ZiPatch { let len = header_decompress( &mut compressed_data, &mut decompressed_data, - ) - .ok_or(PatchError::ParseError)?; + )?; decompressed_data[..len as usize].to_vec() } }; @@ -780,7 +773,11 @@ impl ZiPatch { let filename: PathBuf = [data_dir, &entry.path].iter().collect(); // Sometimes, the patch asks for a directory it didn't make yet. - fs::create_dir_all(filename.parent().ok_or(PatchError::ParseError)?)?; + fs::create_dir_all(filename.parent().ok_or( + crate::Error::InvalidFilename { + path: filename.clone(), + }, + )?)?; std::fs::write(filename, data)?; } @@ -797,7 +794,7 @@ impl ZiPatch { } /// Creates a new ZiPatch describing the diff between `base_directory` and `new_directory`. - pub fn create(base_directory: &str, new_directory: &str) -> Option { + pub fn create(base_directory: &str, new_directory: &str) -> crate::Result { let mut buffer = ByteBuffer::new(); { @@ -805,7 +802,7 @@ impl ZiPatch { let mut writer = BufWriter::new(cursor); let header = PatchHeader {}; - header.write(&mut writer).ok()?; + header.write(&mut writer)?; let base_files = crate::patch::recurse(base_directory); let new_files = crate::patch::recurse(new_directory); @@ -851,16 +848,16 @@ impl ZiPatch { crc32: 0, }; - add_file_chunk.write(&mut writer).ok()?; + add_file_chunk.write(&mut writer)?; // reverse reading crc32 - writer.seek(SeekFrom::Current(-4)).ok()?; + writer.seek(SeekFrom::Current(-4))?; // add file data, dummy ver for now - write_data_block_patch(&mut writer, Endian::Little, file_data); // TODO: don't hardcode to little + write_data_block_patch(&mut writer, Endian::Little, file_data)?; // TODO: don't hardcode to little // re-apply crc32 - writer.seek(SeekFrom::Current(4)).ok()?; + writer.seek(SeekFrom::Current(4))?; } // Process deleted files @@ -888,7 +885,7 @@ impl ZiPatch { crc32: 0, }; - remove_file_chunk.write(&mut writer).ok()?; + remove_file_chunk.write(&mut writer)?; } let eof_chunk = PatchChunk { @@ -896,14 +893,14 @@ impl ZiPatch { chunk_type: ChunkType::EndOfFile, crc32: 0, }; - eof_chunk.write(&mut writer).ok()?; + eof_chunk.write(&mut writer)?; } - Some(buffer) + Ok(buffer) } /// List all patch chunks (or "operations") in this ZiPatch file. - pub fn list_operations(patch_path: &str) -> Result { + pub fn list_operations(patch_path: &str) -> crate::Result { let mut file = File::open(patch_path)?; let file_length = file.metadata()?.len(); @@ -1010,7 +1007,7 @@ mod tests { write(data_dir.clone() + "/test.patch", read(d).unwrap()).unwrap(); // Feeding it invalid data should not panic - let Err(PatchError::ParseError) = + let Err(crate::Error::Binrw(binrw::Error::AssertFail { .. })) = ZiPatch::apply(&data_dir.clone(), &(data_dir + "/test.patch")) else { panic!("Expecting a parse error!"); diff --git a/src/pbd.rs b/src/pbd.rs index 36b8414..469e17b 100644 --- a/src/pbd.rs +++ b/src/pbd.rs @@ -102,23 +102,26 @@ pub struct PreBoneDeformMatrices { } impl ReadableFile for PreBoneDeformer { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - PreBoneDeformer::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(PreBoneDeformer::read_options( + &mut cursor, + platform.endianness(), + (), + )?) } } impl WritableFile for PreBoneDeformer { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/pcb.rs b/src/pcb.rs index 5b649a5..076d69a 100644 --- a/src/pcb.rs +++ b/src/pcb.rs @@ -28,21 +28,17 @@ fn parse_resource_node_children( child1_offset: u32, child2_offset: u32, ) -> BinResult> { - let initial_position = reader.stream_position().unwrap(); + let initial_position = reader.stream_position()?; let struct_start = initial_position - ResourceNode::HEADER_SIZE as u64; let mut children = Vec::new(); if child1_offset != 0 { - reader - .seek(SeekFrom::Start(struct_start + child1_offset as u64)) - .unwrap(); + reader.seek(SeekFrom::Start(struct_start + child1_offset as u64))?; children.push(ResourceNode::read_le(reader)?); } if child2_offset != 0 { - reader - .seek(SeekFrom::Start(struct_start + child2_offset as u64)) - .unwrap(); + reader.seek(SeekFrom::Start(struct_start + child2_offset as u64))?; children.push(ResourceNode::read_le(reader)?); } @@ -138,23 +134,22 @@ pub struct Pcb { } impl ReadableFile for Pcb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Pcb::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Pcb::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Pcb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/pcblist.rs b/src/pcblist.rs index d87a7ff..a12e0bc 100644 --- a/src/pcblist.rs +++ b/src/pcblist.rs @@ -30,23 +30,26 @@ pub struct PcbListEntry { } impl ReadableFile for PcbList { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - PcbList::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(PcbList::read_options( + &mut cursor, + platform.endianness(), + (), + )?) } } impl WritableFile for PcbList { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/phyb.rs b/src/phyb.rs index 5b38698..a1c62fd 100644 --- a/src/phyb.rs +++ b/src/phyb.rs @@ -411,23 +411,22 @@ pub struct Phyb { } impl ReadableFile for Phyb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Phyb::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Phyb::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Phyb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/resource/mod.rs b/src/resource/mod.rs index c8755de..bd91b96 100644 --- a/src/resource/mod.rs +++ b/src/resource/mod.rs @@ -34,7 +34,7 @@ //! } //! //! impl Resource for SqPackResourceSpy { -//! fn read(&mut self, path: &str) -> Option { +//! fn read(&mut self, path: &str) -> physis::Result { //! todo!() //! } //! @@ -56,7 +56,7 @@ mod unpacked; pub use unpacked::UnpackedResource; use crate::{ - ByteBuffer, Error, ReadableFile, + ByteBuffer, ReadableFile, common::{Language, Platform}, excel::{Page, Sheet}, exd::EXD, @@ -111,7 +111,7 @@ pub trait Resource: Send + Sync + ClonableResource + 'static { /// let mut file = std::fs::File::create("root.exl").unwrap(); /// file.write(data.as_slice()).unwrap(); /// ``` - fn read(&mut self, path: &str) -> Option; + fn read(&mut self, path: &str) -> crate::Result; /// Checks if a file exists. /// @@ -144,23 +144,16 @@ pub trait Resource: Send + Sync + ClonableResource + 'static { pub fn generic_parsed( resource: &mut R, path: &str, -) -> Result { - if let Some(bytes) = resource.read(path) { - return F::from_existing(resource.platform(), &bytes).ok_or(Error::FileParsingFailed { - path: path.to_string(), - }); - } - - Err(Error::FileNotFound { - path: path.to_string(), - }) +) -> crate::Result { + let bytes = resource.read(path)?; + F::from_existing(resource.platform(), &bytes) } /// Read an excel sheet header by name (e.g. "Achievement"). You most likely want to use the method in `ResourceResolver.` pub fn generic_read_excel_sheet_header( resource: &mut R, name: &str, -) -> Result { +) -> crate::Result { let new_filename = name.to_lowercase(); let path = format!("exd/{new_filename}.exh"); @@ -174,7 +167,7 @@ pub fn generic_read_excel_sheet( exh: &EXH, name: &str, language: Language, -) -> Result { +) -> crate::Result { let mut pages = Vec::with_capacity(exh.header.page_count as usize); for page in 0..exh.header.page_count { let exd = generic_read_excel_exd(resource, name, exh, language, page as usize)?; @@ -190,7 +183,7 @@ pub fn generic_read_excel_sheet( /// Returns all known sheet names listed in the root list. You most likely want to use the method in `ResourceResolver.` pub fn generic_get_all_sheet_names( resource: &mut R, -) -> Result, Error> { +) -> crate::Result> { let root_exl = generic_parsed::(resource, "exd/root.exl")?; Ok(root_exl .entries @@ -205,7 +198,7 @@ fn generic_read_excel_exd( exh: &EXH, language: Language, page: usize, -) -> Result { +) -> crate::Result { let exd_path = format!( "exd/{}", EXD::calculate_filename(name, language, &exh.pages[page]) diff --git a/src/resource/resolver.rs b/src/resource/resolver.rs index b3540c4..1783acd 100644 --- a/src/resource/resolver.rs +++ b/src/resource/resolver.rs @@ -47,14 +47,16 @@ impl ResourceResolver { } // TODO: add documentation - pub fn read(&mut self, path: &str) -> Option { + pub fn read(&mut self, path: &str) -> crate::Result { for resolver in &mut self.resources { - if let Some(bytes) = resolver.read(path) { - return Some(bytes); + if let Ok(bytes) = resolver.read(path) { + return Ok(bytes); } } - None + Err(crate::Error::FileNotFound { + path: path.to_string(), + }) } /// Reads and parses the file located at `path`. This avoids having to call both `read` and `from_existing`. @@ -71,7 +73,7 @@ impl ResourceResolver { /// /// let exl = resolver.parsed::("exd/root.exl").unwrap(); /// ``` - pub fn parsed(&mut self, path: &str) -> Result { + pub fn parsed(&mut self, path: &str) -> crate::Result { self.execute_first_found( |resource| generic_parsed(resource, path), Error::FileNotFound { @@ -81,10 +83,10 @@ impl ResourceResolver { } /// Read an excel sheet header by name (e.g. "Achievement"). - pub fn read_excel_sheet_header(&mut self, name: &str) -> Result { + pub fn read_excel_sheet_header(&mut self, name: &str) -> crate::Result { self.execute_first_found( |resource| generic_read_excel_sheet_header(resource, name), - Error::Unknown, + Error::ResolverFailed, ) } @@ -94,16 +96,16 @@ impl ResourceResolver { exh: &EXH, name: &str, language: Language, - ) -> Result { + ) -> crate::Result { self.execute_first_found( |resource| generic_read_excel_sheet(resource, exh, name, language), - Error::Unknown, + Error::ResolverFailed, ) } /// Returns all known sheet names listed in the root list. - pub fn get_all_sheet_names(&mut self) -> Result, Error> { - self.execute_first_found(generic_get_all_sheet_names, Error::Unknown) + pub fn get_all_sheet_names(&mut self) -> crate::Result> { + self.execute_first_found(generic_get_all_sheet_names, Error::ResolverFailed) } // TODO: add documentation @@ -118,9 +120,9 @@ impl ResourceResolver { } /// Executes the given function `f`, continuing past "FileNotFound" errors and ultimately returns `error` if everything failed. - fn execute_first_found(&mut self, f: F, error: Error) -> Result + fn execute_first_found(&mut self, f: F, error: Error) -> crate::Result where - F: Fn(&mut dyn Resource) -> Result, + F: Fn(&mut dyn Resource) -> crate::Result, { for resource in &mut self.resources { let result = f(resource.as_mut()); diff --git a/src/resource/sqpack.rs b/src/resource/sqpack.rs index fcf58df..2dd07ff 100644 --- a/src/resource/sqpack.rs +++ b/src/resource/sqpack.rs @@ -191,7 +191,7 @@ impl SqPackResource { self.repositories.sort(); } - fn get_dat_file(&self, index_path: &str, data_file_id: u32) -> Option { + fn get_dat_file(&self, index_path: &str, data_file_id: u32) -> crate::Result { // Remove the index or index2 from the last bit of the path let dat_path = index_path.replace(".index2", ""); let dat_path = dat_path.replace(".index", ""); @@ -429,9 +429,13 @@ impl SqPackResource { } /// Reads a file based on an index hash and the index file you want to read from. - pub fn read_from_hash(&mut self, index_path: &Path, hash: Hash) -> Option { + pub fn read_from_hash(&mut self, index_path: &Path, hash: Hash) -> crate::Result { self.cache_index_file(index_path); - let index_file = self.get_index_file(index_path)?; + let index_file = self + .get_index_file(index_path) + .ok_or(crate::Error::FileNotFound { + path: index_path.display().to_string(), + })?; let slice = index_file.find_entry_from_hash(hash); match slice { @@ -440,7 +444,7 @@ impl SqPackResource { self.get_dat_file(&index_path.to_string_lossy(), entry.data_file_id.into())?; dat_file.read_from_offset(entry.offset) } - None => None, + None => Err(crate::Error::HashNotFound { hash }), } } @@ -471,7 +475,7 @@ impl SqPackResource { } impl Resource for SqPackResource { - fn read(&mut self, path: &str) -> Option { + fn read(&mut self, path: &str) -> crate::Result { let slice = self.find_entry(path); match slice { Some((entry, index_path)) => { @@ -480,7 +484,9 @@ impl Resource for SqPackResource { dat_file.read_from_offset(entry.offset) } - None => None, + None => Err(Error::FileNotFound { + path: path.to_string(), + }), } } diff --git a/src/resource/unpacked.rs b/src/resource/unpacked.rs index f24b6c0..cd57c81 100644 --- a/src/resource/unpacked.rs +++ b/src/resource/unpacked.rs @@ -25,11 +25,11 @@ impl UnpackedResource { } impl Resource for UnpackedResource { - fn read(&mut self, path: &str) -> Option { + fn read(&mut self, path: &str) -> crate::Result { let mut new_path = PathBuf::from(&self.base_directory); new_path.push(path.to_lowercase()); - std::fs::read(new_path).ok() + Ok(std::fs::read(new_path)?) } fn exists(&mut self, path: &str) -> bool { @@ -55,8 +55,8 @@ mod tests { fn read_files() { let mut data = common_setup_data(); - assert!(data.read("empty_planlive.lgb").is_some()); - assert!(data.read("non_existent.lgb").is_none()); + assert!(data.read("empty_planlive.lgb").is_ok()); + assert!(data.read("non_existent.lgb").is_err()); } #[test] diff --git a/src/scd.rs b/src/scd.rs index 3022349..2d89197 100644 --- a/src/scd.rs +++ b/src/scd.rs @@ -48,23 +48,22 @@ pub struct Scd { } impl ReadableFile for Scd { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Scd::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Scd::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Scd { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/scn.rs b/src/scn.rs index 841a0cb..12802a3 100644 --- a/src/scn.rs +++ b/src/scn.rs @@ -479,7 +479,7 @@ fn layers_from_offsets(offsets: &Vec, string_heap: &StringHeap) -> BinResul reader.seek(SeekFrom::Start(base_offset + layer_offset))?; // TODO: need separate data heap eventually - layers.push(Layer::read(endian, reader, string_heap, string_heap).unwrap()); + layers.push(Layer::read(endian, reader, string_heap, string_heap)?); } Ok(layers) diff --git a/src/sgb.rs b/src/sgb.rs index 99ca415..2e08ea3 100644 --- a/src/sgb.rs +++ b/src/sgb.rs @@ -28,29 +28,28 @@ pub struct Sgb { impl ReadableFile for Sgb { /// Read an existing file. - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let endianness = platform.endianness(); let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(cursor.position() as i64); - Sgb::read_options(&mut cursor, endianness, (&string_heap,)).ok() + Ok(Sgb::read_options(&mut cursor, endianness, (&string_heap,))?) } } impl WritableFile for Sgb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); let mut string_heap = StringHeap::from(cursor.position() as i64); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; // TODO: write string heap } - Some(buffer) + Ok(buffer) } } diff --git a/src/shcd.rs b/src/shcd.rs index 033c60d..e29bde5 100644 --- a/src/shcd.rs +++ b/src/shcd.rs @@ -60,23 +60,22 @@ pub struct SHCD { } impl ReadableFile for SHCD { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - SHCD::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(SHCD::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for SHCD { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/shpk.rs b/src/shpk.rs index 6ce0c2f..e16d33a 100644 --- a/src/shpk.rs +++ b/src/shpk.rs @@ -249,10 +249,9 @@ pub struct ShaderPackage { const SELECTOR_MULTIPLER: u32 = 31; impl ReadableFile for ShaderPackage { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - let mut package = - ShaderPackage::read_options(&mut cursor, platform.endianness(), ()).ok()?; + let mut package = ShaderPackage::read_options(&mut cursor, platform.endianness(), ())?; for (i, node) in package.nodes.iter().enumerate() { package.node_selectors.push((node.selector, i as u32)); @@ -261,21 +260,20 @@ impl ReadableFile for ShaderPackage { package.node_selectors.push((alias.selector, alias.node)); } - Some(package) + Ok(package) } } impl WritableFile for ShaderPackage { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/skeleton.rs b/src/skeleton.rs index 5c550e0..fca47d9 100644 --- a/src/skeleton.rs +++ b/src/skeleton.rs @@ -76,10 +76,10 @@ pub struct Skeleton { } impl ReadableFile for Skeleton { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - let sklb = SKLB::read_options(&mut cursor, platform.endianness(), ()).ok()?; + let sklb = SKLB::read_options(&mut cursor, platform.endianness(), ())?; let root = HavokBinaryTagFileReader::read(&sklb.raw_data); let raw_animation_container = root.find_object_by_type("hkaAnimationContainer"); @@ -107,7 +107,7 @@ impl ReadableFile for Skeleton { }); } - Some(skeleton) + Ok(skeleton) } } diff --git a/src/skp.rs b/src/skp.rs index 86e6fe1..ecb438b 100644 --- a/src/skp.rs +++ b/src/skp.rs @@ -31,23 +31,22 @@ pub struct Skp { } impl ReadableFile for Skp { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Skp::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Skp::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Skp { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/sqpack/data.rs b/src/sqpack/data.rs index 561f14a..07b5faa 100644 --- a/src/sqpack/data.rs +++ b/src/sqpack/data.rs @@ -216,10 +216,10 @@ pub struct SqPackData { impl SqPackData { /// Creates a new reference to an existing dat file. - pub fn from_existing(platform: Platform, path: &str) -> Option { - Some(Self { + pub fn from_existing(platform: Platform, path: &str) -> crate::Result { + Ok(Self { platform, - file: std::fs::File::open(path).ok()?, + file: std::fs::File::open(path)?, }) } @@ -227,20 +227,17 @@ impl SqPackData { /// by the function. /// /// If the block of data is successfully parsed, it returns the file data - otherwise is None. - pub fn read_from_offset(&mut self, offset: u64) -> Option { - self.file - .seek(SeekFrom::Start(offset)) - .expect("Unable to find offset in file."); + pub fn read_from_offset(&mut self, offset: u64) -> crate::Result { + self.file.seek(SeekFrom::Start(offset))?; let file_info = FileInfo::read_options( &mut self.file, self.platform.endianness(), (&self.platform,), - ) - .ok()?; + )?; match file_info.file_type { - FileType::Empty => None, + FileType::Empty => Err(crate::Error::InvalidFile), FileType::Standard => Self::read_standard_file( &mut self.file, self.platform.endianness(), @@ -268,11 +265,11 @@ impl SqPackData { pub fn read_from_reader( stream: &mut R, platform: Platform, - ) -> Option { - let file_info = FileInfo::read_options(stream, platform.endianness(), (&platform,)).ok()?; + ) -> crate::Result { + let file_info = FileInfo::read_options(stream, platform.endianness(), (&platform,))?; match file_info.file_type { - FileType::Empty => None, + FileType::Empty => Err(crate::Error::InvalidFile), FileType::Standard => { Self::read_standard_file(stream, platform.endianness(), 0, &file_info) } @@ -289,13 +286,14 @@ impl SqPackData { endian: Endian, offset: u64, file_info: &FileInfo, - ) -> Option { - let standard_file_info = file_info.standard_info.as_ref()?; + ) -> crate::Result { + assert_eq!(file_info.file_type, FileType::Standard); + let standard_file_info = file_info.standard_info.as_ref().unwrap(); // NOTE: This should never be called if the FileType isn't Standard. let mut blocks: Vec = Vec::with_capacity(standard_file_info.num_blocks as usize); for _ in 0..standard_file_info.num_blocks { - blocks.push(Block::read_options(stream, endian, ()).ok()?); + blocks.push(Block::read_options(stream, endian, ())?); } let mut data: Vec = Vec::with_capacity(file_info.file_size as usize); @@ -303,17 +301,14 @@ impl SqPackData { let starting_position = offset + (file_info.size as u64); for i in 0..standard_file_info.num_blocks { - data.append( - &mut read_data_block( - stream, - endian, - starting_position + (blocks[i as usize].offset as u64), - ) - .expect("Failed to read data block."), - ); + data.append(&mut read_data_block( + stream, + endian, + starting_position + (blocks[i as usize].offset as u64), + )?); } - Some(data) + Ok(data) } /// Reads a model file block. @@ -322,8 +317,9 @@ impl SqPackData { endian: Endian, offset: u64, file_info: &FileInfo, - ) -> Option { - let model_file_info = file_info.model_info.as_ref()?; + ) -> crate::Result { + assert_eq!(file_info.file_type, FileType::Model); + let model_file_info = file_info.model_info.as_ref().unwrap(); // NOTE: This should never be called if the FileType isn't Model. let mut buffer = Cursor::new(Vec::new()); @@ -331,12 +327,10 @@ impl SqPackData { let total_blocks = model_file_info.num.total(); - let compressed_block_sizes: Vec = stream - .read_type_args( - endian, - VecArgs::builder().count(total_blocks as usize).finalize(), - ) - .ok()?; + let compressed_block_sizes: Vec = stream.read_type_args( + endian, + VecArgs::builder().count(total_blocks as usize).finalize(), + )?; let mut current_block = 0; @@ -347,35 +341,30 @@ impl SqPackData { let mut index_data_sizes: [u32; 3] = [0; 3]; // start writing at 0x44 - buffer.seek(SeekFrom::Start(0x44)).ok()?; + buffer.seek(SeekFrom::Start(0x44))?; - stream - .seek(SeekFrom::Start( - base_offset + (model_file_info.offset.stack_size as u64), - )) - .ok()?; + stream.seek(SeekFrom::Start( + base_offset + (model_file_info.offset.stack_size as u64), + ))?; // read from stack blocks - let mut read_model_blocks = |offset: u64, size: usize| -> Option { - stream.seek(SeekFrom::Start(base_offset + offset)).ok()?; + let mut read_model_blocks = |offset: u64, size: usize| -> crate::Result { + stream.seek(SeekFrom::Start(base_offset + offset))?; let stack_start = buffer.position(); for _ in 0..size { - let last_pos = &stream.stream_position().ok()?; + let last_pos = &stream.stream_position()?; - let data = - read_data_block(stream, endian, *last_pos).expect("Unable to read block data."); + let data = read_data_block(stream, endian, *last_pos)?; // write to buffer - buffer.write_all(data.as_slice()).ok()?; + buffer.write_all(data.as_slice())?; - stream - .seek(SeekFrom::Start( - last_pos + (compressed_block_sizes[current_block] as u64), - )) - .ok()?; + stream.seek(SeekFrom::Start( + last_pos + (compressed_block_sizes[current_block] as u64), + ))?; current_block += 1; } - Some(buffer.position() - stack_start) + Ok(buffer.position() - stack_start) }; let stack_size = read_model_blocks( @@ -387,44 +376,41 @@ impl SqPackData { model_file_info.num.runtime_size as usize, )? as u32; - let mut process_model_data = - |i: usize, - size: u32, - offset: u32, - offsets: &mut [u32; 3], - data_sizes: &mut [u32; 3]| { - if size != 0 { - let current_vertex_offset = buffer.position() as u32; - if i == 0 || current_vertex_offset != offsets[i - 1] { - offsets[i] = current_vertex_offset; - } else { - offsets[i] = 0; - } - - stream - .seek(SeekFrom::Start(base_offset + (offset as u64))) - .ok(); - - for _ in 0..size { - let last_pos = stream.stream_position().unwrap(); - - let data = read_data_block(stream, endian, last_pos) - .expect("Unable to read raw model block!"); - - buffer - .write_all(data.as_slice()) - .expect("Unable to write to memory buffer!"); - - data_sizes[i] += data.len() as u32; - stream - .seek(SeekFrom::Start( - last_pos + (compressed_block_sizes[current_block] as u64), - )) - .expect("Unable to seek properly."); - current_block += 1; - } + let mut process_model_data = |i: usize, + size: u32, + offset: u32, + offsets: &mut [u32; 3], + data_sizes: &mut [u32; 3]| + -> crate::Result<()> { + if size != 0 { + let current_vertex_offset = buffer.position() as u32; + if i == 0 || current_vertex_offset != offsets[i - 1] { + offsets[i] = current_vertex_offset; + } else { + offsets[i] = 0; + } + + stream + .seek(SeekFrom::Start(base_offset + (offset as u64))) + .ok(); + + for _ in 0..size { + let last_pos = stream.stream_position().unwrap(); + + let data = read_data_block(stream, endian, last_pos)?; + + buffer.write_all(data.as_slice())?; + + data_sizes[i] += data.len() as u32; + stream.seek(SeekFrom::Start( + last_pos + (compressed_block_sizes[current_block] as u64), + ))?; + current_block += 1; } - }; + } + + Ok(()) + }; // process all 3 lods for i in 0..3 { @@ -435,7 +421,7 @@ impl SqPackData { model_file_info.offset.vertex_buffer_size[i], &mut vertex_data_offsets, &mut vertex_data_sizes, - ); + )?; // TODO: process edges @@ -446,7 +432,7 @@ impl SqPackData { model_file_info.offset.index_buffer_size[i], &mut index_data_offsets, &mut index_data_sizes, - ); + )?; } let header = ModelFileHeader { @@ -464,11 +450,11 @@ impl SqPackData { has_edge_geometry: model_file_info.edge_geometry_enabled, }; - buffer.seek(SeekFrom::Start(0)).ok()?; + buffer.seek(SeekFrom::Start(0))?; - header.write_options(&mut buffer, endian, ()).ok()?; + header.write_options(&mut buffer, endian, ())?; - Some(buffer.into_inner()) + Ok(buffer.into_inner()) } /// Reads a texture file block. @@ -477,26 +463,25 @@ impl SqPackData { endian: Endian, offset: u64, file_info: &FileInfo, - ) -> Option { - let texture_file_info = file_info.texture_info.as_ref()?; + ) -> crate::Result { + assert_eq!(file_info.file_type, FileType::Texture); + let texture_file_info = file_info.texture_info.as_ref().unwrap(); // NOTE: This should never be called if the FileType isn't Texture. let mut data: Vec = Vec::with_capacity(file_info.file_size as usize); // write the header if it exists let mipmap_size = texture_file_info.lods[0].compressed_size; if mipmap_size != 0 { - let original_pos = stream.stream_position().ok()?; + let original_pos = stream.stream_position()?; - stream - .seek(SeekFrom::Start(offset + file_info.size as u64)) - .ok()?; + stream.seek(SeekFrom::Start(offset + file_info.size as u64))?; let mut header = vec![0u8; texture_file_info.lods[0].compressed_offset as usize]; - stream.read_exact(&mut header).ok()?; + stream.read_exact(&mut header)?; data.append(&mut header); - stream.seek(SeekFrom::Start(original_pos)).ok()?; + stream.seek(SeekFrom::Start(original_pos))?; } for i in 0..texture_file_info.num_blocks { @@ -506,7 +491,7 @@ impl SqPackData { + (file_info.size as u64); for _ in 0..texture_file_info.lods[i as usize].block_count { - let original_pos = stream.stream_position().ok()?; + let original_pos = stream.stream_position()?; data.append(&mut read_data_block( &mut stream, @@ -514,13 +499,13 @@ impl SqPackData { running_block_total, )?); - stream.seek(SeekFrom::Start(original_pos)).ok()?; + stream.seek(SeekFrom::Start(original_pos))?; - running_block_total += stream.read_type_args::(endian, ()).ok()? as u64; + running_block_total += stream.read_type_args::(endian, ())? as u64; } } - Some(data) + Ok(data) } } @@ -539,6 +524,6 @@ mod tests { let mut dat = SqPackData::from_existing(Platform::Win32, d.to_str().unwrap()).unwrap(); // Reading invalid data should just be nothing, but no panics - assert!(dat.read_from_offset(0).is_none()); + assert!(dat.read_from_offset(0).is_err()); } } diff --git a/src/sqpack/mod.rs b/src/sqpack/mod.rs index 7a8bcfa..caef0c6 100644 --- a/src/sqpack/mod.rs +++ b/src/sqpack/mod.rs @@ -3,7 +3,7 @@ use std::io::{Read, Seek, SeekFrom, Write}; -use binrw::{BinRead, BinWrite, Endian, binrw}; +use binrw::{BinRead, BinResult, BinWrite, Endian, binrw}; use data::{BlockHeader, CompressionMode}; use crate::common::Platform; @@ -61,10 +61,10 @@ pub(crate) fn read_data_block( buf: &mut T, endianness: Endian, starting_position: u64, -) -> Option> { - buf.seek(SeekFrom::Start(starting_position)).ok()?; +) -> crate::Result> { + buf.seek(SeekFrom::Start(starting_position))?; - let block_header = BlockHeader::read_options(buf, endianness, ()).unwrap(); + let block_header = BlockHeader::read_options(buf, endianness, ())?; match block_header.compression { CompressionMode::Compressed { @@ -72,20 +72,18 @@ pub(crate) fn read_data_block( decompressed_length, } => { let mut compressed_data: Vec = vec![0; compressed_length as usize]; - buf.read_exact(&mut compressed_data).ok()?; + buf.read_exact(&mut compressed_data)?; let mut decompressed_data: Vec = vec![0; decompressed_length as usize]; - if !no_header_decompress(&mut compressed_data, &mut decompressed_data) { - return None; - } + no_header_decompress(&mut compressed_data, &mut decompressed_data)?; - Some(decompressed_data) + Ok(decompressed_data) } CompressionMode::Uncompressed { file_size } => { let mut local_data: Vec = vec![0; file_size as usize]; - buf.read_exact(&mut local_data).ok()?; + buf.read_exact(&mut local_data)?; - Some(local_data) + Ok(local_data) } } } @@ -94,8 +92,8 @@ pub(crate) fn read_data_block( pub(crate) fn read_data_block_patch( buf: &mut T, endianness: Endian, -) -> Option> { - let block_header = BlockHeader::read_options(buf, endianness, ()).unwrap(); +) -> BinResult> { + let block_header = BlockHeader::read_options(buf, endianness, ())?; match block_header.compression { CompressionMode::Compressed { @@ -106,27 +104,29 @@ pub(crate) fn read_data_block_patch( ((compressed_length as usize + 143) & 0xFFFFFF80) - (block_header.size as usize); let mut compressed_data: Vec = vec![0; compressed_length]; - buf.read_exact(&mut compressed_data).ok()?; + buf.read_exact(&mut compressed_data)?; let mut decompressed_data: Vec = vec![0; decompressed_length as usize]; - if !no_header_decompress(&mut compressed_data, &mut decompressed_data) { - return None; + if let Err(err) = no_header_decompress(&mut compressed_data, &mut decompressed_data) { + return Err(binrw::Error::Custom { + pos: 0, + err: Box::new(err), + }); } - Some(decompressed_data) + Ok(decompressed_data) } CompressionMode::Uncompressed { file_size } => { let new_file_size: usize = (file_size as usize + 143) & 0xFFFFFF80; let mut local_data: Vec = vec![0; file_size as usize]; - buf.read_exact(&mut local_data).ok()?; + buf.read_exact(&mut local_data)?; buf.seek(SeekFrom::Current( (new_file_size - block_header.size as usize - file_size as usize) as i64, - )) - .ok()?; + ))?; - Some(local_data) + Ok(local_data) } } } @@ -135,7 +135,7 @@ pub(crate) fn write_data_block_patch( writer: &mut T, endianness: Endian, data: Vec, -) { +) -> crate::Result<()> { let new_file_size: usize = (data.len() + 143) & 0xFFFFFF80; // This only adds uncompressed data for now, to simplify implementation @@ -146,7 +146,9 @@ pub(crate) fn write_data_block_patch( file_size: data.len() as i32, }, }; - block_header.write_options(writer, endianness, ()).unwrap(); + block_header.write_options(writer, endianness, ())?; + + data.write(writer)?; - data.write(writer).unwrap(); + Ok(()) } diff --git a/src/stm.rs b/src/stm.rs index 9dcde8f..d0c2212 100644 --- a/src/stm.rs +++ b/src/stm.rs @@ -225,7 +225,7 @@ fn read_entries>( ) -> BinResult> { let mut entries = HashMap::with_capacity(keys.len()); - let start_position = reader.stream_position().unwrap(); + let start_position = reader.stream_position()?; for (key, offset) in keys.iter().zip(offsets) { reader.seek(SeekFrom::Start(start_position + *offset as u64 * 2))?; @@ -238,23 +238,22 @@ fn read_entries>( } impl ReadableFile for Stm { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Stm::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Stm::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Stm { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/string_heap.rs b/src/string_heap.rs index 05f77e3..b258778 100644 --- a/src/string_heap.rs +++ b/src/string_heap.rs @@ -73,7 +73,7 @@ impl HeapPointer { #[binrw::parser(reader)] pub(crate) fn read_pointer_pos() -> BinResult { - Ok(reader.stream_position().unwrap()) + Ok(reader.stream_position()?) } impl StringHeap { diff --git a/src/svb.rs b/src/svb.rs index 2e4bee8..8469b6a 100644 --- a/src/svb.rs +++ b/src/svb.rs @@ -66,23 +66,22 @@ pub struct SvcEntry { } impl ReadableFile for Svb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Svb::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Svb::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Svb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/tera.rs b/src/tera.rs index 45554e1..4af7c68 100644 --- a/src/tera.rs +++ b/src/tera.rs @@ -44,23 +44,26 @@ pub struct Terrain { } impl ReadableFile for Terrain { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Terrain::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Terrain::read_options( + &mut cursor, + platform.endianness(), + (), + )?) } } impl WritableFile for Terrain { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/tex.rs b/src/tex.rs index 48449b8..a50f8f2 100644 --- a/src/tex.rs +++ b/src/tex.rs @@ -130,23 +130,22 @@ pub struct Texture { type DecodeFunction = fn(&[u8], usize, usize, &mut [u32]) -> Result<(), &'static str>; impl ReadableFile for Texture { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Self::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Self::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Texture { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/tmb.rs b/src/tmb.rs index 97d707a..954e3ef 100644 --- a/src/tmb.rs +++ b/src/tmb.rs @@ -53,12 +53,10 @@ pub(crate) fn read_timeline_list_2(struct_offset: i64) -> BinResult Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); let string_heap = StringHeap::from(0); - Tmb::read_options(&mut cursor, platform.endianness(), (&string_heap,)).ok() + Ok(Tmb::read_options( + &mut cursor, + platform.endianness(), + (&string_heap,), + )?) } } impl WritableFile for Tmb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); let mut string_heap = StringHeap::from(0); - self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,)) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), (&mut string_heap,))?; // TODO: write string heap } - Some(buffer) + Ok(buffer) } } diff --git a/src/uld.rs b/src/uld.rs index 77183d6..7fd3539 100644 --- a/src/uld.rs +++ b/src/uld.rs @@ -318,23 +318,22 @@ pub struct Uld { } impl ReadableFile for Uld { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Uld::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Uld::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Uld { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } } diff --git a/src/uwb.rs b/src/uwb.rs index e1172d4..84d0f26 100644 --- a/src/uwb.rs +++ b/src/uwb.rs @@ -53,23 +53,22 @@ impl Uwc { } impl ReadableFile for Uwb { - fn from_existing(platform: Platform, buffer: ByteSpan) -> Option { + fn from_existing(platform: Platform, buffer: ByteSpan) -> crate::Result { let mut cursor = Cursor::new(buffer); - Uwb::read_options(&mut cursor, platform.endianness(), ()).ok() + Ok(Uwb::read_options(&mut cursor, platform.endianness(), ())?) } } impl WritableFile for Uwb { - fn write_to_buffer(&self, platform: Platform) -> Option { + fn write_to_buffer(&self, platform: Platform) -> crate::Result { let mut buffer = ByteBuffer::new(); { let mut cursor = Cursor::new(&mut buffer); - self.write_options(&mut cursor, platform.endianness(), ()) - .ok()?; + self.write_options(&mut cursor, platform.endianness(), ())?; } - Some(buffer) + Ok(buffer) } }