archive creation!
This commit is contained in:
parent
c972b7a7c1
commit
ce190ea641
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue