Add `ar extract` command
This commit is contained in:
parent
4a84975648
commit
9a6cb70ff8
|
@ -295,7 +295,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "decomp-toolkit"
|
name = "decomp-toolkit"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ar",
|
"ar",
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
||||||
description = "Yet another GameCube/Wii decompilation toolkit."
|
description = "Yet another GameCube/Wii decompilation toolkit."
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
repository = "https://github.com/encounter/decomp-toolkit"
|
repository = "https://github.com/encounter/decomp-toolkit"
|
||||||
|
|
21
README.md
21
README.md
|
@ -20,6 +20,7 @@ project structure and build system that uses decomp-toolkit under the hood.
|
||||||
- [Analyzer features](#analyzer-features)
|
- [Analyzer features](#analyzer-features)
|
||||||
- [Commands](#commands)
|
- [Commands](#commands)
|
||||||
- [ar create](#ar-create)
|
- [ar create](#ar-create)
|
||||||
|
- [ar extract](#ar-extract)
|
||||||
- [demangle](#demangle)
|
- [demangle](#demangle)
|
||||||
- [dol info](#dol-info)
|
- [dol info](#dol-info)
|
||||||
- [dol split](#dol-split)
|
- [dol split](#dol-split)
|
||||||
|
@ -276,6 +277,26 @@ $ echo input_2.o >> rspfile
|
||||||
$ dtk ar create out.a @rspfile
|
$ dtk ar create out.a @rspfile
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ar extract
|
||||||
|
|
||||||
|
Extracts the contents of static library (.a) files.
|
||||||
|
|
||||||
|
Accepts multiple files, glob patterns (e.g. `*.a`) and response files (e.g. `@rspfile`).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `-o`, `--out <output-dir>`: Output directory. Defaults to the current directory.
|
||||||
|
- `-v`, `--verbose`: Verbose output.
|
||||||
|
- `-q`, `--quiet`: Suppresses all output except errors.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Extracts to outdir
|
||||||
|
$ dtk ar extract lib.a -o outdir
|
||||||
|
|
||||||
|
# With multiple inputs, extracts to separate directories
|
||||||
|
# Extracts to outdir/lib1, outdir/lib2
|
||||||
|
$ dtk ar extract lib1.a lib2.a -o outdir
|
||||||
|
```
|
||||||
|
|
||||||
### demangle
|
### demangle
|
||||||
|
|
||||||
Demangles CodeWarrior C++ symbols. A thin wrapper for [cwdemangle](https://github.com/encounter/cwdemangle).
|
Demangles CodeWarrior C++ symbols. A thin wrapper for [cwdemangle](https://github.com/encounter/cwdemangle).
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use argp::FromArgs;
|
use argp::FromArgs;
|
||||||
use object::{Object, ObjectSymbol, SymbolScope};
|
use object::{Object, ObjectSymbol, SymbolScope};
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ pub struct Args {
|
||||||
#[argp(subcommand)]
|
#[argp(subcommand)]
|
||||||
enum SubCommand {
|
enum SubCommand {
|
||||||
Create(CreateArgs),
|
Create(CreateArgs),
|
||||||
|
Extract(ExtractArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
@ -37,9 +38,28 @@ pub struct CreateArgs {
|
||||||
files: Vec<PathBuf>,
|
files: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
/// Extracts a static library.
|
||||||
|
#[argp(subcommand, name = "extract")]
|
||||||
|
pub struct ExtractArgs {
|
||||||
|
#[argp(positional)]
|
||||||
|
/// input files
|
||||||
|
files: Vec<PathBuf>,
|
||||||
|
#[argp(option, short = 'o')]
|
||||||
|
/// output directory
|
||||||
|
out: Option<PathBuf>,
|
||||||
|
#[argp(switch, short = 'q')]
|
||||||
|
/// quiet output
|
||||||
|
quiet: bool,
|
||||||
|
#[argp(switch, short = 'v')]
|
||||||
|
/// verbose output
|
||||||
|
verbose: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(args: Args) -> Result<()> {
|
pub fn run(args: Args) -> Result<()> {
|
||||||
match args.command {
|
match args.command {
|
||||||
SubCommand::Create(c_args) => create(c_args),
|
SubCommand::Create(c_args) => create(c_args),
|
||||||
|
SubCommand::Extract(c_args) => extract(c_args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,3 +107,51 @@ fn create(args: CreateArgs) -> Result<()> {
|
||||||
builder.into_inner()?.flush()?;
|
builder.into_inner()?.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract(args: ExtractArgs) -> Result<()> {
|
||||||
|
// Process response files (starting with '@')
|
||||||
|
let files = process_rsp(&args.files)?;
|
||||||
|
|
||||||
|
// Extract files
|
||||||
|
let mut num_files = 0;
|
||||||
|
for path in &files {
|
||||||
|
let mut out_dir = if let Some(out) = &args.out { out.clone() } else { PathBuf::new() };
|
||||||
|
// If there are multiple files, extract to separate directories
|
||||||
|
if files.len() > 1 {
|
||||||
|
out_dir
|
||||||
|
.push(path.with_extension("").file_name().ok_or_else(|| anyhow!("No file name"))?);
|
||||||
|
}
|
||||||
|
std::fs::create_dir_all(&out_dir)?;
|
||||||
|
if !args.quiet {
|
||||||
|
println!("Extracting {} to {}", path.display(), out_dir.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = map_file(path)?;
|
||||||
|
let mut archive = ar::Archive::new(file.as_slice());
|
||||||
|
while let Some(entry) = archive.next_entry() {
|
||||||
|
let mut entry =
|
||||||
|
entry.with_context(|| format!("Processing entry in {}", path.display()))?;
|
||||||
|
let file_name = std::str::from_utf8(entry.header().identifier())?;
|
||||||
|
if !args.quiet && args.verbose {
|
||||||
|
println!("\t{}", file_name);
|
||||||
|
}
|
||||||
|
let mut file_path = out_dir.clone();
|
||||||
|
for segment in file_name.split(&['/', '\\']) {
|
||||||
|
file_path.push(sanitise_file_name::sanitise(segment));
|
||||||
|
}
|
||||||
|
if let Some(parent) = file_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
let mut file = File::create(&file_path)
|
||||||
|
.with_context(|| format!("Failed to create file {}", file_path.display()))?;
|
||||||
|
std::io::copy(&mut entry, &mut file)?;
|
||||||
|
file.flush()?;
|
||||||
|
|
||||||
|
num_files += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !args.quiet {
|
||||||
|
println!("Extracted {} files", num_files);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue