start adding SSR support
This commit is contained in:
parent
cc3a4b2e09
commit
8472af4c21
|
@ -6,7 +6,7 @@ use std::fs::File;
|
|||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use binread::prelude::*;
|
||||
use one_rust::JodOne;
|
||||
use one_rust::prelude::*;
|
||||
|
||||
/// Unpack .one archives from NiGHTS: Journey of Dreams
|
||||
#[derive(FromArgs)]
|
||||
|
@ -70,10 +70,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
Action::DescribeFile(subcmd) => {
|
||||
let mut infile = File::open(&subcmd.file)?;
|
||||
let archive: JodOne = infile.read_le()?;
|
||||
let infile = File::open(&subcmd.file)?;
|
||||
let archive = read_one_archive(infile)?;
|
||||
println!("{:?}", archive);
|
||||
for entry in archive.entries {
|
||||
for entry in archive.entries() {
|
||||
println!("{:?}", entry);
|
||||
}
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
entries.push((path, data));
|
||||
}
|
||||
|
||||
let archive = JodOne::pack(entries, None);
|
||||
let outfile = File::create(&subcmd.file)?;
|
||||
archive.write(outfile)?;
|
||||
let archive = JodOne::pack(entries);
|
||||
let mut outfile = File::create(&subcmd.file)?;
|
||||
archive.write(&mut outfile)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
|
@ -5,4 +5,24 @@ extern crate binwrite;
|
|||
extern crate prs_rust;
|
||||
|
||||
mod one;
|
||||
pub use one::*;
|
||||
|
||||
pub mod prelude {
|
||||
pub use one::OneArchive;
|
||||
pub use one::dreams::*;
|
||||
pub use one::rings::*;
|
||||
pub use one::read_one_archive;
|
||||
}
|
||||
|
||||
use binread::NullString;
|
||||
|
||||
fn string_binread_helper(x: NullString) -> String {
|
||||
x.into_string()
|
||||
}
|
||||
|
||||
fn string_binwrite_helper(pad_to_len: usize) -> impl Fn(&String) -> Vec<u8> {
|
||||
move |x: &String| {
|
||||
let mut v = x.as_bytes().to_vec();
|
||||
v.resize(pad_to_len, 0);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
use binread::FilePtr;
|
||||
use binwrite::BinWrite;
|
||||
use prs_rust::prs;
|
||||
use one::{OneArchive, WriteSeek, OneArchiveEntry};
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
use std::mem::size_of;
|
||||
|
||||
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
|
||||
// 1 JodOne{version: 0xca, comment: " ", category: Default, ..}
|
||||
// 11 JodOne{version: 0xca, comment: " ", category: landData, ..}
|
||||
// 4 JodOne{version: 0xcb, comment: " ", category: landData, ..}
|
||||
// 988 JodOne{version: 0xcc, comment: " ", category: Default, ..}
|
||||
// 1579 JodOne{version: 0xcc, comment: " ", category: landData, ..}
|
||||
|
||||
const JOD_ONE_MAGIC: &'static [u8; 16] = b"ThisIsOneFile\0\0\0";
|
||||
|
||||
const SIZE_OF_JOD_ONE_HEADER: usize = JOD_ONE_MAGIC.len() + 2 * size_of::<u32>() + 32 + 132;
|
||||
const SIZE_OF_JOD_ONE_ENTRY: usize = 6 * size_of::<u32>() + 192;
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(little, magic = b"ThisIsOneFile\0\0\0")]
|
||||
#[binwrite(little)]
|
||||
pub struct JodOne {
|
||||
// usually 0xCC... archive creator version maybe?
|
||||
// but i've seen 0xCA in Test04/disp/Test04.one and 0xCB in Test05/disp/stg2010_sun.one
|
||||
pub version: u32,
|
||||
// "Default" or "landData"
|
||||
#[br(pad_size_to = 32, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(32)))]
|
||||
pub category: String,
|
||||
pub count: u32,
|
||||
// always " "
|
||||
#[br(pad_size_to = 132, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(132)))]
|
||||
pub comment: String,
|
||||
#[br(count = count)]
|
||||
pub entries: Vec<JodOneEntry>
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(little, assert(id_again == id))]
|
||||
#[binwrite(little)]
|
||||
pub struct JodOneEntry {
|
||||
pub id: u32,
|
||||
pub size_cmp: u32,
|
||||
pub is_compressed: u32,
|
||||
pub size_dec: u32,
|
||||
pub id_again: u32,
|
||||
#[br(count = size_cmp)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<u8>>| x.ptr))]
|
||||
pub data: FilePtr<u32, Vec<u8>>,
|
||||
#[br(pad_size_to = 192, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(192)))]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for JodOne {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JodOne{{version: 0x{:x}, comment: {:?}, category: {}, count: {}, ..}}", self.version, self.comment, self.category, self.count)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for JodOneEntry {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"JodOneEntry{{id: {}, is_compressed: {}, name: {:?}, size_cmp: {}, size_dec: {}, ..}}",
|
||||
self.id, self.is_compressed != 0, self.name, self.size_cmp, self.size_dec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl OneArchiveEntry for JodOneEntry {}
|
||||
|
||||
impl OneArchive for JodOne {
|
||||
fn write(&self, mut writer: &mut dyn WriteSeek) -> std::io::Result<()> {
|
||||
writer.write(JOD_ONE_MAGIC)?;
|
||||
BinWrite::write(&self, &mut writer)?;
|
||||
for entry in &self.entries {
|
||||
let loc = writer.seek(std::io::SeekFrom::Current(0)).unwrap() as usize;
|
||||
let padding = vec![0; entry.data.ptr as usize - loc];
|
||||
writer.write(&padding)?;
|
||||
writer.write(entry.data.value.as_ref().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pack(inputs: impl IntoIterator<Item=(PathBuf, Vec<u8>)>) -> Self where Self: Sized {
|
||||
let mut entries = Vec::new();
|
||||
for (id, (path, data)) in inputs.into_iter().enumerate() {
|
||||
let cmp = prs::Compressor::new(&data, None).compress();
|
||||
let name = path.to_string_lossy().replace(std::path::MAIN_SEPARATOR, "\\");
|
||||
if cmp.len() < data.len() {
|
||||
entries.push(JodOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: cmp.len() as u32,
|
||||
is_compressed: 1,
|
||||
size_dec: data.len() as u32,
|
||||
id_again: id as u32,
|
||||
data: FilePtr { ptr: 0, value: Some(cmp) },
|
||||
name,
|
||||
});
|
||||
} else {
|
||||
entries.push(JodOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: data.len() as u32,
|
||||
is_compressed: 0,
|
||||
size_dec: 0,
|
||||
id_again: id as u32,
|
||||
data: FilePtr { ptr: 0, value: Some(data) },
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut loc = (SIZE_OF_JOD_ONE_HEADER + SIZE_OF_JOD_ONE_ENTRY * entries.len()) as u32;
|
||||
for entry in &mut entries {
|
||||
loc = ((loc + 15) / 16) * 16; // round up to 16 byte alignment
|
||||
entry.data.ptr = loc;
|
||||
loc += entry.data.value.as_ref().unwrap().len() as u32;
|
||||
}
|
||||
|
||||
// user can overwrite string/version fields afterward
|
||||
JodOne {
|
||||
version: 0xCC,
|
||||
category: "Default".to_string(),
|
||||
count: entries.len() as u32,
|
||||
comment: " ".to_string(),
|
||||
entries,
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack(self) -> Box<dyn Iterator<Item=(PathBuf, Vec<u8>)>> {
|
||||
Box::new(self.entries.into_iter().map(|entry| {
|
||||
let key = PathBuf::from(entry.name.replace('\\', std::path::MAIN_SEPARATOR.encode_utf8(&mut [0u8; 4])));
|
||||
if entry.is_compressed == 0 || entry.size_dec == 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> {
|
||||
self.entries.iter().map(|x| {
|
||||
let y: &dyn OneArchiveEntry = x;
|
||||
y
|
||||
}).collect()
|
||||
}
|
||||
}
|
|
@ -1,159 +1,35 @@
|
|||
use binread::{FilePtr, NullString};
|
||||
use prs_rust::prs;
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
use std::mem::size_of;
|
||||
use std::io::{Write, Seek};
|
||||
use binwrite::BinWrite;
|
||||
use std::io::{Seek, Write, Read};
|
||||
use binread::BinReaderExt;
|
||||
use std::error::Error;
|
||||
use binread::io::SeekFrom;
|
||||
use std::fmt::Debug;
|
||||
|
||||
fn string_binread_helper(x: NullString) -> String {
|
||||
x.into_string()
|
||||
pub(crate) mod dreams;
|
||||
pub(crate) mod rings;
|
||||
|
||||
pub trait WriteSeek: Write + Seek {}
|
||||
impl<T> WriteSeek for T where T: Write + Seek {}
|
||||
|
||||
pub trait OneArchive: Debug {
|
||||
fn write(&self, writer: &mut dyn WriteSeek) -> std::io::Result<()>;
|
||||
fn pack(inputs: impl IntoIterator<Item=(PathBuf, Vec<u8>)>) -> Self where Self: Sized;
|
||||
fn unpack(self) -> Box<dyn Iterator<Item=(PathBuf, Vec<u8>)>>;
|
||||
fn entries(&self) -> Vec<&dyn OneArchiveEntry>;
|
||||
}
|
||||
|
||||
fn string_binwrite_helper(pad_to_len: usize) -> impl Fn(&String) -> Vec<u8> {
|
||||
move |x: &String| {
|
||||
let mut v = x.as_bytes().to_vec();
|
||||
v.resize(pad_to_len, 0);
|
||||
v
|
||||
pub trait OneArchiveEntry: Debug {}
|
||||
|
||||
pub fn read_one_archive(mut reader: impl Read + Seek) -> Result<Box<dyn OneArchive>, Box<dyn Error>> {
|
||||
let rewind = reader.seek(SeekFrom::Current(0))?;
|
||||
match reader.read_le::<dreams::JodOne>() {
|
||||
Ok(one) => return Ok(Box::new(one)),
|
||||
Err(e) => info!("Not a NiGHTS Journey of Dreams archive: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// 1 JodOne{version: 0xca, comment: " ", category: Default, ..}
|
||||
// 11 JodOne{version: 0xca, comment: " ", category: landData, ..}
|
||||
// 4 JodOne{version: 0xcb, comment: " ", category: landData, ..}
|
||||
// 988 JodOne{version: 0xcc, comment: " ", category: Default, ..}
|
||||
// 1579 JodOne{version: 0xcc, comment: " ", category: landData, ..}
|
||||
|
||||
const JOD_ONE_MAGIC: &'static [u8; 16] = b"ThisIsOneFile\0\0\0";
|
||||
|
||||
const SIZE_OF_JOD_ONE_HEADER: usize = JOD_ONE_MAGIC.len() + 2 * size_of::<u32>() + 32 + 132;
|
||||
const SIZE_OF_JOD_ONE_ENTRY: usize = 6 * size_of::<u32>() + 192;
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(little, magic = b"ThisIsOneFile\0\0\0")]
|
||||
#[binwrite(little)]
|
||||
pub struct JodOne {
|
||||
// usually 0xCC... archive creator version maybe?
|
||||
// but i've seen 0xCA in Test04/disp/Test04.one and 0xCB in Test05/disp/stg2010_sun.one
|
||||
pub version: u32,
|
||||
// "Default" or "landData"
|
||||
#[br(pad_size_to = 32, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(32)))]
|
||||
pub category: String,
|
||||
pub count: u32,
|
||||
// always " "
|
||||
#[br(pad_size_to = 132, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(132)))]
|
||||
pub comment: String,
|
||||
#[br(count = count)]
|
||||
pub entries: Vec<JodOneEntry>
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(little, assert(id_again == id))]
|
||||
#[binwrite(little)]
|
||||
pub struct JodOneEntry {
|
||||
pub id: u32,
|
||||
pub size_cmp: u32,
|
||||
pub is_compressed: u32,
|
||||
pub size_dec: u32,
|
||||
pub id_again: u32,
|
||||
#[br(count = size_cmp)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<u8>>| x.ptr))]
|
||||
pub data: FilePtr<u32, Vec<u8>>,
|
||||
#[br(pad_size_to = 192, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(192)))]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for JodOne {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JodOne{{version: 0x{:x}, comment: {:?}, category: {}, count: {}, ..}}", self.version, self.comment, self.category, self.count)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for JodOneEntry {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"JodOneEntry{{id: {}, is_compressed: {}, name: {:?}, size_cmp: {}, size_dec: {}, ..}}",
|
||||
self.id, self.is_compressed != 0, self.name.to_string(), self.size_cmp, self.size_dec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl JodOne {
|
||||
pub fn write(&self, mut writer: impl Write + Seek) -> std::io::Result<()> {
|
||||
writer.write(JOD_ONE_MAGIC)?;
|
||||
BinWrite::write(&self, &mut writer)?;
|
||||
for entry in &self.entries {
|
||||
let loc = writer.seek(std::io::SeekFrom::Current(0)).unwrap() as usize;
|
||||
let padding = vec![0; entry.data.ptr as usize - loc];
|
||||
writer.write(&padding)?;
|
||||
writer.write(entry.data.value.as_ref().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pack(inputs: impl IntoIterator<Item = (PathBuf, Vec<u8>)>, category: Option<&str>) -> Self {
|
||||
let mut entries = Vec::new();
|
||||
for (id, (path, data)) in inputs.into_iter().enumerate() {
|
||||
let cmp = prs::Compressor::new(&data, None).compress();
|
||||
let name = path.to_string_lossy().replace(std::path::MAIN_SEPARATOR, "\\");
|
||||
if cmp.len() < data.len() {
|
||||
entries.push(JodOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: cmp.len() as u32,
|
||||
is_compressed: 1,
|
||||
size_dec: data.len() as u32,
|
||||
id_again: id as u32,
|
||||
data: FilePtr { ptr: 0, value: Some(cmp) },
|
||||
name,
|
||||
});
|
||||
} else {
|
||||
entries.push(JodOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: data.len() as u32,
|
||||
is_compressed: 0,
|
||||
size_dec: 0,
|
||||
id_again: id as u32,
|
||||
data: FilePtr { ptr: 0, value: Some(data) },
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut loc = (SIZE_OF_JOD_ONE_HEADER + SIZE_OF_JOD_ONE_ENTRY * entries.len()) as u32;
|
||||
for entry in &mut entries {
|
||||
loc = ((loc + 15) / 16) * 16; // round up to 16 byte alignment
|
||||
entry.data.ptr = loc;
|
||||
loc += entry.data.value.as_ref().unwrap().len() as u32;
|
||||
}
|
||||
|
||||
JodOne {
|
||||
version: 0xCC,
|
||||
category: category.unwrap_or("Default").to_string(),
|
||||
count: entries.len() as u32,
|
||||
comment: " ".to_string(),
|
||||
entries,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> impl Iterator<Item = (PathBuf, Vec<u8>)> {
|
||||
self.entries.into_iter().map(|entry| {
|
||||
debug!("{:?}", entry);
|
||||
let key = PathBuf::from(entry.name.replace('\\', std::path::MAIN_SEPARATOR.encode_utf8(&mut [0u8; 4])));
|
||||
if entry.is_compressed == 0 || entry.size_dec == 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)
|
||||
}
|
||||
})
|
||||
reader.seek(SeekFrom::Start(rewind))?;
|
||||
match reader.read_be::<rings::SsrOne>() {
|
||||
Ok(one) => return Ok(Box::new(one)),
|
||||
Err(e) => info!("Not a Sonic and the Secret Rings archive: {}", e),
|
||||
}
|
||||
Err("No valid .one archive found".into())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
use binread::FilePtr;
|
||||
use binwrite::BinWrite;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
use crate::string_binread_helper;
|
||||
use crate::string_binwrite_helper;
|
||||
use one::{OneArchive, WriteSeek, OneArchiveEntry};
|
||||
use prs_rust::prs;
|
||||
use std::path::PathBuf;
|
||||
use std::fmt::Formatter;
|
||||
use std::mem::size_of;
|
||||
|
||||
const SIZE_OF_SSR_ONE_HEADER: usize = 4 * size_of::<u32>(); // 0x10
|
||||
const SIZE_OF_SSR_ONE_ENTRY: usize = 4 * size_of::<u32>() + 32; // 0x30
|
||||
|
||||
// stats on padding and size_dec:
|
||||
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;
|
||||
// $ fd one.txt | xargs cat | sed 's/count: [0-9]\+, //' | grep '^SsrOne{' | sort | uniq -c
|
||||
// 284 SsrOne{_unknown: 0x0, ..}
|
||||
// $ fd one.txt | xargs cat | grep '^SsrOneEntry{' | sed \
|
||||
// > -e 's/ [1-9][0-9]*, / 1+, /g' \
|
||||
// > -e 's/id: [01]+\?, name: "[^"]\+", //' | sort | uniq -c
|
||||
// 36 SsrOneEntry{size_cmp: 1+, size_dec: 0, ..}
|
||||
// 5105 SsrOneEntry{size_cmp: 1+, size_dec: 1+, ..}
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(big, assert(entries.ptr == 0x10 && data_offset == entries.ptr + count * 0x30))]
|
||||
#[binwrite(big)]
|
||||
pub struct SsrOne {
|
||||
/// number of files stored in the .one
|
||||
pub count: u32,
|
||||
/// the location of the table of contents. usually 0x10, just after this header
|
||||
#[br(count = count)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<_>>| x.ptr))]
|
||||
pub entries: FilePtr<u32, Vec<SsrOneEntry>>,
|
||||
/// location of the end of the toc/beginning of data. TODO: assertion
|
||||
pub data_offset: u32,
|
||||
/// always 0
|
||||
pub _padding: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinWrite)]
|
||||
#[br(big)]
|
||||
#[binwrite(big)]
|
||||
pub struct SsrOneEntry {
|
||||
#[br(pad_size_to = 32, map = string_binread_helper)]
|
||||
#[binwrite(preprocessor(string_binwrite_helper(32)))]
|
||||
pub name: String,
|
||||
pub id: u32,
|
||||
// note: seek_before/restore_position shenanigans because we need size_cmp for data's count
|
||||
#[br(seek_before = SeekFrom::Current(4), restore_position)]
|
||||
pub size_cmp: u32,
|
||||
#[br(count = size_cmp)]
|
||||
#[binwrite(preprocessor(|x: &FilePtr<u32, Vec<u8>>| x.ptr))]
|
||||
pub data: FilePtr<u32, Vec<u8>>,
|
||||
#[br(seek_before = SeekFrom::Current(4))]
|
||||
pub size_dec: u32,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SsrOne {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "SsrOne{{count: {}, _unknown: 0x{:x}, ..}}", self.count, self._padding)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SsrOneEntry {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SsrOneEntry{{id: {}, name: {:?}, size_cmp: {}, size_dec: {}, ..}}",
|
||||
self.id, self.name, self.size_cmp, self.size_dec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl OneArchiveEntry for SsrOneEntry {}
|
||||
|
||||
impl OneArchive for SsrOne {
|
||||
fn write(&self, mut writer: &mut dyn WriteSeek) -> std::io::Result<()> {
|
||||
BinWrite::write(&self, &mut writer)?;
|
||||
for entry in self.entries.value.as_ref().unwrap() {
|
||||
let loc = writer.seek(std::io::SeekFrom::Current(0)).unwrap() as usize;
|
||||
let padding = vec![0; entry.data.ptr as usize - loc];
|
||||
writer.write(&padding)?;
|
||||
writer.write(entry.data.value.as_ref().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pack(inputs: impl IntoIterator<Item=(PathBuf, Vec<u8>)>) -> Self where Self: Sized {
|
||||
let mut entries = Vec::new();
|
||||
for (id, (path, data)) in inputs.into_iter().enumerate() {
|
||||
let cmp = prs::Compressor::new(&data, None).compress();
|
||||
let name = path.to_string_lossy().to_uppercase();
|
||||
if cmp.len() < data.len() {
|
||||
entries.push(SsrOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: cmp.len() as u32,
|
||||
size_dec: data.len() as u32,
|
||||
data: FilePtr { ptr: 0, value: Some(cmp) },
|
||||
name,
|
||||
});
|
||||
} else {
|
||||
entries.push(SsrOneEntry {
|
||||
id: id as u32,
|
||||
size_cmp: data.len() as u32,
|
||||
size_dec: 0,
|
||||
data: FilePtr { ptr: 0, value: Some(data) },
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let data_offset = (SIZE_OF_SSR_ONE_HEADER + SIZE_OF_SSR_ONE_ENTRY * entries.len()) as u32;
|
||||
let mut loc = data_offset;
|
||||
for entry in &mut entries {
|
||||
loc = ((loc + 3) / 4) * 4; // round up to 4 byte alignment
|
||||
entry.data.ptr = loc;
|
||||
loc += entry.data.value.as_ref().unwrap().len() as u32;
|
||||
}
|
||||
|
||||
// user can overwrite string/version fields afterward
|
||||
SsrOne {
|
||||
count: entries.len() as u32,
|
||||
entries: FilePtr {
|
||||
ptr: SIZE_OF_SSR_ONE_HEADER as u32,
|
||||
value: Some(entries),
|
||||
},
|
||||
data_offset,
|
||||
_padding: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack(self) -> Box<dyn Iterator<Item=(PathBuf, Vec<u8>)>> {
|
||||
Box::new(self.entries.value.unwrap().into_iter().map(|entry| {
|
||||
let key = PathBuf::from(entry.name);
|
||||
if entry.size_dec == 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> {
|
||||
self.entries.iter().map(|x| {
|
||||
let y: &dyn OneArchiveEntry = x;
|
||||
y
|
||||
}).collect()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue