Heroes archive creating support

This commit is contained in:
lifning 2021-01-17 12:22:21 -08:00
parent 416875502a
commit 19e7dfa238
3 changed files with 74 additions and 10 deletions

View File

@ -26,6 +26,7 @@ enum Action {
CreateJodFile(CreateJodFile),
CreateSsrFile(CreateSsrFile),
CreateShadFile(CreateShadFile),
CreateHerFile(CreateHerFile),
}
enum GameType {
@ -125,6 +126,18 @@ struct CreateShadFile {
contents: Vec<PathBuf>,
}
/// Create a Sonic _H_eroes .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cHf")]
struct CreateHerFile {
/// path of the .one file to create
#[argh(positional)]
file: PathBuf,
/// paths of the files to pack into the archive
#[argh(positional)]
contents: Vec<PathBuf>,
}
fn create_one(subcmd: CreateFile) -> Result<(), Box<dyn Error>> {
if subcmd.file.exists() {
return Err(format!("{:?} already exists, refusing to overwrite", subcmd.file).into());
@ -141,7 +154,7 @@ fn create_one(subcmd: CreateFile) -> Result<(), Box<dyn Error>> {
GameType::Dreams => Box::new(JodOne::pack(contents)),
GameType::Rings => Box::new(SsrOne::pack(contents)),
GameType::Shadow => Box::new(ShadOne::pack(contents)),
GameType::Heroes => todo!(),
GameType::Heroes => Box::new(HerOne::pack(contents)),
};
let mut outfile = File::create(&subcmd.file)?;
archive.write(&mut outfile)?;
@ -200,5 +213,13 @@ fn main() -> Result<(), Box<dyn Error>> {
};
create_one(subcmd)
}
Action::CreateHerFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Heroes,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
}
}

View File

@ -10,6 +10,7 @@ mod one;
pub mod prelude {
pub use one::dreams::{JodOne, JodOneEntry};
pub use one::heroes::{HerOne, HerOneEntry};
pub use one::read_one_archive;
pub use one::rings::{SsrOne, SsrOneEntry};
pub use one::shadow::{ShadOne, ShadOneEntry};

View File

@ -8,7 +8,7 @@ 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_ENTRY: u32 = 3 * size_of::<u32>() as u32;
const SIZE_OF_FILENAME: usize = 0x40;
const TYPICAL_STRING_TABLE_COUNT: usize = 0x100;
@ -33,18 +33,18 @@ pub struct HerOne {
#[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),
count = string_table.iter().skip(2).position(|x| x.inner.is_empty()).unwrap_or(0),
args(version, string_table.as_ptr())
)]
pub entries: Vec<HerOneEntry>,
}
#[derive(BinRead, BinWrite, Clone)]
pub struct HerOneFilename(
#[derive(BinRead, BinWrite, Clone, Default)]
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,
);
pub inner: 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.
@ -61,7 +61,7 @@ pub struct HerOneEntry {
version: u32,
#[br(count = size_cmp)]
pub data: Vec<u8>,
#[br(calc = unsafe { (*string_table.add(id as usize)).0.clone() })]
#[br(calc = unsafe { (*string_table.add(id as usize)).inner.clone() })]
#[binwrite(ignore)]
name: String,
}
@ -86,14 +86,56 @@ impl OneArchiveEntry for HerOneEntry {}
impl OneArchive for HerOne {
fn write(&self, mut writer: &mut dyn WriteSeek) -> std::io::Result<()> {
todo!()
BinWrite::write(&self, &mut writer)
}
fn pack(contents: impl IntoIterator<Item = (PathBuf, Vec<u8>)>) -> Self
where
Self: Sized,
{
todo!()
// TODO: set_version that updates both copies here as well as all the entries
let version = 0x1400ffff;
let mut entries = Vec::new();
let mut string_table = vec![HerOneFilename::default(); 2];
let mut archive_size_minus_12 = 0;
for (id, (path, data)) in contents.into_iter().enumerate() {
let mut cmp = prs::Compressor::new(&data, None).compress();
let name = path.to_string_lossy().to_uppercase();
string_table.push(HerOneFilename {
inner: name.clone(),
});
// round up to 4-byte alignment
while (cmp.len() & 3) != 0 {
cmp.push(0);
}
archive_size_minus_12 += SIZE_OF_ENTRY + cmp.len() as u32;
entries.push(HerOneEntry {
id: id as u32 + 2,
size_cmp: cmp.len() as u32,
version,
data: cmp,
name,
});
}
if string_table.len() < TYPICAL_STRING_TABLE_COUNT {
string_table.resize_with(TYPICAL_STRING_TABLE_COUNT, HerOneFilename::default);
}
// we're counting 'entry 1' for the string table, but not 'entry 0' for the entire file
let string_table_size = (string_table.len() * SIZE_OF_FILENAME) as u32;
archive_size_minus_12 += SIZE_OF_ENTRY + string_table_size;
HerOne {
id_archive: 0,
archive_size_minus_12,
version,
id_string_table: 1,
string_table_size,
version_again: version,
string_table,
entries,
}
}
fn unpack(self) -> Box<dyn Iterator<Item = (PathBuf, Vec<u8>)>> {