Skip to content

Commit

Permalink
Add WAD support to VFS & wad commands
Browse files Browse the repository at this point in the history
  • Loading branch information
encounter committed Nov 7, 2024
1 parent 146c4d2 commit 1cc38ad
Show file tree
Hide file tree
Showing 12 changed files with 770 additions and 9 deletions.
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0"
version = "1.2.0"
version = "1.3.0"
edition = "2021"
publish = false
repository = "https://github.com/encounter/decomp-toolkit"
Expand All @@ -25,13 +25,15 @@ strip = "debuginfo"
codegen-units = 1

[dependencies]
aes = "0.8"
anyhow = { version = "1.0", features = ["backtrace"] }
ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_table" }
argp = "0.3"
base16ct = "0.2"
base64 = "0.22"
byteorder = "1.5"
typed-path = "0.9"
cbc = "0.1"
crossterm = "0.28"
cwdemangle = "1.0"
cwextab = "1.0"
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ project structure and build system that uses decomp-toolkit under the hood.
- [yay0 compress](#yay0-compress)
- [yaz0 decompress](#yaz0-decompress)
- [yaz0 compress](#yaz0-compress)
- [wad info](#wad-info)
- [wad extract](#wad-extract)
- [wad verify](#wad-verify)

## Goals

Expand Down Expand Up @@ -474,6 +477,7 @@ Supported containers:
- Disc images (see [disc info](#disc-info) for supported formats)
- RARC archives (older .arc)
- U8 archives (newer .arc)
- WAD files (Wii VC)

Supported compression formats are handled transparently:
- Yay0 (SZP) / Yaz0 (SZS)
Expand Down Expand Up @@ -562,3 +566,31 @@ $ dtk yaz0 compress input.bin -o output.bin.yaz0
# or, for batch processing
$ dtk yaz0 compress rels/* -o rels
```

### wad info

Prints information about a WAD file.

```shell
$ dtk wad info input.wad
```

### wad extract

> [!NOTE]
> [vfs cp](#vfs-cp) is more flexible and supports WAD files.
> This command is now equivalent to `dtk vfs cp input.wad: output_dir`

Extracts the contents of a WAD file.

```shell
$ dtk wad extract input.wad -o output_dir
```

### wad verify

Verifies the contents of a WAD file.

```shell
$ dtk wad verify input.wad
```
1 change: 1 addition & 0 deletions src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ pub mod rso;
pub mod shasum;
pub mod u8_arc;
pub mod vfs;
pub mod wad;
pub mod yay0;
pub mod yaz0;
108 changes: 108 additions & 0 deletions src/cmd/wad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use anyhow::Result;
use argp::FromArgs;
use size::Size;
use typed_path::Utf8NativePathBuf;

use crate::{
cmd::vfs,
util::{
path::native_path,
wad::{process_wad, verify_wad},
},
vfs::open_file,
};

#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing Wii WAD files.
#[argp(subcommand, name = "wad")]
pub struct Args {
#[argp(subcommand)]
command: SubCommand,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand)]
enum SubCommand {
Extract(ExtractArgs),
Info(InfoArgs),
Verify(VerifyArgs),
}

#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Extracts WAD file contents.
#[argp(subcommand, name = "extract")]
pub struct ExtractArgs {
#[argp(positional, from_str_fn(native_path))]
/// WAD file
file: Utf8NativePathBuf,
#[argp(option, short = 'o', from_str_fn(native_path))]
/// output directory
output: Option<Utf8NativePathBuf>,
#[argp(switch)]
/// Do not decompress files when copying.
no_decompress: bool,
#[argp(switch, short = 'q')]
/// Quiet output. Don't print anything except errors.
quiet: bool,
}

#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Views WAD file information.
#[argp(subcommand, name = "info")]
pub struct InfoArgs {
#[argp(positional, from_str_fn(native_path))]
/// WAD file
file: Utf8NativePathBuf,
}

#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Verifies WAD file integrity.
#[argp(subcommand, name = "verify")]
pub struct VerifyArgs {
#[argp(positional, from_str_fn(native_path))]
/// WAD file
file: Utf8NativePathBuf,
}

pub fn run(args: Args) -> Result<()> {
match args.command {
SubCommand::Info(c_args) => info(c_args),
SubCommand::Verify(c_args) => verify(c_args),
SubCommand::Extract(c_args) => extract(c_args),
}
}

fn info(args: InfoArgs) -> Result<()> {
let mut file = open_file(&args.file, true)?;
let wad = process_wad(file.as_mut())?;
println!("Title ID: {}", hex::encode(wad.ticket().title_id));
println!("Title key: {}", hex::encode(wad.title_key));
println!("Fake signed: {}", wad.fake_signed);
for content in wad.contents() {
println!(
"Content {:08x}: Offset {:#X}, size {}",
content.content_index.get(),
wad.content_offset(content.content_index.get()),
Size::from_bytes(content.size.get())
);
}
Ok(())
}

fn verify(args: VerifyArgs) -> Result<()> {
let mut file = open_file(&args.file, true)?;
let wad = process_wad(file.as_mut())?;
verify_wad(&wad, file.as_mut())?;
println!("Verification successful");
Ok(())
}

fn extract(args: ExtractArgs) -> Result<()> {
let path = Utf8NativePathBuf::from(format!("{}:", args.file));
let output = args.output.unwrap_or_else(|| Utf8NativePathBuf::from("."));
vfs::cp(vfs::CpArgs {
paths: vec![path, output],
no_decompress: args.no_decompress,
quiet: args.quiet,
})
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum SubCommand {
Vfs(cmd::vfs::Args),
Yay0(cmd::yay0::Args),
Yaz0(cmd::yaz0::Args),
Wad(cmd::wad::Args),
}

// Duplicated from supports-color so we can check early.
Expand Down Expand Up @@ -181,6 +182,7 @@ fn main() {
SubCommand::Vfs(c_args) => cmd::vfs::run(c_args),
SubCommand::Yay0(c_args) => cmd::yay0::run(c_args),
SubCommand::Yaz0(c_args) => cmd::yaz0::run(c_args),
SubCommand::Wad(c_args) => cmd::wad::run(c_args),
});
if let Err(e) = result {
eprintln!("Failed: {e:?}");
Expand Down
2 changes: 2 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ pub mod nested;
pub mod nlzss;
pub mod path;
pub mod rarc;
pub mod read;
pub mod reader;
pub mod rel;
pub mod rso;
pub mod signatures;
pub mod split;
pub mod take_seek;
pub mod u8_arc;
pub mod wad;

#[inline]
pub const fn align_up(value: u32, align: u32) -> u32 { (value + (align - 1)) & !(align - 1) }
Expand Down
26 changes: 26 additions & 0 deletions src/util/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::{io, io::Read};

use zerocopy::{FromBytes, FromZeros, IntoBytes};

#[inline(always)]
pub fn read_from<T, R>(reader: &mut R) -> io::Result<T>
where
T: FromBytes + IntoBytes,
R: Read + ?Sized,
{
let mut ret = <T>::new_zeroed();
reader.read_exact(ret.as_mut_bytes())?;
Ok(ret)
}

#[inline(always)]
pub fn read_box_slice<T, R>(reader: &mut R, count: usize) -> io::Result<Box<[T]>>
where
T: FromBytes + IntoBytes,
R: Read + ?Sized,
{
let mut ret = <[T]>::new_box_zeroed_with_elems(count)
.map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?;
reader.read_exact(ret.as_mut().as_mut_bytes())?;
Ok(ret)
}
Loading

0 comments on commit 1cc38ad

Please sign in to comment.