diff --git a/Cargo.lock b/Cargo.lock index 3b6f517..f37da61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,122 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "binread" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258d2d861723c05ad02956cae4878ceddb5e05b0dc2841ca73a3479ab7a46f4d" +dependencies = [ + "binread_derive", +] + +[[package]] +name = "binread_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63ec80ac459fd10ae68854c26ee1c5ab5ac93866dba8f8a95cabbfeaa79b284" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "binwrite" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42c3da1db1f384ad643e27cb6ba4f08f2a7cd8753bef8dd95c39fe45d7a474c8" +dependencies = [ + "binwrite_derive", + "paste", +] + +[[package]] +name = "binwrite_derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476f17c19ce04226ba997c229ce8fb96eaef1c0e0745ec91d8df35f723aae353" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" [[package]] name = "one-rust" version = "0.1.0" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "prs-rust 0.1.0", + "binread", + "binwrite", + "byteorder", + "prs-rust", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", ] [[package]] name = "prs-rust" version = "0.1.0" -[metadata] -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/Cargo.toml b/Cargo.toml index 2889cf4..3b6125e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" authors = ["lifning "] [dependencies] +binread = "1" +binwrite = "0.2" byteorder = "1" prs-rust = { path = "../prs-rust" } diff --git a/examples/onear.rs b/examples/onear.rs new file mode 100644 index 0000000..26c7024 --- /dev/null +++ b/examples/onear.rs @@ -0,0 +1,21 @@ +extern crate one_rust; +extern crate binread; + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use binread::prelude::*; +use one_rust::JodOne; + +fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + for arg in &args[1..] { + let mut infile = File::open(arg)?; + let mut archive: JodOne = infile.read_le()?; + let entries = archive.extract(); + for (name, data) in entries { + File::create(name)?.write_all(&data)?; + } + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d6f421e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +extern crate byteorder; +extern crate prs_rust; +#[macro_use] extern crate binread; +#[macro_use] extern crate binwrite; + +mod one; +pub use one::*; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 23434de..0000000 --- a/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::env; -use std::fs::File; - -pub mod one; - -fn main() -> std::io::Result<()> { - let args: Vec = env::args().collect(); - for arg in &args[1..] { - let mut infile = File::open(arg)?; - one::OneJodArchive::from_file(&mut infile)?.extract()?; - } - Ok(()) -} diff --git a/src/one/mod.rs b/src/one/mod.rs index bb29779..ec9c2d9 100644 --- a/src/one/mod.rs +++ b/src/one/mod.rs @@ -1,146 +1,46 @@ -extern crate byteorder; -extern crate prs_rust; +use binread::{FilePtr, NullString}; +use prs_rust::prs; +use std::collections::BTreeMap; +use std::path::PathBuf; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; -use self::byteorder::{ReadBytesExt, LittleEndian}; -use self::prs_rust::prs; -use std::fs::File; -use std::io::{Read, Seek, SeekFrom, Write}; -use std::str; - -pub struct OneJodArchive { - read_handle: R, - _header: OneJodHead, - entries: Vec, +#[derive(BinRead)] +#[br(little, magic = b"ThisIsOneFile\0\0\0")] +pub struct JodOne { + _unknown1: u32, + #[br(pad_size_to = 32)] + pub category: NullString, + #[br(pad_after = 128)] + pub count: u32, + #[br(count = count)] + pub entries: Vec } -impl OneJodArchive { - pub fn from_file(mut input: R) -> std::io::Result> { - let mut magic = [0; 16]; - input.read_exact(&mut magic)?; - let _unknown1 = input.read_u32::()?; - let mut category = [0; 32]; - input.read_exact(&mut category)?; - let file_count = input.read_u32::()?; - let mut reserved = [0; 128]; - input.read_exact(&mut reserved)?; - let head = OneJodHead { - magic, - _unknown1, - category, - file_count, - reserved, - }; - assert_eq!(head.magic(), b"ThisIsOneFile\0\0\0"); +#[derive(BinRead)] +#[br(little, assert(id_again == id))] +pub struct JodOneEntry { + pub _unknown1: u32, + pub id: u32, + pub size_cmp: u32, + pub _unknown2: u32, + pub size_dec: u32, + pub id_again: u32, + #[br(count = size_cmp)] + pub data: FilePtr>, + #[br(pad_size_to = 188)] + pub name: NullString, +} - let mut entries = Vec::new(); - for i in 0..head.file_count() { - let _unknown1 = input.read_u32::()?; - let id = input.read_u32::()?; - let size_cmp = input.read_u32::()?; - let _unknown2 = input.read_u32::()?; - let size_dec = input.read_u32::()?; - let id_again = input.read_u32::()?; - let offset = input.read_u32::()?; - let mut file_name = [0u8; 188]; - input.read_exact(&mut file_name)?; - let entry = OneJodEntry { - _unknown1, - id, - size_cmp, - _unknown2, - size_dec, - id_again, - offset, - file_name, - }; - assert_eq!(entry.id(), i); - entries.push(entry); - } - - Ok(OneJodArchive { - read_handle: input, - _header: head, - entries, - }) - } - - pub fn extract(&mut self) -> std::io::Result<()> { +impl JodOne { + pub fn extract(&mut self) -> BTreeMap> { + let mut map = BTreeMap::new(); for entry in &self.entries { - let mut cmp_buf = vec![0u8; entry.size_cmp() as usize]; - self.read_handle.seek(SeekFrom::Start(entry.offset() as u64))?; - self.read_handle.read_exact(cmp_buf.as_mut_slice())?; - let dec_buf = prs::decompress::Decompressor::new(&cmp_buf).decompress(); - println!("{}", entry.file_name()); - let mut file_out = File::create(entry.file_name()).unwrap(); - file_out.write_all(&dec_buf)?; + // TODO: improve prs crate to use size_dec hint? + let dec_buf = prs::decompress::Decompressor::new(&*entry.data).decompress(); + map.insert(PathBuf::from(OsStr::from_bytes(&entry.name.0)), dec_buf); } - Ok(()) + map } } -struct OneJodHead { - magic: [u8; 16], - _unknown1: u32, - category: [u8; 32], - file_count: u32, - reserved: [u8; 128], -} - -#[allow(unused)] -impl OneJodHead { - fn magic(&self) -> &[u8] { &self.magic } - fn magic_mut(&mut self) -> &mut [u8] { &mut self.magic } - - fn _unknown1(&self) -> u32 { self._unknown1 } - fn set_unknown1(&mut self, n: u32) { self._unknown1 = n; } - - // NB: may not really be utf8? should probably be ascii, though... - fn category(&self) -> &str { str::from_utf8(&self.category).unwrap() } - fn category_mut(&mut self) -> &mut str { str::from_utf8_mut(&mut self.category).unwrap() } - fn set_category(&mut self, n: &str) { self.category.copy_from_slice(n.as_bytes()); } - - fn file_count(&self) -> u32 { self.file_count } - fn set_file_count(&mut self, n: u32) { self.file_count = n; } - - fn reserved(&self) -> &[u8] { &self.reserved } - fn reserved_mut(&mut self) -> &mut [u8] { &mut self.reserved } -} - -struct OneJodEntry { - _unknown1: u32, - id: u32, - size_cmp: u32, - _unknown2: u32, - size_dec: u32, - id_again: u32, - offset: u32, - file_name: [u8; 188], -} - -#[allow(unused)] -impl OneJodEntry { - fn _unknown1(&self) -> u32 { self._unknown1 } - fn set_unknown1(&mut self, n: u32) { self._unknown1 = n; } - - fn id(&self) -> u32 { assert_eq!(self.id, self.id_again); self.id } - fn set_id(&mut self, n: u32) { self.id = n; self.id_again = n; } - - fn size_cmp(&self) -> u32 { self.size_cmp } - fn set_size_cmp(&mut self, n: u32) { self.size_cmp = n; } - - fn _unknown2(&self) -> u32 { self._unknown2 } - fn set_unknown2(&mut self, n: u32) { self._unknown2 = n; } - - fn size_dec(&self) -> u32 { self.size_dec } - fn set_size_dec(&mut self, n: u32) { self.size_dec = n; } - - fn offset(&self) -> u32 { self.offset } - fn set_offset(&mut self, n: u32) { self.offset = n; } - - fn file_name(&self) -> &str { - let idx = self.file_name.iter().position(|x| *x == 0u8).unwrap(); - str::from_utf8(&self.file_name[0..idx]).unwrap() - } - fn file_name_mut(&mut self) -> &mut str { str::from_utf8_mut(&mut self.file_name).unwrap() } - fn set_file_name(&mut self, n: &str) { self.file_name.copy_from_slice(n.as_bytes()); } -}