Heroes archive creating support
This commit is contained in:
parent
416875502a
commit
19e7dfa238
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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>)>> {
|
||||
|
|
Loading…
Reference in New Issue