initial Heroes list/extract support

This commit is contained in:
lifning 2021-01-17 11:59:29 -08:00
parent d8a0ecf273
commit 416875502a
5 changed files with 136 additions and 11 deletions

View File

@ -1,3 +1,5 @@
use crate::string_binread_helper;
use crate::string_binwrite_helper;
use binread::FilePtr;
use binwrite::BinWrite;
use one::{OneArchive, OneArchiveEntry, WriteSeek};
@ -6,9 +8,6 @@ use std::fmt::Formatter;
use std::mem::size_of;
use std::path::PathBuf;
use crate::string_binread_helper;
use crate::string_binwrite_helper;
// stats on version, comment, and category:
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;
// $ fd one.txt | xargs cat | sed 's/count: .*/..}/' | grep '^JodOne{' | sort | uniq -c

116
one-rust/src/one/heroes.rs Normal file
View File

@ -0,0 +1,116 @@
use crate::string_binread_helper;
use crate::string_binwrite_helper;
use binread::BinRead;
use binwrite::BinWrite;
use one::{OneArchive, OneArchiveEntry, WriteSeek};
use prs_rust::prs;
use std::fmt::Formatter;
use std::mem::size_of;
use std::path::PathBuf;
const SIZE_OF_PRE_HEADER: u32 = 3 * size_of::<u32>() as u32;
const SIZE_OF_FILENAME: usize = 0x40;
const TYPICAL_STRING_TABLE_COUNT: usize = 0x100;
#[derive(BinRead, BinWrite)]
#[br(little, assert(
id_archive == 0 && id_string_table == 1 && version == version_again &&
entries.iter().enumerate().all(|(i, ent)| ent.id as usize == i + 2)
))]
#[binwrite(little)]
pub struct HerOne {
/// 0. conceptually the ID of the "entry" containing the entire archive
id_archive: u32,
/// the size of the archive, not counting the "pre-header" of these first three u32's
archive_size_minus_12: u32,
/// presumably the archiver version. known values: 0x1400ffff from GCN version
version: u32,
/// 1. conceptually the ID of the (uncompressed) array of filenames, indexed by ID.
id_string_table: u32,
/// usually 0x4000. size of string table in bytes.
string_table_size: u32,
version_again: u32,
#[br(count = string_table_size as usize / self::SIZE_OF_FILENAME)]
pub string_table: Vec<HerOneFilename>,
#[br(
count = string_table.iter().skip(2).position(|x| x.0.is_empty()).unwrap_or(0),
args(version, string_table.as_ptr())
)]
pub entries: Vec<HerOneEntry>,
}
#[derive(BinRead, BinWrite, Clone)]
pub struct HerOneFilename(
#[br(pad_size_to = self::SIZE_OF_FILENAME, map = string_binread_helper)]
#[binwrite(preprocessor(string_binwrite_helper(self::SIZE_OF_FILENAME)))]
pub String,
);
// there is one file for which the assertion can't be (size_dec == 0) == (is_compressed == 0).
// event/event8006_sceneE.one: EVENT8006.CEN, whose size_dec is 0x20, its in-archive size.
#[derive(BinRead, BinWrite)]
#[br(
little,
import(version_hdr: u32, string_table: *const HerOneFilename),
assert(version == version_hdr)
)]
#[binwrite(little)]
pub struct HerOneEntry {
pub id: u32,
pub size_cmp: u32,
version: u32,
#[br(count = size_cmp)]
pub data: Vec<u8>,
#[br(calc = unsafe { (*string_table.add(id as usize)).0.clone() })]
#[binwrite(ignore)]
name: String,
}
impl std::fmt::Debug for HerOne {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "HerOne{{version: 0x{:x}, ..}}", self.version)
}
}
impl std::fmt::Debug for HerOneEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"HerOneEntry{{id: {}, name: {:?}, size_cmp: {}, ..}}",
self.id, self.name, self.size_cmp,
)
}
}
impl OneArchiveEntry for HerOneEntry {}
impl OneArchive for HerOne {
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>)>> {
Box::new(self.entries.into_iter().map(|entry| {
let key = PathBuf::from(entry.name);
let dec_buf = prs::Decompressor::new(entry.data, None).decompress();
(key, dec_buf)
}))
}
fn entries(&self) -> Vec<&dyn OneArchiveEntry> {
self.entries
.iter()
.map(|x| {
let y: &dyn OneArchiveEntry = x;
y
})
.collect()
}
}

