From f734b0f95b4736ea313e62bc0ad3f0b8d7690c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Sun, 7 May 2023 13:18:38 +0200 Subject: [PATCH] add archive tests --- rust/springylib/src/archive/mod.rs | 112 +++++++++++++++--- .../src/archive/{standard.rs => v1.rs} | 0 rust/springylib/src/archive/v1a.dat | Bin 0 -> 256 bytes rust/springylib/src/archive/v1b.dat | Bin 0 -> 320 bytes .../src/archive/{mhjnr.rs => v2.rs} | 0 rust/springylib/src/archive/v2a.dat | Bin 0 -> 320 bytes 6 files changed, 93 insertions(+), 19 deletions(-) rename rust/springylib/src/archive/{standard.rs => v1.rs} (100%) create mode 100644 rust/springylib/src/archive/v1a.dat create mode 100644 rust/springylib/src/archive/v1b.dat rename rust/springylib/src/archive/{mhjnr.rs => v2.rs} (100%) create mode 100644 rust/springylib/src/archive/v2a.dat diff --git a/rust/springylib/src/archive/mod.rs b/rust/springylib/src/archive/mod.rs index 8df3e9e..18feca9 100644 --- a/rust/springylib/src/archive/mod.rs +++ b/rust/springylib/src/archive/mod.rs @@ -5,8 +5,8 @@ use std::io::{Read, Seek}; use std::ops::Deref; pub mod error; -mod mhjnr; -mod standard; +mod v1; +mod v2; use crate::archive::error::{Error, Result}; @@ -14,6 +14,7 @@ use crate::archive::error::{Error, Result}; pub struct Archive(HashMap); /// Pointer to the file inside the archive +#[derive(Debug, PartialEq)] pub struct FilePointer { pub position: usize, pub length: usize, @@ -34,17 +35,17 @@ impl From> for Archive { } /// These are all slightly divergent data layouts -enum ArchiveKind { +pub enum ArchiveKind { /// Appears in a variety of Moorhuhn Shoot 'em Up /// games, starting with Moorhuhn Winter. /// - /// The name can have a max length of 0x30, however the header + /// The name can have a max length of 0x30/0x40, however the header /// does not store the amount of files and instead is delimited /// by a final entry with the name `****` /// - /// File Entries have a max path length of 0x30 with a total entry + /// File Entries have a max path length of 0x30/0x40 with a total entry /// size of 0x40. - V1, + V1(usize), /// Appears in Moorhuhn Jump 'n Run games as well /// as Moorhuhn Kart 2, starting with Moorhuhn Kart 2. /// @@ -54,12 +55,6 @@ enum ArchiveKind { /// File Entries have a max path length of 0x68, /// with a total entry size of 0x80 V2, - /// Appears in later Moorhuhn Shoot 'em Up games, starting with Moorhuhn - /// Invasion. - /// - /// Works the same as V2, but has the maximum header and path string size - /// increased from 0x30 to 0x40 - V3, } impl ArchiveKind { @@ -71,9 +66,9 @@ impl ArchiveKind { let name = NullString::read(reader)?.to_string(); reader.rewind()?; match name.as_str() { - "MHJNR-XXL" | "MHJNR-XS" | "Moorhuhn Kart 2" => Ok(ArchiveKind::V1), - "MH-W V1.0" | "MH3 V1.0 " | "MH 1 REMAKE" => Ok(ArchiveKind::V2), - "MHP XXL" | "MHINV XXL V1.0" => Ok(ArchiveKind::V3), + "MHJNR-XXL" | "MHJNR-XS" | "Moorhuhn Kart 2" => Ok(ArchiveKind::V2), + "MH-W V1.0" | "MH3 V1.0 " | "MH 1 REMAKE" => Ok(ArchiveKind::V1(0x30)), + "MHP XXL" | "MHINV XXL V1.0" => Ok(ArchiveKind::V1(0x40)), name => Err(Error::Unsupported { reason: name.to_string(), }), @@ -87,10 +82,89 @@ impl Archive { where R: Read + Seek, { - match ArchiveKind::guess(reader)? { - ArchiveKind::V1 => Ok(standard::Container::read_args(reader, (0x30,))?.into()), - ArchiveKind::V2 => Ok(mhjnr::Container::read(reader)?.into()), - ArchiveKind::V3 => Ok(standard::Container::read_args(reader, (0x40,))?.into()), + let kind = ArchiveKind::guess(reader)?; + Archive::read_kind(reader, kind) + } + + /// Reads a specific archive kind from a binary stream + /// + /// Usually you want to use `read` instead. + pub fn read_kind(reader: &mut R, kind: ArchiveKind) -> Result + where + R: Read + Seek, + { + match kind { + ArchiveKind::V1(size) => Ok(v1::Container::read_args(reader, (size,))?.into()), + ArchiveKind::V2 => Ok(v2::Container::read(reader)?.into()), } } } + +#[cfg(test)] +mod tests { + use crate::archive::{Archive, FilePointer}; + use std::io::Cursor; + + #[test] + fn it_should_load_v2() { + let bin = include_bytes!("v2a.dat"); + let archive = Archive::read(&mut Cursor::new(bin)).unwrap(); + assert_eq!(archive.len(), 2); + assert_eq!( + archive["data\\config.txt"], + FilePointer { + position: 0x57b40, + length: 0xf4 + } + ); + assert_eq!( + archive["data\\fonts\\dangerfont.bmp"], + FilePointer { + position: 0x57c40, + length: 0x7dfd8, + } + ) + } + + #[test] + fn it_should_load_v1a() { + let bin = include_bytes!("v1a.dat"); + let archive = Archive::read(&mut Cursor::new(bin)).unwrap(); + assert_eq!(archive.len(), 2); + assert_eq!( + archive["data\\mhx.fnt"], + FilePointer { + position: 0x1200, + length: 0x8d9, + } + ); + assert_eq!( + archive["data\\text.txt"], + FilePointer { + position: 0x1c00, + length: 0x427e, + } + ) + } + + #[test] + fn it_should_load_v1b() { + let bin = include_bytes!("v1b.dat"); + let archive = Archive::read(&mut Cursor::new(bin)).unwrap(); + assert_eq!(archive.len(), 2); + assert_eq!( + archive["data\\endbranding_xxl.txt"], + FilePointer { + position: 0x7000, + length: 0x40, + } + ); + assert_eq!( + archive["data\\settings_xxl.txt"], + FilePointer { + position: 0x7200, + length: 0x872, + } + ) + } +} diff --git a/rust/springylib/src/archive/standard.rs b/rust/springylib/src/archive/v1.rs similarity index 100% rename from rust/springylib/src/archive/standard.rs rename to rust/springylib/src/archive/v1.rs diff --git a/rust/springylib/src/archive/v1a.dat b/rust/springylib/src/archive/v1a.dat new file mode 100644 index 0000000000000000000000000000000000000000..2332ee999997ffbf86608234ba8504030b21f36a GIT binary patch literal 256 zcmebEP%u;oa`koec4Z(62r)1)A}LHsEJ=*X&8W~z%PT<=#?1ukzsZ4+2J0_Lttine ZsUXS#83u+rCxm`2AVBblU;*t%1^}Zq4_W{K literal 0 HcmV?d00001 diff --git a/rust/springylib/src/archive/v1b.dat b/rust/springylib/src/archive/v1b.dat new file mode 100644 index 0000000000000000000000000000000000000000..d9ea9253c487bc334f8517fef959a46c566312ec GIT binary patch literal 320 zcmebE2vCTK@L?bu6fiI_B56)ZEJ=(>%}YrtO3X{i%uA22sL0VPsVG5Gf}aUA&jCpV i*u3J@k`ka<#UvXBG^hxOI1mPE0Re(X1`FsuWB>rVh!>Io literal 0 HcmV?d00001 diff --git a/rust/springylib/src/archive/mhjnr.rs b/rust/springylib/src/archive/v2.rs similarity index 100% rename from rust/springylib/src/archive/mhjnr.rs rename to rust/springylib/src/archive/v2.rs diff --git a/rust/springylib/src/archive/v2a.dat b/rust/springylib/src/archive/v2a.dat new file mode 100644 index 0000000000000000000000000000000000000000..fa88ca50d1d9c257a4dcce530c5c2ed832550adf GIT binary patch literal 320 zcmebE@bU}Njfn7Jzz&!|!i?DEK%yy$C5bV~`FUxX>3Ss5-Y`vu10xW7N5pt+uWw>#l9m5C!-scwT literal 0 HcmV?d00001