archive creation!

This commit is contained in:
lifning 2021-01-16 19:46:23 -08:00
parent c972b7a7c1
commit ce190ea641
3 changed files with 102 additions and 33 deletions

View File

@ -19,6 +19,7 @@ struct Args {
#[argh(subcommand)]
enum Action {
ExtractFile(ExtractFile),
CreateFile(CreateFile),
DescribeFile(DescribeFile),
}
@ -40,6 +41,18 @@ struct DescribeFile {
file: PathBuf,
}
/// Create a .one archive from the given files.
#[derive(FromArgs)]
#[argh(subcommand, name = "cf")]
struct CreateFile {
/// 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 main() -> Result<(), Box<dyn std::error::Error>> {
let args: Args = argh::from_env();
match args.action {
@ -64,6 +77,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{:?}", entry);
}
}
Action::CreateFile(subcmd) => {
if subcmd.file.exists() {
return Err(format!("{:?} already exists, refusing to overwrite", subcmd.file).into());
}
let mut entries = Vec::new();
for path in subcmd.contents {
let mut data = Vec::new();
File::open(&path)?.read_to_end(&mut data)?;
entries.push((path, data));
}
let archive = JodOne::pack(entries, None);
let outfile = File::create(&subcmd.file)?;
archive.write(outfile)?;
}
}
Ok(())
}

View File

@ -1,6 +1,6 @@
#![feature(fixed_size_array)]
//#![feature(fixed_size_array)]
#[macro_use] extern crate binread;
#[macro_use] extern crate binwrite;
extern crate binwrite;
#[macro_use] extern crate log;
extern crate prs_rust;

View File

@ -2,11 +2,12 @@ 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;
fn string_binread_helper(x: NullString) -> String {
unsafe {
String::from_utf8_unchecked(x.0)
}
x.into_string()
}
fn string_binwrite_helper(pad_to_len: usize) -> impl Fn(&String) -> Vec<u8> {
@ -17,14 +18,19 @@ fn string_binwrite_helper(pad_to_len: usize) -> impl Fn(&String) -> Vec<u8> {
}
}
// stats on _unknown1 and category:
// stats on _unknown1, _unknown2, and category:
// $ find . -name \*.one -exec bash -c "onear tf "{}" > "{}".txt" \;
// $ fd one.txt | xargs cat | sed 's/count: .*/..}/' | grep 'category' | sort | uniq -c
// 1 JodOne{_unknown1: 0xca, category: Default, ..}
// 11 JodOne{_unknown1: 0xca, category: landData, ..}
// 4 JodOne{_unknown1: 0xcb, category: landData, ..}
// 988 JodOne{_unknown1: 0xcc, category: Default, ..}
// 1579 JodOne{_unknown1: 0xcc, category: landData, ..}
// $ fd one.txt | xargs cat | sed 's/count: .*/..}/' | grep '^JodOne{' | sort | uniq -c
// 1 JodOne{_unknown1: 0xca, _unknown2: " ", category: Default, ..}
// 11 JodOne{_unknown1: 0xca, _unknown2: " ", category: landData, ..}
// 4 JodOne{_unknown1: 0xcb, _unknown2: " ", category: landData, ..}
// 988 JodOne{_unknown1: 0xcc, _unknown2: " ", category: Default, ..}
// 1579 JodOne{_unknown1: 0xcc, _unknown2: " ", 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)]
@ -34,14 +40,15 @@ 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
_unknown1: u32,
// usually "Default" or "landData"
// "Default" or "landData"
#[br(pad_size_to = 32, map = string_binread_helper)]
#[binwrite(preprocessor(string_binwrite_helper(32)))]
pub category: String,
#[br(pad_after = 128)]
pub count: u32,
// always 0? is this part of the pad_after above?
pub _unknown2: u32,
// always " "
#[br(pad_size_to = 132, map = string_binread_helper)]
#[binwrite(preprocessor(string_binwrite_helper(132)))]
_unknown2: String,
#[br(count = count)]
pub entries: Vec<JodOneEntry>
}
@ -66,7 +73,7 @@ pub struct JodOneEntry {
impl std::fmt::Debug for JodOne {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "JodOne{{_unknown1: 0x{:x}, _unknown2: 0x{:x}, category: {}, count: {}, ..}}", self._unknown1, self._unknown2, self.category, self.count)
write!(f, "JodOne{{_unknown1: 0x{:x}, _unknown2: {:?}, category: {}, count: {}, ..}}", self._unknown1, self._unknown2, self.category, self.count)
}
}
@ -81,26 +88,59 @@ impl std::fmt::Debug for JodOneEntry {
}
impl JodOne {
pub fn pack(inputs: impl Iterator<Item = (PathBuf, Vec<u8>)>, category: Option<&str>) -> Self {
let mut entries = Vec::new();
for (id, (path, data)) in inputs.enumerate() {
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: path.to_string_lossy().replace(std::path::MAIN_SEPARATOR, "\\"),
})
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 {
_unknown1: 0xCC,
category: category.unwrap_or("Default").to_string(),
count: 0,
_unknown2: 0x20,
entries: vec![]
count: entries.len() as u32,
_unknown2: " ".to_string(),
entries,
}
}