implement extraction for Shadow
This commit is contained in:
parent
2f887b4924
commit
b7c670b37b
|
@ -1,4 +1,5 @@
|
|||
use binread::FilePtr;
|
||||
use binread::io::{Read, Seek, SeekFrom};
|
||||
use binread::{BinRead, BinResult, FilePtr, ReadOptions};
|
||||
use binwrite::BinWrite;
|
||||
use one::{OneArchive, OneArchiveEntry, WriteSeek};
|
||||
use std::fmt::Formatter;
|
||||
|
@ -6,23 +7,58 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::string_binread_helper;
|
||||
use crate::string_binwrite_helper;
|
||||
use prs_rust::prs;
|
||||
use std::mem::size_of;
|
||||
|
||||
// stats on version, comment, and count
|
||||
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;
|
||||
// $ fd one.txt | xargs cat | grep '^ShadOne{' | sed \
|
||||
// > -e 's/count: [1-9][0-9]*, /count: 1+, /' \
|
||||
// > -e 's/comment: ".\+"/comment: ".+"/' | sort | uniq -c
|
||||
// 11 ShadOne{version: 0x1c020020, comment: "", count: 1+, ..}
|
||||
// 1 ShadOne{version: 0x1c020037, comment: "", count: 0, ..}
|
||||
// 445 ShadOne{version: 0x1c020037, comment: "", count: 1+, ..}
|
||||
// 787 ShadOne{version: 0x1c020037, comment: ".+", count: 1+, ..}
|
||||
|
||||
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_HEADER: u32 = (4 * 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;
|
||||
|
||||
fn compute_size_cmp<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
ro: &ReadOptions,
|
||||
(count, arc_size): (u32, u32),
|
||||
) -> BinResult<u32> {
|
||||
let saved_pos = reader.seek(SeekFrom::Current(0))?;
|
||||
let cur_offset = u32::read_options(reader, ro, ())?;
|
||||
let next_pos = reader.seek(SeekFrom::Current(
|
||||
SIZE_OF_ENTRY as i64 - size_of::<u32>() as i64,
|
||||
))?;
|
||||
// last entry, use whole-archive end offset instead of next-file begin offset
|
||||
let next_offset = if next_pos as u32 > SIZE_OF_HEADER + count * self::SIZE_OF_ENTRY {
|
||||
arc_size
|
||||
} else {
|
||||
u32::read_options(reader, ro, ())?
|
||||
};
|
||||
reader.seek(SeekFrom::Start(saved_pos))?;
|
||||
Ok(next_offset - cur_offset)
|
||||
}
|
||||
|
||||
#[derive(BinRead, BinWrite)]
|
||||
#[br(little, assert(start == 0 && (magic == self::MAGIC_60 || magic == self::MAGIC_50)))]
|
||||
#[br(little, assert(start == 0 && matches!(magic.as_str(), self::MAGIC_60 | self::MAGIC_50)))]
|
||||
#[binwrite(little)]
|
||||
pub struct ShadOne {
|
||||
/// 0. presumably where data starts, not counting the "mini-header" of these first three u32's
|
||||
/// 0. presumably where data starts, not counting the "pre-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
|
||||
/// the size of the archive, not counting the "pre-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
|
||||
/// 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)))]
|
||||
|
@ -32,21 +68,32 @@ pub struct ShadOne {
|
|||
#[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)]
|
||||
#[br(count = count, args(count, archive_size_minus_twelve))]
|
||||
pub entries: Vec<ShadOneEntry>,
|
||||
}
|
||||
|
||||
// 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)]
|
||||
#[br(
|
||||
little,
|
||||
assert(if size_dec == 0 { is_compressed == 0 } else { true }),
|
||||
import(count: u32, arc_size: u32)
|
||||
)]
|
||||
#[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)]
|
||||
// we have to compute this via shenanigans because it's not otherwise represented.
|
||||
// we can't just use prs::Decompressor::get_sizes because of event8006_sceneE.one, noted above.
|
||||
#[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))]
|
||||
pub data_offset: FilePtr<u32, ()>,
|
||||
pub data: FilePtr<u32, Vec<u8>>, // TODO: compute size by finding PRS length?
|
||||
pub is_compressed: u32,
|
||||
}
|
||||
|
||||
|
@ -64,8 +111,11 @@ 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,
|
||||
"ShadOneEntry{{is_compressed: {}, name: {:?}, size_dec: {}, _size_cmp: {}, ..}}",
|
||||
self.is_compressed != 0,
|
||||
self.name,
|
||||
self.size_dec,
|
||||
self._size_cmp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +135,17 @@ impl OneArchive for ShadOne {
|
|||
}
|
||||
|
||||
fn unpack(self) -> Box<dyn Iterator<Item = (PathBuf, Vec<u8>)>> {
|
||||
todo!()
|
||||
Box::new(self.entries.into_iter().map(|entry| {
|
||||
let key = PathBuf::from(entry.name);
|
||||
if entry.is_compressed == 0 {
|
||||
(key, entry.data.into_inner())
|
||||
} else {
|
||||
let dec_buf =
|
||||
prs::Decompressor::new(entry.data.into_inner(), Some(entry.size_dec as usize))
|
||||
.decompress();
|
||||
(key, dec_buf)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn entries(&self) -> Vec<&dyn OneArchiveEntry> {
|
||||
|
|
2
prs-rust
2
prs-rust
|
@ -1 +1 @@
|
|||
Subproject commit 8ee6b274c0180d5d58ffa00f815a84cc0780603a
|
||||
Subproject commit c5a4269779ca926114065745f0691f4669c7d2bd
|
Loading…
Reference in New Issue