mirror of
https://github.com/PrimeDecomp/retrotool.git
synced 2026-07-12 18:18:58 -07:00
Add pak package cmd; reorganization & fixes
This commit is contained in:
Generated
+15
-15
@@ -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
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "paktool-rs"
|
||||
name = "retrotool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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!()
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)) }
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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:?}");
|
||||
|
||||
@@ -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])
|
||||
}};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user