View File

@ -1,6 +1,7 @@
use binread::io::SeekFrom;
use binread::BinReaderExt;
use one::dreams::JodOne;
use one::heroes::HerOne;
use one::rings::SsrOne;
use one::shadow::ShadOne;
use std::error::Error;
@ -9,6 +10,7 @@ use std::io::{Read, Seek, Write};
use std::path::PathBuf;
pub(crate) mod dreams;
pub(crate) mod heroes;
pub(crate) mod rings;
pub(crate) mod shadow;
@ -32,6 +34,7 @@ pub enum DynOneArchive {
JodOne(JodOne),
SsrOne(SsrOne),
ShadOne(ShadOne),
HerOne(HerOne),
}
impl Debug for DynOneArchive {
@ -40,6 +43,7 @@ impl Debug for DynOneArchive {
DynOneArchive::JodOne(one) => one.fmt(f),
DynOneArchive::SsrOne(one) => one.fmt(f),
DynOneArchive::ShadOne(one) => one.fmt(f),
DynOneArchive::HerOne(one) => one.fmt(f),
}
}
}
@ -50,6 +54,7 @@ impl OneArchive for DynOneArchive {
DynOneArchive::JodOne(one) => one.write(writer),
DynOneArchive::SsrOne(one) => one.write(writer),
DynOneArchive::ShadOne(one) => one.write(writer),
DynOneArchive::HerOne(one) => one.write(writer),
}
}
@ -65,6 +70,7 @@ impl OneArchive for DynOneArchive {
DynOneArchive::JodOne(one) => one.unpack(),
DynOneArchive::SsrOne(one) => one.unpack(),
DynOneArchive::ShadOne(one) => one.unpack(),
DynOneArchive::HerOne(one) => one.unpack(),
}
}
@ -73,6 +79,7 @@ impl OneArchive for DynOneArchive {
DynOneArchive::JodOne(one) => one.entries(),
DynOneArchive::SsrOne(one) => one.entries(),
DynOneArchive::ShadOne(one) => one.entries(),
DynOneArchive::HerOne(one) => one.entries(),
}
}
}
@ -93,5 +100,10 @@ pub fn read_one_archive(mut reader: impl Read + Seek) -> Result<DynOneArchive, B
Ok(one) => return Ok(DynOneArchive::ShadOne(one)),
Err(e) => info!("Not a Shadow the Hedgehog archive: {}", e),
}
reader.seek(SeekFrom::Start(rewind))?;
match reader.read_le::<heroes::HerOne>() {
Ok(one) => return Ok(DynOneArchive::HerOne(one)),
Err(e) => info!("Not a Sonic Heroes archive: {}", e),
}
Err("No valid .one archive found".into())
}

View File

@ -1,3 +1,5 @@
use crate::string_binread_helper;
use crate::string_binwrite_helper;
use binread::FilePtr;
use binwrite::BinWrite;
use one::{OneArchive, OneArchiveEntry, WriteSeek};
@ -7,9 +9,6 @@ use std::io::SeekFrom;
use std::mem::size_of;
use std::path::PathBuf;
use crate::string_binread_helper;
use crate::string_binwrite_helper;
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;

View File

@ -1,14 +1,13 @@
use crate::string_binread_helper;
use crate::string_binwrite_helper;
use binread::io::{Read, Seek, SeekFrom};
use binread::{BinRead, BinResult, FilePtr, ReadOptions};
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;
use prs_rust::prs;
use std::fmt::Formatter;
use std::mem::size_of;
use std::path::PathBuf;
// stats on version, comment, and count
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;