diff --git a/psoutils/src/quest.rs b/psoutils/src/quest.rs index 6728a00..61a26f6 100644 --- a/psoutils/src/quest.rs +++ b/psoutils/src/quest.rs @@ -4,6 +4,9 @@ use thiserror::Error; use crate::quest::bin::{QuestBin, QuestBinError}; use crate::quest::dat::{QuestDat, QuestDatError}; +use crate::quest::qst::{QuestQst, QuestQstError}; +use crate::text::Language; +use byteorder::WriteBytesExt; pub mod bin; pub mod dat; @@ -19,6 +22,9 @@ pub enum QuestError { #[error("Error processing quest dat")] QuestDatError(#[from] QuestDatError), + + #[error("Error processing quest qst")] + QuestQstError(#[from] QuestQstError), } pub struct Quest { @@ -27,20 +33,191 @@ pub struct Quest { } impl Quest { - pub fn from_compressed_bindat(bin_path: &Path, dat_path: &Path) -> Result { - let bin = QuestBin::from_compressed_file(bin_path)?; - let dat = QuestDat::from_compressed_file(dat_path)?; + pub fn from_bindat_files(bin_path: &Path, dat_path: &Path) -> Result { + // try to load bin and dat files each as compressed files first as that is the normal + // format that these are stored as. if that fails, then try one more time for each one + // to load as an uncompressed file. if that fails too, return the error + + let bin = match QuestBin::from_compressed_file(bin_path) { + Err(QuestBinError::PrsCompressionError(_)) => { + QuestBin::from_uncompressed_file(bin_path)? + } + Err(e) => return Err(QuestError::QuestBinError(e)), + Ok(bin) => bin, + }; + + let dat = match QuestDat::from_compressed_file(dat_path) { + Err(QuestDatError::PrsCompressionError(_)) => { + QuestDat::from_uncompressed_file(dat_path)? + } + Err(e) => return Err(QuestError::QuestDatError(e)), + Ok(dat) => dat, + }; Ok(Quest { bin, dat }) } - pub fn from_uncompressed_bindat(bin_path: &Path, dat_path: &Path) -> Result { - let bin = QuestBin::from_uncompressed_file(bin_path)?; - let dat = QuestDat::from_uncompressed_file(dat_path)?; + pub fn from_qst_file(path: &Path) -> Result { + let qst = QuestQst::from_file(path)?; + Self::from_qst(qst) + } + + pub fn from_qst(qst: QuestQst) -> Result { + let bin = qst.extract_bin()?; + let dat = qst.extract_dat()?; Ok(Quest { bin, dat }) } + + pub fn as_qst(&self) -> Result { + Ok(QuestQst::from_bindat(&self.bin, &self.dat)?) + } + + pub fn write_as_qst_bytes(&self, writer: &mut T) -> Result<(), QuestError> { + let qst = self.as_qst()?; + Ok(qst.write_bytes(writer)?) + } + + pub fn to_qst_file(&self, path: &Path) -> Result<(), QuestError> { + let qst = QuestQst::from_bindat(&self.bin, &self.dat)?; + Ok(qst.to_file(path)?) + } + + pub fn to_compressed_bindat_files( + &self, + bin_path: &Path, + dat_path: &Path, + ) -> Result<(), QuestError> { + self.bin.to_compressed_file(bin_path)?; + self.dat.to_compressed_file(dat_path)?; + Ok(()) + } + + pub fn to_uncompressed_bindat_files( + &self, + bin_path: &Path, + dat_path: &Path, + ) -> Result<(), QuestError> { + self.bin.to_uncompressed_file(bin_path)?; + self.dat.to_uncompressed_file(dat_path)?; + Ok(()) + } + + pub fn name(&self) -> &String { + &self.bin.header.name + } + + pub fn short_description(&self) -> &String { + &self.bin.header.short_description + } + + pub fn long_description(&self) -> &String { + &self.bin.header.long_description + } + + pub fn language(&self) -> Language { + self.bin.header.language + } + + pub fn is_download(&self) -> bool { + self.bin.header.is_download + } + + pub fn set_is_download(&mut self, value: bool) { + self.bin.header.is_download = value + } + + pub fn quest_number(&self) -> u8 { + self.bin.header.quest_number() + } + + pub fn quest_number_u16(&self) -> u16 { + self.bin.header.quest_number_u16() + } + + pub fn episode(&self) -> u8 { + self.bin.header.episode() + } } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + use claim::*; + use tempfile::*; + + #[test] + pub fn can_load_from_compressed_bindat_files() { + let bin_path = Path::new("test-assets/q058-ret-gc.bin"); + let dat_path = Path::new("test-assets/q058-ret-gc.dat"); + assert_ok!(Quest::from_bindat_files(bin_path, dat_path)); + } + + #[test] + pub fn can_load_from_uncompressed_bindat_files() { + let bin_path = Path::new("test-assets/q058-ret-gc.uncompressed.bin"); + let dat_path = Path::new("test-assets/q058-ret-gc.uncompressed.dat"); + assert_ok!(Quest::from_bindat_files(bin_path, dat_path)); + } + + #[test] + pub fn can_load_from_offline_qst_file() { + let path = Path::new("test-assets/q058-ret-gc.offline.qst"); + assert_ok!(Quest::from_qst_file(path)); + } + + #[test] + pub fn can_load_from_online_qst_file() { + let path = Path::new("test-assets/q058-ret-gc.online.qst"); + assert_ok!(Quest::from_qst_file(path)); + } + + #[test] + pub fn can_create_from_qst_struct() { + let qst = QuestQst::from_file(Path::new("test-assets/q058-ret-gc.online.qst")).unwrap(); + assert_ok!(Quest::from_qst(qst)); + } + + #[test] + pub fn can_save_to_compressed_bindat_files() -> Result<(), QuestError> { + let quest = Quest::from_bindat_files( + Path::new("test-assets/q058-ret-gc.bin"), + Path::new("test-assets/q058-ret-gc.dat"), + )?; + let tmp_dir = TempDir::new()?; + let bin_save_path = tmp_dir.path().join("quest58.bin"); + let dat_save_path = tmp_dir.path().join("quest58.dat"); + assert_ok!(quest.to_compressed_bindat_files(&bin_save_path, &dat_save_path)); + assert_ok!(QuestBin::from_compressed_file(&bin_save_path)); + assert_ok!(QuestDat::from_compressed_file(&dat_save_path)); + Ok(()) + } + + #[test] + pub fn can_save_to_uncompressed_bindat_files() -> Result<(), QuestError> { + let quest = Quest::from_bindat_files( + Path::new("test-assets/q058-ret-gc.bin"), + Path::new("test-assets/q058-ret-gc.dat"), + )?; + let tmp_dir = TempDir::new()?; + let bin_save_path = tmp_dir.path().join("quest58.bin"); + let dat_save_path = tmp_dir.path().join("quest58.dat"); + assert_ok!(quest.to_uncompressed_bindat_files(&bin_save_path, &dat_save_path)); + assert_ok!(QuestBin::from_uncompressed_file(&bin_save_path)); + assert_ok!(QuestDat::from_uncompressed_file(&dat_save_path)); + Ok(()) + } + + #[test] + pub fn can_save_to_qst_file() -> Result<(), QuestError> { + let quest = Quest::from_bindat_files( + Path::new("test-assets/q058-ret-gc.bin"), + Path::new("test-assets/q058-ret-gc.dat"), + )?; + let tmp_dir = TempDir::new()?; + let qst_save_path = tmp_dir.path().join("quest58.qst"); + assert_ok!(quest.to_qst_file(&qst_save_path)); + assert_ok!(QuestQst::from_file(&qst_save_path)); + Ok(()) + } +}