Add pak package cmd; reorganization & fixes

This commit is contained in:
Luke Street
2023-02-11 17:38:38 -05:00
parent dbbdf92bdd
commit ff4a4047b2
13 changed files with 752 additions and 294 deletions
Generated
+15 -15
View File
@@ -221,21 +221,6 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "paktool-rs"
version = "0.1.0"
dependencies = [
"anyhow",
"argh",
"binrw",
"binrw_derive",
"byteorder",
"env_logger",
"log",
"memmap2",
"uuid",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
@@ -271,6 +256,21 @@ version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "retrotool"
version = "0.1.0"
dependencies = [
"anyhow",
"argh",
"binrw",
"binrw_derive",
"byteorder",
"env_logger",
"log",
"memmap2",
"uuid",
]
[[package]]
name = "rustix"
version = "0.36.8"
+1 -1
View File
@@ -1,5 +1,5 @@
[package]
name = "paktool-rs"
name = "retrotool"
version = "0.1.0"
edition = "2021"
+10
View File
@@ -1 +1,11 @@
pub mod pak;
pub mod txtr;
use argh::FromArgs;
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
pub enum SubCommand {
Pak(pak::Args),
Txtr(txtr::Args),
}
+372 -272
View File
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
use std::path::PathBuf;
use anyhow::{ensure, Result};
use argh::FromArgs;
use binrw::Endian;
use crate::{
format::{chunk::ChunkDescriptor, rfrm::FormDescriptor, FourCC},
util::file::map_file,
};
// Texture
pub const K_FORM_TXTR: FourCC = FourCC(*b"TXTR");
#[derive(FromArgs, PartialEq, Debug)]
/// process TXTR files
#[argh(subcommand, name = "txtr")]
pub struct Args {
#[argh(subcommand)]
command: SubCommand,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum SubCommand {
Convert(ConvertArgs),
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
/// converts a TXTR file
#[argh(subcommand, name = "convert")]
pub struct ConvertArgs {
#[argh(positional)]
/// input file
input: PathBuf,
#[argh(positional)]
/// output directory
output: PathBuf,
}
pub fn run(args: Args) -> Result<()> {
match args.command {
SubCommand::Convert(c_args) => convert(c_args),
}
}
struct TextureHeader {}
struct TextureMeta {}
fn convert(args: ConvertArgs) -> Result<()> {
let mmap = map_file(args.input)?;
let (meta, meta_data, remain) = ChunkDescriptor::slice(&mmap, Endian::Little)?;
ensure!(meta.id == *b"META");
let (desc, data, _) = FormDescriptor::slice(remain, Endian::Little)?;
ensure!(desc.id == K_FORM_TXTR);
ensure!(desc.version == 47);
let (desc, head_data, remain) = ChunkDescriptor::slice(data, Endian::Little)?;
ensure!(desc.id == *b"HEAD");
todo!()
}
+30
View File
@@ -0,0 +1,30 @@
use binrw::binrw;
use uuid::Uuid;
use crate::format::FourCC;
// Asset directory
pub const K_CHUNK_ADIR: FourCC = FourCC(*b"ADIR");
#[binrw]
#[derive(Clone, Debug, Default)]
pub struct AssetDirectory {
#[bw(try_calc = entries.len().try_into())]
pub entry_count: u32,
#[br(count = entry_count)]
pub entries: Vec<AssetDirectoryEntry>,
}
#[binrw]
#[derive(Clone, Debug)]
pub struct AssetDirectoryEntry {
pub asset_type: FourCC,
#[br(map = Uuid::from_u128)]
#[bw(map = Uuid::as_u128)]
pub asset_id: Uuid,
pub version: u32,
pub other_version: u32,
pub offset: u64,
pub decompressed_size: u64,
pub size: u64,
}
+77
View File
@@ -0,0 +1,77 @@
use std::io::{Read, Seek, SeekFrom, Write};
use anyhow::{anyhow, Result};
use binrw::{binrw, io::Cursor, BinReaderExt, BinResult, BinWriterExt, Endian};
use crate::format::{
adir::{AssetDirectory, K_CHUNK_ADIR},
meta::{Metadata, K_CHUNK_META},
strg::{StringTable, K_CHUNK_STRG},
FourCC,
};
#[binrw]
#[derive(Clone, Debug)]
pub struct ChunkDescriptor {
pub id: FourCC,
pub size: u64,
pub unk: u32,
// game skips this amount of bytes before continuing
// but always 0?
pub skip: u64,
}
pub const CHUNK_DESCRIPTOR_SIZE: usize = 24;
impl ChunkDescriptor {
#[inline]
pub fn read<R: Read + Seek>(reader: &mut R, e: Endian) -> BinResult<Self> {
let desc: ChunkDescriptor = reader.read_type(e)?;
reader.seek(SeekFrom::Current(desc.skip as i64))?;
Ok(desc)
}
#[inline]
pub fn slice(data: &[u8], e: Endian) -> BinResult<(Self, &[u8], &[u8])> {
let mut reader = Cursor::new(data);
let header = Self::read(&mut reader, e)?;
let start = reader.position();
let slice = &data[start as usize..(start + header.size) as usize];
let remain = &data[(start + header.size) as usize..];
Ok((header, slice, remain))
}
pub fn write<W: Write + Seek, CB>(&mut self, w: &mut W, e: Endian, mut cb: CB) -> Result<()>
where CB: FnMut(&mut W) -> Result<()> {
let form_pos = w.stream_position()?;
w.write_type(self, e)?;
let data_pos = w.stream_position()?;
cb(w)?;
let end_pos = w.stream_position()?;
w.seek(SeekFrom::Start(form_pos))?;
self.size = end_pos - data_pos;
w.write_type(self, e)?;
w.seek(SeekFrom::Start(end_pos))?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum ChunkType {
AssetDirectory(AssetDirectory),
Metadata(Metadata),
StringTable(StringTable),
}
impl ChunkType {
#[inline]
pub fn read(data: &[u8], kind: FourCC, e: Endian) -> Result<Self> {
let mut reader = Cursor::new(data);
match kind {
K_CHUNK_ADIR => Ok(Self::AssetDirectory(reader.read_type(e)?)),
K_CHUNK_META => Ok(Self::Metadata(reader.read_type(e)?)),
K_CHUNK_STRG => Ok(Self::StringTable(reader.read_type(e)?)),
_ => Err(anyhow!("Unknown chunk type {:?}", kind)),
}
}
}
+25
View File
@@ -0,0 +1,25 @@
use binrw::binrw;
use uuid::Uuid;
use crate::format::FourCC;
// Metadata
pub const K_CHUNK_META: FourCC = FourCC(*b"META");
#[binrw]
#[derive(Clone, Debug, Default)]
pub struct Metadata {
#[bw(try_calc = entries.len().try_into())]
pub entry_count: u32,
#[br(count = entry_count)]
pub entries: Vec<MetadataEntry>,
}
#[binrw]
#[derive(Clone, Debug)]
pub struct MetadataEntry {
#[br(map = Uuid::from_u128)]
#[bw(map = Uuid::as_u128)]
pub asset_id: Uuid,
pub offset: u32,
}
+47
View File
@@ -0,0 +1,47 @@
pub mod adir;
pub mod chunk;
pub mod meta;
pub mod rfrm;
pub mod strg;
use std::fmt::{Debug, Display, Formatter, Write};
use binrw::binrw;
use crate::array_ref;
#[binrw]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct FourCC(pub [u8; 4]);
impl FourCC {
#[inline]
const fn swap(self) -> Self { Self([self.0[3], self.0[2], self.0[1], self.0[0]]) }
}
impl Display for FourCC {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for c in self.0 {
f.write_char(c as char)?;
}
Ok(())
}
}
impl Debug for FourCC {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_char('"')?;
for c in self.0 {
f.write_char(c as char)?;
}
f.write_char('"')?;
Ok(())
}
}
impl PartialEq<[u8; 4]> for FourCC {
fn eq(&self, other: &[u8; 4]) -> bool { &self.0 == other }
}
#[inline]
pub fn peek_four_cc(data: &[u8]) -> FourCC { FourCC(*array_ref!(data, 0, 4)) }
+55
View File
@@ -0,0 +1,55 @@
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
use anyhow::Result;
use binrw::{binrw, BinReaderExt, BinResult, BinWriterExt, Endian};
use crate::format::FourCC;
// Resource format
pub const K_CHUNK_RFRM: FourCC = FourCC(*b"RFRM");
// Package file
pub const K_FORM_PAK: FourCC = FourCC(*b"PACK");
// Table of contents
pub const K_FORM_TOC: FourCC = FourCC(*b"TOCC");
#[binrw]
#[brw(magic = b"RFRM")]
#[derive(Clone, Debug)]
pub struct FormDescriptor {
pub size: u64,
pub unk1: u64,
pub id: FourCC,
pub version: u32,
pub other_version: u32,
}
impl FormDescriptor {
#[inline]
pub fn read<R: Read + Seek>(reader: &mut R, e: Endian) -> BinResult<Self> {
reader.read_type(e)
}
#[inline]
pub fn slice(data: &[u8], e: Endian) -> BinResult<(Self, &[u8], &[u8])> {
let mut reader = Cursor::new(data);
let header = Self::read(&mut reader, e)?;
let start = reader.position();
let slice = &data[start as usize..(start + header.size) as usize];
let remain = &data[(start + header.size) as usize..];
Ok((header, slice, remain))
}
pub fn write<W: Write + Seek, CB>(&mut self, w: &mut W, e: Endian, mut cb: CB) -> Result<()>
where CB: FnMut(&mut W) -> Result<()> {
let form_pos = w.stream_position()?;
w.write_type(self, e)?;
let data_pos = w.stream_position()?;
cb(w)?;
let end_pos = w.stream_position()?;
w.seek(SeekFrom::Start(form_pos))?;
self.size = end_pos - data_pos;
w.write_type(self, e)?;
w.seek(SeekFrom::Start(end_pos))?;
Ok(())
}
}
+40
View File
@@ -0,0 +1,40 @@
use binrw::binrw;
use uuid::Uuid;
use crate::format::FourCC;
// String table
pub const K_CHUNK_STRG: FourCC = FourCC(*b"STRG");
#[binrw]
#[derive(Clone, Debug, Default)]
pub struct StringTable {
#[bw(try_calc = entries.len().try_into())]
pub entry_count: u32,
#[br(count = entry_count)]
pub entries: Vec<StringTableEntry>,
}
impl StringTable {
pub fn name_for_uuid(&self, id: Uuid) -> Option<String> {
self.entries
.iter()
.find(|e| e.asset_id == id)
.map(|e| String::from_utf8(e.name.clone()).unwrap())
}
}
#[binrw]
#[derive(Clone, Debug)]
pub struct StringTableEntry {
#[br(map = FourCC::swap)]
#[bw(map = |&f| f.swap())]
pub kind: FourCC,
#[br(map = Uuid::from_u128)]
#[bw(map = Uuid::as_u128)]
pub asset_id: Uuid,
#[bw(try_calc = name.len().try_into())]
pub name_length: u32,
#[br(count = name_length)]
pub name: Vec<u8>,
}
+5 -6
View File
@@ -1,8 +1,12 @@
#![feature(cow_is_borrowed)]
mod argh_version;
mod cmd;
mod format;
mod util;
use argh::FromArgs;
use cmd::SubCommand;
#[derive(FromArgs, PartialEq, Debug)]
/// GameCube/Wii decompilation project tools.
@@ -11,12 +15,6 @@ struct TopLevel {
command: SubCommand,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum SubCommand {
Pak(cmd::pak::Args),
}
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_timestamp(None)
@@ -27,6 +25,7 @@ fn main() {
let args: TopLevel = argh_version::from_env();
let result = match args.command {
SubCommand::Pak(args) => cmd::pak::run(args),
SubCommand::Txtr(args) => cmd::txtr::run(args),
};
if let Err(e) = result {
eprintln!("Failed: {e:?}");
+12
View File
@@ -1,2 +1,14 @@
pub mod file;
pub mod lzss;
/// Creates a fixed-size array reference from a slice.
#[macro_export]
macro_rules! array_ref {
($slice:expr, $offset:expr, $size:expr) => {{
#[inline]
fn to_array<T>(slice: &[T]) -> &[T; $size] {
unsafe { &*(slice.as_ptr() as *const [_; $size]) }
}
to_array(&$slice[$offset..$offset + $size])
}};
}