start adding Shadow the Hedgehog support
This commit is contained in:
parent
3e4fd54402
commit
2f887b4924
|
@ -12,6 +12,7 @@ pub mod prelude {
|
|||
pub use one::dreams::{JodOne, JodOneEntry};
|
||||
pub use one::read_one_archive;
|
||||
pub use one::rings::{SsrOne, SsrOneEntry};
|
||||
pub use one::shadow::{ShadOne, ShadOneEntry};
|
||||
pub use one::{OneArchive, OneArchiveEntry};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,26 +18,36 @@ use crate::string_binwrite_helper;
|
|||
// 988 JodOne{version: 0xcc, comment: " ", category: Default, ..}
|
||||
// 1579 JodOne{version: 0xcc, comment: " ", category: landData, ..}
|
||||
|
||||
const JOD_ONE_MAGIC: &'static [u8; 16] = b"ThisIsOneFile\0\0\0";
|
||||
const MAGIC: &'static str = "ThisIsOneFile";
|
||||
|
||||
const SIZE_OF_JOD_ONE_HEADER: usize = JOD_ONE_MAGIC.len() + 2 * size_of::<u32>() + 32 + 132;
|
||||
const SIZE_OF_JOD_ONE_ENTRY: usize = 6 * size_of::<u32>() + 192;
|
||||
const SIZE_OF_MAGIC: usize = 16;
|
||||
const SIZE_OF_CATEGORY: usize = 32;
|
||||
const SIZE_OF_COMMENT: usize = 132;
|
||||
const SIZE_OF_HEADER: u32 =
|
||||
(2 * size_of::<u32>() + SIZE_OF_MAGIC + SIZE_OF_CATEGORY + SIZE_OF_COMMENT) as u32;
|
||||
|
||||
const SIZE_OF_FILENAME: usize = 192;
|
||||
const SIZE_OF_ENTRY: u32 = (6 * size_of::<u32>() + SIZE_OF_FILENAME) as u32;
|
||||
|
||||
// not using binread's built-in [br(magic=...)] because it doesn't let me reference self::MAGIC
|
||||
#[derive(BinRead, BinWrite)]
|
||||
#[br(little, magic = b"ThisIsOneFile\0\0\0")]
|
||||
#[br(little, assert(magic == self::MAGIC))]
|
||||
#[binwrite(little)]
|
||||
pub struct JodOne {
|
||||
// usually 0xCC... archive creator version maybe?
|
||||
// but i've seen 0xCA in Test04/disp/Test04.one and 0xCB in Test05/disp/stg2010_sun.one
|
||||
#[br(pad_size_to = self::SIZE_OF_MAGIC, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_MAGIC)))]
|
||||
pub magic: String,
|
||||
/// usually 0xCC... archive creator version?
|
||||
/// i've seen 0xCA in Test04/disp/Test04.one and 0xCB in Test05/disp/stg2010_sun.one
|
||||
pub version: u32,
|
||||
// "Default" or "landData"
|
||||
#[br(pad_size_to = 32, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(32)))]
|
||||
/// always "Default" or "landData"
|
||||
#[br(pad_size_to = self::SIZE_OF_CATEGORY, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_CATEGORY)))]
|
||||
pub category: String,
|
||||
pub count: u32,
|
||||
// always " "
|
||||
#[br(pad_size_to = 132, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(132)))]
|
||||
/// always " "
|
||||
#[br(pad_size_to = self::SIZE_OF_COMMENT, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_COMMENT)))]
|
||||
pub comment: String,
|
||||
#[br(count = count)]
|
||||
pub entries: Vec<JodOneEntry>,
|
||||
|
@ -53,10 +63,10 @@ pub struct JodOneEntry {
|
|||
pub size_dec: u32,
|
||||
pub id_again: u32,
|
||||
#[br(count = size_cmp)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<u8>>| x.ptr))]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr))]
|
||||
pub data: FilePtr<u32, Vec<u8>>,
|
||||
#[br(pad_size_to = 192, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(192)))]
|
||||
#[br(pad_size_to = self::SIZE_OF_FILENAME, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_FILENAME)))]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
@ -88,7 +98,6 @@ impl OneArchiveEntry for JodOneEntry {}
|
|||
|
||||
impl OneArchive for JodOne {
|
||||
fn write(&self, mut writer: &mut dyn WriteSeek) -> std::io::Result<()> {
|
||||
writer.write(JOD_ONE_MAGIC)?;
|
||||
BinWrite::write(&self, &mut writer)?;
|
||||
for entry in &self.entries {
|
||||
let loc = writer.seek(std::io::SeekFrom::Current(0)).unwrap() as usize;
|
||||
|
@ -138,7 +147,7 @@ impl OneArchive for JodOne {
|
|||
}
|
||||
}
|
||||
|
||||
let mut loc = (SIZE_OF_JOD_ONE_HEADER + SIZE_OF_JOD_ONE_ENTRY * entries.len()) as u32;
|
||||
let mut loc = SIZE_OF_HEADER + SIZE_OF_ENTRY * entries.len() as u32;
|
||||
for entry in &mut entries {
|
||||
loc = ((loc + 15) / 16) * 16; // round up to 16 byte alignment
|
||||
entry.data.ptr = loc;
|
||||
|
@ -147,6 +156,7 @@ impl OneArchive for JodOne {
|
|||
|
||||
// user can overwrite string/version fields afterward
|
||||
JodOne {
|
||||
magic: MAGIC.to_string(),
|
||||
version: 0xCC,
|
||||
category: "Default".to_string(),
|
||||
count: entries.len() as u32,
|
||||
|
|
|
@ -2,6 +2,7 @@ use binread::io::SeekFrom;
|
|||
use binread::BinReaderExt;
|
||||
use one::dreams::JodOne;
|
||||
use one::rings::SsrOne;
|
||||
use one::shadow::ShadOne;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
@ -9,6 +10,7 @@ use std::path::PathBuf;
|
|||
|
||||
pub(crate) mod dreams;
|
||||
pub(crate) mod rings;
|
||||
pub(crate) mod shadow;
|
||||
|
||||
pub trait WriteSeek: Write + Seek {}
|
||||
impl<T> WriteSeek for T where T: Write + Seek {}
|
||||
|
@ -22,13 +24,14 @@ pub trait OneArchive: Debug {
|
|||
fn entries(&self) -> Vec<&dyn OneArchiveEntry>;
|
||||
}
|
||||
|
||||
// TODO: find out the least-common-denominator of things we know about these files
|
||||
// TODO: find out the least-common-denominator of things we know about these files to expose here
|
||||
pub trait OneArchiveEntry: Debug {}
|
||||
|
||||
// hack to work around inability to consume self via boxed trait object
|
||||
pub enum DynOneArchive {
|
||||
JodOne(JodOne),
|
||||
SsrOne(SsrOne),
|
||||
ShadOne(ShadOne),
|
||||
}
|
||||
|
||||
impl Debug for DynOneArchive {
|
||||
|
@ -36,6 +39,7 @@ impl Debug for DynOneArchive {
|
|||
match self {
|
||||
DynOneArchive::JodOne(one) => one.fmt(f),
|
||||
DynOneArchive::SsrOne(one) => one.fmt(f),
|
||||
DynOneArchive::ShadOne(one) => one.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +49,7 @@ impl OneArchive for DynOneArchive {
|
|||
match self {
|
||||
DynOneArchive::JodOne(one) => one.write(writer),
|
||||
DynOneArchive::SsrOne(one) => one.write(writer),
|
||||
DynOneArchive::ShadOne(one) => one.write(writer),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +64,7 @@ impl OneArchive for DynOneArchive {
|
|||
match self {
|
||||
DynOneArchive::JodOne(one) => one.unpack(),
|
||||
DynOneArchive::SsrOne(one) => one.unpack(),
|
||||
DynOneArchive::ShadOne(one) => one.unpack(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +72,7 @@ impl OneArchive for DynOneArchive {
|
|||
match self {
|
||||
DynOneArchive::JodOne(one) => one.entries(),
|
||||
DynOneArchive::SsrOne(one) => one.entries(),
|
||||
DynOneArchive::ShadOne(one) => one.entries(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,5 +88,10 @@ pub fn read_one_archive(mut reader: impl Read + Seek) -> Result<DynOneArchive, B
|
|||
Ok(one) => return Ok(DynOneArchive::SsrOne(one)),
|
||||
Err(e) => info!("Not a Sonic and the Secret Rings archive: {}", e),
|
||||
}
|
||||
reader.seek(SeekFrom::Start(rewind))?;
|
||||
match reader.read_le::<shadow::ShadOne>() {
|
||||
Ok(one) => return Ok(DynOneArchive::ShadOne(one)),
|
||||
Err(e) => info!("Not a Shadow the Hedgehog archive: {}", e),
|
||||
}
|
||||
Err("No valid .one archive found".into())
|
||||
}
|
||||
|
|
|
@ -10,13 +10,14 @@ use std::path::PathBuf;
|
|||
use crate::string_binread_helper;
|
||||
use crate::string_binwrite_helper;
|
||||
|
||||
const SIZE_OF_SSR_ONE_HEADER: usize = 4 * size_of::<u32>(); // 0x10
|
||||
const SIZE_OF_SSR_ONE_ENTRY: usize = 4 * size_of::<u32>() + 32; // 0x30
|
||||
const SIZE_OF_HEADER: u32 = 4 * size_of::<u32>() as u32;
|
||||
const SIZE_OF_FILENAME: usize = 32;
|
||||
const SIZE_OF_ENTRY: u32 = (4 * size_of::<u32>() + SIZE_OF_FILENAME) as u32;
|
||||
|
||||
// stats on padding and size_dec:
|
||||
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;
|
||||
// $ fd one.txt | xargs cat | sed 's/count: [0-9]\+, //' | grep '^SsrOne{' | sort | uniq -c
|
||||
// 284 SsrOne{_unknown: 0x0, ..}
|
||||
// 284 SsrOne{_padding: 0x0, ..}
|
||||
// $ fd one.txt | xargs cat | grep '^SsrOneEntry{' | sed \
|
||||
// > -e 's/ [1-9][0-9]*, / 1+, /g' \
|
||||
// > -e 's/id: [01]+\?, name: "[^"]\+", //' | sort | uniq -c
|
||||
|
@ -24,14 +25,17 @@ const SIZE_OF_SSR_ONE_ENTRY: usize = 4 * size_of::<u32>() + 32; // 0x30
|
|||
// 5105 SsrOneEntry{size_cmp: 1+, size_dec: 1+, ..}
|
||||
|
||||
#[derive(BinRead, BinWrite)]
|
||||
#[br(big, assert(entries.ptr == 0x10 && data_offset == entries.ptr + count * 0x30))]
|
||||
#[br(big, assert(
|
||||
entries.ptr == self::SIZE_OF_HEADER &&
|
||||
data_offset == entries.ptr + count * self::SIZE_OF_ENTRY
|
||||
))]
|
||||
#[binwrite(big)]
|
||||
pub struct SsrOne {
|
||||
/// number of files stored in the .one
|
||||
pub count: u32,
|
||||
/// the location of the table of contents. usually 0x10, just after this header
|
||||
/// the location of the table of contents. always 0x10, just after this header
|
||||
#[br(count = count)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<_>>| x.ptr))]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr))]
|
||||
pub entries: FilePtr<u32, Vec<SsrOneEntry>>,
|
||||
/// location of the end of the toc/beginning of data. TODO: assertion
|
||||
pub data_offset: u32,
|
||||
|
@ -43,8 +47,8 @@ pub struct SsrOne {
|
|||
#[br(big)]
|
||||
#[binwrite(big)]
|
||||
pub struct SsrOneEntry {
|
||||
#[br(pad_size_to = 32, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(32)))]
|
||||
#[br(pad_size_to = self::SIZE_OF_FILENAME, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_FILENAME)))]
|
||||
pub name: String,
|
||||
pub id: u32,
|
||||
// note: seek_before/restore_position shenanigans because we need _size_cmp for data's count
|
||||
|
@ -52,7 +56,7 @@ pub struct SsrOneEntry {
|
|||
#[binwrite(ignore)]
|
||||
pub _size_cmp: u32,
|
||||
#[br(count = _size_cmp)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<u8>>| x.ptr))]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr))]
|
||||
pub data: FilePtr<u32, Vec<u8>>,
|
||||
pub size_cmp: u32,
|
||||
pub size_dec: u32,
|
||||
|
@ -62,7 +66,7 @@ impl std::fmt::Debug for SsrOne {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SsrOne{{count: {}, _unknown: 0x{:x}, ..}}",
|
||||
"SsrOne{{count: {}, _padding: 0x{:x}, ..}}",
|
||||
self.count, self._padding
|
||||
)
|
||||
}
|
||||
|
@ -129,7 +133,7 @@ impl OneArchive for SsrOne {
|
|||
}
|
||||
}
|
||||
|
||||
let data_offset = (SIZE_OF_SSR_ONE_HEADER + SIZE_OF_SSR_ONE_ENTRY * entries.len()) as u32;
|
||||
let data_offset = SIZE_OF_HEADER + SIZE_OF_ENTRY * entries.len() as u32;
|
||||
let mut loc = data_offset;
|
||||
for entry in &mut entries {
|
||||
loc = ((loc + 3) / 4) * 4; // round up to 4 byte alignment
|
||||
|
@ -141,7 +145,7 @@ impl OneArchive for SsrOne {
|
|||
SsrOne {
|
||||
count: entries.len() as u32,
|
||||
entries: FilePtr {
|
||||
ptr: SIZE_OF_SSR_ONE_HEADER as u32,
|
||||
ptr: SIZE_OF_HEADER as u32,
|
||||
value: Some(entries),
|
||||
},
|
||||
data_offset,
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
use binread::FilePtr;
|
||||
use binwrite::BinWrite;
|
||||
use one::{OneArchive, OneArchiveEntry, WriteSeek};
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::string_binread_helper;
|
||||
use crate::string_binwrite_helper;
|
||||
|
||||
const MAGIC_50: &'static str = "One Ver 0.50";
|
||||
const MAGIC_60: &'static str = "One Ver 0.60";
|
||||
|
||||
const SIZE_OF_MAGIC: usize = 16;
|
||||
const SIZE_OF_COMMENT: usize = 0x90;
|
||||
const SIZE_OF_FILENAME: usize = 0x2c;
|
||||
|
||||
#[derive(BinRead, BinWrite)]
|
||||
#[br(little, assert(start == 0 && (magic == self::MAGIC_60 || magic == self::MAGIC_50)))]
|
||||
#[binwrite(little)]
|
||||
pub struct ShadOne {
|
||||
/// 0. presumably where data starts, not counting the "mini-header" of these first three u32's
|
||||
pub start: u32,
|
||||
/// the size of the archive, not counting the "mini-header" of these first three u32's
|
||||
pub archive_size_minus_twelve: u32,
|
||||
/// presumably the archiver version. known values: 0x1c020037 and 0x1c020020 from gamecube version
|
||||
pub version: u32,
|
||||
#[br(pad_size_to = self::SIZE_OF_MAGIC, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_MAGIC)))]
|
||||
pub magic: String,
|
||||
/// number of files stored in the .one
|
||||
pub count: u32,
|
||||
#[br(pad_size_to = self::SIZE_OF_COMMENT, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_COMMENT)))]
|
||||
pub comment: String,
|
||||
#[br(count = count)]
|
||||
pub entries: Vec<ShadOneEntry>,
|
||||
}
|
||||
|
||||
#[derive(BinRead, BinWrite)]
|
||||
#[br(little)]
|
||||
#[binwrite(little)]
|
||||
pub struct ShadOneEntry {
|
||||
#[br(pad_size_to = self::SIZE_OF_FILENAME, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_FILENAME)))]
|
||||
pub name: String,
|
||||
pub size_dec: u32,
|
||||
#[br(offset = 0xC)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr))]
|
||||
pub data_offset: FilePtr<u32, ()>,
|
||||
pub is_compressed: u32,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ShadOne {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ShadOne{{version: 0x{:x}, comment: {:?}, count: {}, ..}}",
|
||||
self.version, self.comment, self.count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ShadOneEntry {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ShadOneEntry{{is_compressed: {}, name: {:?}, size_dec: {}, ..}}",
|
||||
self.is_compressed, self.name, self.size_dec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl OneArchiveEntry for ShadOneEntry {}
|
||||
|
||||
impl OneArchive for ShadOne {
|
||||
fn write(&self, mut _writer: &mut dyn WriteSeek) -> std::io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pack(_contents: impl IntoIterator<Item = (PathBuf, Vec<u8>)>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn unpack(self) -> Box<dyn Iterator<Item = (PathBuf, Vec<u8>)>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn entries(&self) -> Vec<&dyn OneArchiveEntry> {
|
||||
self.entries
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let y: &dyn OneArchiveEntry = x;
|
||||
y
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue