mod cmd_apfs;
mod cmd_bench;
mod cmd_dmg;
mod cmd_fs;
mod cmd_hfs;
mod cmd_info;
mod cmd_payload;
mod cmd_pkg;
mod extract;
mod pipeline;
mod style;
use std::io;
use std::path::PathBuf;
use std::process;
use clap::{Args, CommandFactory, Parser, Subcommand};
use clap_complete::{Shell, generate};
#[derive(Parser)]
#[command(
name = "dpp-tool",
about = "Apple DMG pipeline explorer — navigate DMG → HFS+/APFS → PKG → PBZX → files",
version,
after_help = "\
EXAMPLES:
dpp-tool info Kernel_Debug_Kit.dmg
dpp-tool --in-memory fs info small.dmg
dpp-tool dmg ls Kernel_Debug_Kit.dmg
dpp-tool fs tree Kernel_Debug_Kit.dmg /Library
dpp-tool fs find Kernel_Debug_Kit.dmg -n \"*.kext\" -t d
dpp-tool pkg ls Kernel_Debug_Kit.dmg /KernelDebugKit.pkg
dpp-tool payload ls Kernel_Debug_Kit.dmg /path.pkg com.apple.pkg.KDK /"
)]
struct Cli {
#[arg(long, global = true, conflicts_with = "in_memory")]
temp_file: bool,
#[arg(long, global = true)]
in_memory: bool,
#[arg(long, global = true)]
no_color: bool,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Info {
dmg: PathBuf,
},
#[command(alias = "benchmark")]
Bench {
dmg: PathBuf,
},
Dmg {
#[command(subcommand)]
command: DmgCommand,
},
Fs {
#[command(subcommand)]
command: FsCommand,
},
Hfs {
#[command(subcommand)]
command: HfsCommand,
},
Apfs {
#[command(subcommand)]
command: ApfsCommand,
},
Pkg {
#[command(subcommand)]
command: PkgCommand,
},
Payload {
#[command(subcommand)]
command: PayloadCommand,
},
#[command(hide = true)]
Completions {
shell: Shell,
},
}
#[derive(Subcommand)]
enum DmgCommand {
Info {
dmg: PathBuf,
},
Ls {
dmg: PathBuf,
},
Cat {
dmg: PathBuf,
partition_id: Option<i32>,
},
}
#[derive(Args)]
struct FindArgs {
#[arg(short, long)]
name: Option<String>,
#[arg(short = 't', long = "type")]
file_type: Option<char>,
}
#[derive(Subcommand)]
enum FsCommand {
Info {
dmg: PathBuf,
},
Ls {
dmg: PathBuf,
path: String,
},
Tree {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value_t = 3)]
depth: usize,
},
Cat {
dmg: PathBuf,
path: String,
},
Stat {
dmg: PathBuf,
path: String,
},
Find {
dmg: PathBuf,
#[command(flatten)]
args: FindArgs,
},
Extract {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value = ".")]
output: PathBuf,
},
}
#[derive(Subcommand)]
enum HfsCommand {
Info {
dmg: PathBuf,
},
Ls {
dmg: PathBuf,
path: String,
},
Tree {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value_t = 3)]
depth: usize,
},
Cat {
dmg: PathBuf,
path: String,
},
Stat {
dmg: PathBuf,
path: String,
},
Find {
dmg: PathBuf,
#[command(flatten)]
args: FindArgs,
},
Extract {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value = ".")]
output: PathBuf,
},
}
#[derive(Subcommand)]
enum ApfsCommand {
Info {
dmg: PathBuf,
},
Ls {
dmg: PathBuf,
path: String,
},
Tree {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value_t = 3)]
depth: usize,
},
Cat {
dmg: PathBuf,
path: String,
},
Stat {
dmg: PathBuf,
path: String,
},
Find {
dmg: PathBuf,
#[command(flatten)]
args: FindArgs,
},
Extract {
dmg: PathBuf,
path: Option<String>,
#[arg(short, long, default_value = ".")]
output: PathBuf,
},
}
#[derive(Subcommand)]
enum PkgCommand {
Info {
dmg: PathBuf,
pkg_path: String,
},
Ls {
dmg: PathBuf,
pkg_path: String,
},
Find {
dmg: PathBuf,
pkg_path: String,
#[command(flatten)]
args: FindArgs,
},
Cat {
dmg: PathBuf,
pkg_path: String,
file: String,
},
Extract {
dmg: PathBuf,
pkg_path: String,
path: Option<String>,
#[arg(short, long, default_value = ".")]
output: PathBuf,
},
}
#[derive(Subcommand)]
enum PayloadCommand {
Info {
dmg: PathBuf,
pkg_path: String,
component: String,
},
Ls {
dmg: PathBuf,
pkg_path: String,
component: String,
path: Option<String>,
},
Tree {
dmg: PathBuf,
pkg_path: String,
component: String,
path: Option<String>,
#[arg(short, long, default_value_t = 3)]
depth: usize,
},
Find {
dmg: PathBuf,
pkg_path: String,
component: String,
#[command(flatten)]
args: FindArgs,
},
Cat {
dmg: PathBuf,
pkg_path: String,
component: String,
file: String,
},
Extract {
dmg: PathBuf,
pkg_path: String,
component: String,
path: Option<String>,
#[arg(short, long, default_value = ".")]
output: PathBuf,
},
}
fn main() {
let cli = Cli::parse();
style::init_color(cli.no_color);
let mode = if cli.in_memory {
dpp::ExtractMode::InMemory
} else {
dpp::ExtractMode::default()
};
let result = match cli.command {
Command::Info { dmg } => cmd_info::run(&dmg, mode),
Command::Bench { dmg } => cmd_bench::run(&dmg, mode),
Command::Dmg { command } => cmd_dmg::run(command, mode),
Command::Fs { command } => cmd_fs::run(command, mode),
Command::Hfs { command } => cmd_hfs::run(command, mode),
Command::Apfs { command } => cmd_apfs::run(command, mode),
Command::Pkg { command } => cmd_pkg::run(command, mode),
Command::Payload { command } => cmd_payload::run(command, mode),
Command::Completions { shell } => {
let mut cmd = Cli::command();
generate(shell, &mut cmd, "dpp-tool", &mut io::stdout());
Ok(())
}
};
if let Err(e) = result {
eprintln!("{}error:{} {e}", style::red(), style::reset());
process::exit(1);
}
}