mirror of https://github.com/encounter/nod-rs.git
Fixes & bump to version 1.0.0
This commit is contained in:
parent
7e6d880792
commit
1895b7df3f
|
@ -19,7 +19,7 @@ jobs:
|
||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
# We use nightly options in rustfmt.toml
|
# We use nightly options in rustfmt.toml
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
|
@ -58,7 +58,7 @@ jobs:
|
||||||
# Prevent new advisories from failing CI
|
# Prevent new advisories from failing CI
|
||||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Cargo test
|
- name: Cargo test
|
||||||
|
@ -127,7 +127,7 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: matrix.packages != ''
|
if: matrix.packages != ''
|
||||||
run: |
|
run: |
|
||||||
|
@ -143,7 +143,7 @@ jobs:
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
run: cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
|
@ -160,7 +160,7 @@ jobs:
|
||||||
needs: [ build ]
|
needs: [ build ]
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Rename artifacts
|
- name: Rename artifacts
|
||||||
|
@ -172,6 +172,6 @@ jobs:
|
||||||
done
|
done
|
||||||
ls -R ../out
|
ls -R ../out
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||||
with:
|
with:
|
||||||
files: out/*
|
files: out/*
|
||||||
|
|
|
@ -411,7 +411,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nod"
|
name = "nod"
|
||||||
version = "0.2.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
"aes",
|
"aes",
|
||||||
|
@ -434,7 +434,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nodtool"
|
name = "nodtool"
|
||||||
version = "0.2.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argp",
|
"argp",
|
||||||
"base16ct",
|
"base16ct",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "nod"
|
name = "nod"
|
||||||
version = "0.2.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.73.0"
|
rust-version = "1.73.0"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
@ -23,20 +23,20 @@ compress-zlib = ["adler", "miniz_oxide"]
|
||||||
compress-zstd = ["zstd"]
|
compress-zstd = ["zstd"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adler = { version = "1.0.2", optional = true }
|
adler = { version = "1.0", optional = true }
|
||||||
aes = "0.8.4"
|
aes = "0.8"
|
||||||
base16ct = "0.2.0"
|
base16ct = "0.2"
|
||||||
bzip2 = { version = "0.4.4", features = ["static"], optional = true }
|
bzip2 = { version = "0.4", features = ["static"], optional = true }
|
||||||
cbc = "0.1.2"
|
cbc = "0.1"
|
||||||
digest = "0.10.7"
|
digest = "0.10"
|
||||||
dyn-clone = "1.0.16"
|
dyn-clone = "1.0"
|
||||||
encoding_rs = "0.8.33"
|
encoding_rs = "0.8"
|
||||||
itertools = "0.12.1"
|
itertools = "0.12"
|
||||||
liblzma = { version = "0.2.3", features = ["static"], optional = true }
|
liblzma = { version = "0.2", features = ["static"], optional = true }
|
||||||
log = "0.4.20"
|
log = "0.4"
|
||||||
miniz_oxide = { version = "0.7.2", optional = true }
|
miniz_oxide = { version = "0.7", optional = true }
|
||||||
rayon = "1.8.1"
|
rayon = "1.8"
|
||||||
sha1 = "0.10.6"
|
sha1 = "0.10"
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0"
|
||||||
zerocopy = { version = "0.7.32", features = ["alloc", "derive"] }
|
zerocopy = { version = "0.7", features = ["alloc", "derive"] }
|
||||||
zstd = { version = "0.13.0", optional = true }
|
zstd = { version = "0.13", optional = true }
|
||||||
|
|
|
@ -118,7 +118,7 @@ impl PartitionBase for PartitionGC {
|
||||||
|
|
||||||
fn open_file(&mut self, node: &Node) -> io::Result<SharedWindowedReadStream> {
|
fn open_file(&mut self, node: &Node) -> io::Result<SharedWindowedReadStream> {
|
||||||
assert_eq!(node.kind(), NodeKind::File);
|
assert_eq!(node.kind(), NodeKind::File);
|
||||||
self.new_window(node.offset(false), node.length(false))
|
self.new_window(node.offset(false), node.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ideal_buffer_size(&self) -> usize { SECTOR_SIZE }
|
fn ideal_buffer_size(&self) -> usize { SECTOR_SIZE }
|
||||||
|
|
|
@ -437,7 +437,7 @@ impl PartitionBase for PartitionWii {
|
||||||
|
|
||||||
fn open_file(&mut self, node: &Node) -> io::Result<SharedWindowedReadStream> {
|
fn open_file(&mut self, node: &Node) -> io::Result<SharedWindowedReadStream> {
|
||||||
assert_eq!(node.kind(), NodeKind::File);
|
assert_eq!(node.kind(), NodeKind::File);
|
||||||
self.new_window(node.offset(true), node.length(true))
|
self.new_window(node.offset(true), node.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ideal_buffer_size(&self) -> usize { SECTOR_DATA_SIZE }
|
fn ideal_buffer_size(&self) -> usize { SECTOR_DATA_SIZE }
|
||||||
|
|
|
@ -63,18 +63,12 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For files, this is the byte size of the file. (Wii: >> 2)
|
/// For files, this is the byte size of the file.
|
||||||
///
|
///
|
||||||
/// For directories, this is the child end index in the FST.
|
/// For directories, this is the child end index in the FST.
|
||||||
///
|
///
|
||||||
/// Number of child files and directories recursively is `length - offset`.
|
/// Number of child files and directories recursively is `length - offset`.
|
||||||
pub fn length(&self, is_wii: bool) -> u64 {
|
pub fn length(&self) -> u64 { self.length.get() as u64 }
|
||||||
if is_wii && self.kind == 0 {
|
|
||||||
self.length.get() as u64 * 4
|
|
||||||
} else {
|
|
||||||
self.length.get() as u64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A view into the file system tree (FST).
|
/// A view into the file system tree (FST).
|
||||||
|
@ -90,7 +84,7 @@ impl<'a> Fst<'a> {
|
||||||
return Err("FST root node not found");
|
return Err("FST root node not found");
|
||||||
};
|
};
|
||||||
// String table starts after the last node
|
// String table starts after the last node
|
||||||
let string_base = root_node.length(false) * size_of::<Node>() as u64;
|
let string_base = root_node.length() * size_of::<Node>() as u64;
|
||||||
if string_base >= buf.len() as u64 {
|
if string_base >= buf.len() as u64 {
|
||||||
return Err("FST string table out of bounds");
|
return Err("FST string table out of bounds");
|
||||||
}
|
}
|
||||||
|
@ -137,10 +131,10 @@ impl<'a> Fst<'a> {
|
||||||
}
|
}
|
||||||
// Descend into directory
|
// Descend into directory
|
||||||
idx += 1;
|
idx += 1;
|
||||||
stop_at = Some(node.length(false) as usize + idx);
|
stop_at = Some(node.length() as usize + idx);
|
||||||
} else if node.is_dir() {
|
} else if node.is_dir() {
|
||||||
// Skip directory
|
// Skip directory
|
||||||
idx = node.length(false) as usize;
|
idx = node.length() as usize;
|
||||||
} else {
|
} else {
|
||||||
// Skip file
|
// Skip file
|
||||||
idx += 1;
|
idx += 1;
|
||||||
|
|
|
@ -102,7 +102,7 @@ impl BlockIO for DiscIOCISO {
|
||||||
let phys_block = self.block_map[block as usize];
|
let phys_block = self.block_map[block as usize];
|
||||||
if phys_block == u16::MAX {
|
if phys_block == u16::MAX {
|
||||||
// Check if block is junk data
|
// Check if block is junk data
|
||||||
if self.nkit_header.as_ref().is_some_and(|h| h.is_junk_block(block).unwrap_or(false)) {
|
if self.nkit_header.as_ref().and_then(|h| h.is_junk_block(block)).unwrap_or(false) {
|
||||||
return Ok(Block::Junk);
|
return Ok(Block::Junk);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub struct DiscIOWBFS {
|
||||||
/// WBFS header
|
/// WBFS header
|
||||||
header: WBFSHeader,
|
header: WBFSHeader,
|
||||||
/// Map of Wii LBAs to WBFS LBAs
|
/// Map of Wii LBAs to WBFS LBAs
|
||||||
block_table: Box<[U16]>,
|
block_map: Box<[U16]>,
|
||||||
/// Optional NKit header
|
/// Optional NKit header
|
||||||
nkit_header: Option<NKitHeader>,
|
nkit_header: Option<NKitHeader>,
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,11 @@ impl DiscIOWBFS {
|
||||||
return Err(Error::DiscFormat("Only single WBFS discs are supported".to_string()));
|
return Err(Error::DiscFormat("Only single WBFS discs are supported".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read WBFS LBA table
|
// Read WBFS LBA map
|
||||||
inner
|
inner
|
||||||
.seek(SeekFrom::Start(header.sector_size() as u64 + DISC_HEADER_SIZE as u64))
|
.seek(SeekFrom::Start(header.sector_size() as u64 + DISC_HEADER_SIZE as u64))
|
||||||
.context("Seeking to WBFS LBA table")?; // Skip header
|
.context("Seeking to WBFS LBA table")?; // Skip header
|
||||||
let block_table: Box<[U16]> = read_box_slice(&mut inner, header.max_blocks() as usize)
|
let block_map: Box<[U16]> = read_box_slice(&mut inner, header.max_blocks() as usize)
|
||||||
.context("Reading WBFS LBA table")?;
|
.context("Reading WBFS LBA table")?;
|
||||||
|
|
||||||
// Read NKit header if present (always at 0x10000)
|
// Read NKit header if present (always at 0x10000)
|
||||||
|
@ -104,7 +104,7 @@ impl DiscIOWBFS {
|
||||||
|
|
||||||
// Reset reader
|
// Reset reader
|
||||||
inner.reset();
|
inner.reset();
|
||||||
Ok(Box::new(Self { inner, header, block_table, nkit_header }))
|
Ok(Box::new(Self { inner, header, block_map, nkit_header }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +120,20 @@ impl BlockIO for DiscIOWBFS {
|
||||||
return Ok(Block::Zero);
|
return Ok(Block::Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if block is junk data
|
// Find the block in the map
|
||||||
if self.nkit_header.as_ref().and_then(|h| h.is_junk_block(block)).unwrap_or(false) {
|
let phys_block = self.block_map[block as usize].get();
|
||||||
return Ok(Block::Junk);
|
if phys_block == 0 {
|
||||||
|
// Check if block is junk data
|
||||||
|
if self.nkit_header.as_ref().and_then(|h| h.is_junk_block(block)).unwrap_or(false) {
|
||||||
|
return Ok(Block::Junk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, read zeroes
|
||||||
|
return Ok(Block::Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read block
|
// Read block
|
||||||
let block_start = block_size as u64 * self.block_table[block as usize].get() as u64;
|
let block_start = block_size as u64 * phys_block as u64;
|
||||||
if block_start == 0 {
|
|
||||||
return Ok(Block::Zero);
|
|
||||||
}
|
|
||||||
self.inner.seek(SeekFrom::Start(block_start))?;
|
self.inner.seek(SeekFrom::Start(block_start))?;
|
||||||
self.inner.read_exact(out)?;
|
self.inner.read_exact(out)?;
|
||||||
Ok(Block::Raw)
|
Ok(Block::Raw)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "nodtool"
|
name = "nodtool"
|
||||||
version = "0.2.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.73.0"
|
rust-version = "1.73.0"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|
|
@ -31,7 +31,7 @@ use size::{Base, Size};
|
||||||
use supports_color::Stream;
|
use supports_color::Stream;
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use zerocopy::FromZeroes;
|
use zerocopy::{AsBytes, FromZeroes};
|
||||||
|
|
||||||
#[derive(FromArgs, Debug)]
|
#[derive(FromArgs, Debug)]
|
||||||
/// Tool for reading GameCube and Wii disc images.
|
/// Tool for reading GameCube and Wii disc images.
|
||||||
|
@ -543,44 +543,46 @@ fn extract(args: ExtractArgs) -> Result<()> {
|
||||||
rebuild_encryption: false,
|
rebuild_encryption: false,
|
||||||
validate_hashes: args.validate,
|
validate_hashes: args.validate,
|
||||||
})?;
|
})?;
|
||||||
let is_wii = disc.header().is_wii();
|
let header = disc.header();
|
||||||
|
let is_wii = header.is_wii();
|
||||||
if let Some(partition) = args.partition {
|
if let Some(partition) = args.partition {
|
||||||
if partition.eq_ignore_ascii_case("all") {
|
if partition.eq_ignore_ascii_case("all") {
|
||||||
for info in disc.partitions() {
|
for info in disc.partitions() {
|
||||||
let mut out_dir = output_dir.clone();
|
let mut out_dir = output_dir.clone();
|
||||||
out_dir.push(info.kind.dir_name().as_ref());
|
out_dir.push(info.kind.dir_name().as_ref());
|
||||||
let mut partition = disc.open_partition(info.index)?;
|
let mut partition = disc.open_partition(info.index)?;
|
||||||
extract_partition(partition.as_mut(), &out_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &out_dir, is_wii, args.quiet)?;
|
||||||
}
|
}
|
||||||
} else if partition.eq_ignore_ascii_case("data") {
|
} else if partition.eq_ignore_ascii_case("data") {
|
||||||
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
|
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
|
||||||
extract_partition(partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
||||||
} else if partition.eq_ignore_ascii_case("update") {
|
} else if partition.eq_ignore_ascii_case("update") {
|
||||||
let mut partition = disc.open_partition_kind(PartitionKind::Update)?;
|
let mut partition = disc.open_partition_kind(PartitionKind::Update)?;
|
||||||
extract_partition(partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
||||||
} else if partition.eq_ignore_ascii_case("channel") {
|
} else if partition.eq_ignore_ascii_case("channel") {
|
||||||
let mut partition = disc.open_partition_kind(PartitionKind::Channel)?;
|
let mut partition = disc.open_partition_kind(PartitionKind::Channel)?;
|
||||||
extract_partition(partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
||||||
} else {
|
} else {
|
||||||
let idx = partition.parse::<usize>().map_err(|_| "Invalid partition index")?;
|
let idx = partition.parse::<usize>().map_err(|_| "Invalid partition index")?;
|
||||||
let mut partition = disc.open_partition(idx)?;
|
let mut partition = disc.open_partition(idx)?;
|
||||||
extract_partition(partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
|
let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
|
||||||
extract_partition(partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
extract_partition(header, partition.as_mut(), &output_dir, is_wii, args.quiet)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_partition(
|
fn extract_partition(
|
||||||
|
header: &DiscHeader,
|
||||||
partition: &mut dyn PartitionBase,
|
partition: &mut dyn PartitionBase,
|
||||||
out_dir: &Path,
|
out_dir: &Path,
|
||||||
is_wii: bool,
|
is_wii: bool,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let meta = partition.meta()?;
|
let meta = partition.meta()?;
|
||||||
extract_sys_files(meta.as_ref(), out_dir, quiet)?;
|
extract_sys_files(header, meta.as_ref(), out_dir, quiet)?;
|
||||||
|
|
||||||
// Extract FST
|
// Extract FST
|
||||||
let files_dir = out_dir.join("files");
|
let files_dir = out_dir.join("files");
|
||||||
|
@ -601,7 +603,7 @@ fn extract_partition(
|
||||||
path_segments.truncate(new_size);
|
path_segments.truncate(new_size);
|
||||||
|
|
||||||
// Add the new path segment
|
// Add the new path segment
|
||||||
let end = if node.is_dir() { node.length(false) as usize } else { idx + 1 };
|
let end = if node.is_dir() { node.length() as usize } else { idx + 1 };
|
||||||
path_segments.push((name?, end));
|
path_segments.push((name?, end));
|
||||||
|
|
||||||
let path = path_segments.iter().map(|(name, _)| name.as_ref()).join("/");
|
let path = path_segments.iter().map(|(name, _)| name.as_ref()).join("/");
|
||||||
|
@ -615,7 +617,12 @@ fn extract_partition(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_sys_files(data: &PartitionMeta, out_dir: &Path, quiet: bool) -> Result<()> {
|
fn extract_sys_files(
|
||||||
|
header: &DiscHeader,
|
||||||
|
data: &PartitionMeta,
|
||||||
|
out_dir: &Path,
|
||||||
|
quiet: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let sys_dir = out_dir.join("sys");
|
let sys_dir = out_dir.join("sys");
|
||||||
fs::create_dir_all(&sys_dir)
|
fs::create_dir_all(&sys_dir)
|
||||||
.with_context(|| format!("Creating directory {}", display(&sys_dir)))?;
|
.with_context(|| format!("Creating directory {}", display(&sys_dir)))?;
|
||||||
|
@ -626,6 +633,12 @@ fn extract_sys_files(data: &PartitionMeta, out_dir: &Path, quiet: bool) -> Resul
|
||||||
extract_file(data.raw_dol.as_ref(), &sys_dir.join("main.dol"), quiet)?;
|
extract_file(data.raw_dol.as_ref(), &sys_dir.join("main.dol"), quiet)?;
|
||||||
|
|
||||||
// Wii files
|
// Wii files
|
||||||
|
if header.is_wii() {
|
||||||
|
let disc_dir = out_dir.join("disc");
|
||||||
|
fs::create_dir_all(&disc_dir)
|
||||||
|
.with_context(|| format!("Creating directory {}", display(&disc_dir)))?;
|
||||||
|
extract_file(&header.as_bytes()[..0x100], &disc_dir.join("header.bin"), quiet)?;
|
||||||
|
}
|
||||||
if let Some(ticket) = data.raw_ticket.as_deref() {
|
if let Some(ticket) = data.raw_ticket.as_deref() {
|
||||||
extract_file(ticket, &out_dir.join("ticket.bin"), quiet)?;
|
extract_file(ticket, &out_dir.join("ticket.bin"), quiet)?;
|
||||||
}
|
}
|
||||||
|
@ -666,7 +679,7 @@ fn extract_node(
|
||||||
println!(
|
println!(
|
||||||
"Extracting {} (size: {})",
|
"Extracting {} (size: {})",
|
||||||
display(&file_path),
|
display(&file_path),
|
||||||
Size::from_bytes(node.length(is_wii)).format().with_base(Base::Base10)
|
Size::from_bytes(node.length()).format().with_base(Base::Base10)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let file = File::create(&file_path)
|
let file = File::create(&file_path)
|
||||||
|
@ -677,7 +690,7 @@ fn extract_node(
|
||||||
"Opening file {} on disc for reading (offset {}, size {})",
|
"Opening file {} on disc for reading (offset {}, size {})",
|
||||||
name,
|
name,
|
||||||
node.offset(is_wii),
|
node.offset(is_wii),
|
||||||
node.length(is_wii)
|
node.length()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
io::copy(&mut r, &mut w).with_context(|| format!("Extracting file {}", display(&file_path)))?;
|
io::copy(&mut r, &mut w).with_context(|| format!("Extracting file {}", display(&file_path)))?;
|
||||||
|
|
Loading…
Reference in New Issue