first bit of Shadow .one creation

This commit is contained in:
lifning 2021-01-17 03:48:34 -08:00
parent b7c670b37b
commit 5ac56fb114
2 changed files with 77 additions and 9 deletions

View File

@ -25,6 +25,7 @@ enum Action {
CreateFile(CreateFile),
CreateJodFile(CreateJodFile),
CreateSsrFile(CreateSsrFile),
CreateShadFile(CreateShadFile),
}
enum GameType {
@ -112,6 +113,18 @@ struct CreateSsrFile {
contents: Vec<PathBuf>,
}
/// Create a _S_hadow the Hedgehog .one archive.
#[derive(FromArgs)]
#[argh(subcommand, name = "cSf")]
struct CreateShadFile {
/// 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());
@ -127,7 +140,7 @@ fn create_one(subcmd: CreateFile) -> Result<(), Box<dyn Error>> {
let archive: Box<dyn OneArchive> = match subcmd.game {
GameType::Dreams => Box::new(JodOne::pack(contents)),
GameType::Rings => Box::new(SsrOne::pack(contents)),
GameType::Shadow => todo!(),
GameType::Shadow => Box::new(ShadOne::pack(contents)),
GameType::Heroes => todo!(),
};
let mut outfile = File::create(&subcmd.file)?;
@ -179,5 +192,13 @@ fn main() -> Result<(), Box<dyn Error>> {
};
create_one(subcmd)
}
Action::CreateShadFile(subcmd) => {
let subcmd = CreateFile {
game: GameType::Shadow,
file: subcmd.file,
contents: subcmd.contents,
};
create_one(subcmd)
}
}
}

View File

@ -25,7 +25,8 @@ const MAGIC_60: &'static str = "One Ver 0.60";
const SIZE_OF_MAGIC: usize = 16;
const SIZE_OF_COMMENT: usize = 0x90;
const SIZE_OF_HEADER: u32 = (4 * size_of::<u32>() + SIZE_OF_MAGIC + SIZE_OF_COMMENT) as u32;
const SIZE_OF_PRE_HEADER: u32 = (3 * size_of::<u32>()) as u32;
const SIZE_OF_HEADER: u32 = SIZE_OF_PRE_HEADER + (size_of::<u32>() + SIZE_OF_MAGIC + SIZE_OF_COMMENT) as u32;
const SIZE_OF_FILENAME: usize = 0x2c;
const SIZE_OF_ENTRY: u32 = (3 * size_of::<u32>() + SIZE_OF_FILENAME) as u32;
@ -55,16 +56,16 @@ fn compute_size_cmp<R: Read + Seek>(
#[binwrite(little)]
pub struct ShadOne {
/// 0. presumably where data starts, not counting the "pre-header" of these first three u32's
pub start: u32,
start: u32,
/// the size of the archive, not counting the "pre-header" of these first three u32's
pub archive_size_minus_twelve: u32,
archive_size_minus_twelve: u32,
/// presumably the archiver version. known values: 0x1c020037 and 0x1c020020 from GCN 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,
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,
@ -91,8 +92,8 @@ pub struct ShadOneEntry {
#[br(restore_position, parse_with = compute_size_cmp, args(count, arc_size))]
#[binwrite(ignore)]
pub _size_cmp: u32,
#[br(offset = 12, count = _size_cmp)]
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr))]
#[br(offset = self::SIZE_OF_PRE_HEADER as u64, count = _size_cmp)]
#[binwrite(preprocessor(|x: &FilePtr<u32, _>| x.ptr - self::SIZE_OF_PRE_HEADER))]
pub data: FilePtr<u32, Vec<u8>>, // TODO: compute size by finding PRS length?
pub is_compressed: u32,
}
@ -127,11 +128,57 @@ impl OneArchive for ShadOne {
todo!()
}
fn pack(_contents: impl IntoIterator<Item = (PathBuf, Vec<u8>)>) -> Self
fn pack(contents: impl IntoIterator<Item = (PathBuf, Vec<u8>)>) -> Self
where
Self: Sized,
{
todo!()
let mut entries = Vec::new();
for (path, data) in contents.into_iter() {
let cmp = prs::Compressor::new(&data, None).compress();
let name = path.to_string_lossy().to_uppercase();
if cmp.len() < data.len() {
entries.push(ShadOneEntry {
name,
size_dec: data.len() as u32,
_size_cmp: cmp.len() as u32,
data: FilePtr {
ptr: 0,
value: Some(cmp),
},
is_compressed: 1,
});
} else {
entries.push(ShadOneEntry {
name,
size_dec: 0,
_size_cmp: data.len() as u32,
data: FilePtr {
ptr: 0,
value: Some(data),
},
is_compressed: 0,
});
}
}
let data_offset = SIZE_OF_HEADER + SIZE_OF_ENTRY * entries.len() as u32;
let mut loc = data_offset;
for entry in &mut entries {
entry.data.ptr = loc;
loc += entry.data.value.as_ref().unwrap().len() as u32;
loc = ((loc + 3) / 4) * 4; // round up to 4 byte alignment
}
// user can overwrite string/version fields afterward
ShadOne {
start: 0,
archive_size_minus_twelve: loc - SIZE_OF_PRE_HEADER,
version: 0x1c020037,
magic: MAGIC_60.to_string(),
count: entries.len() as u32,
comment: "".to_string(),
entries,
}
}
fn unpack(self) -> Box<dyn Iterator<Item = (PathBuf, Vec<u8>)>> {