Skip to content

encounter/nod

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

80 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nod Build Status Latest Version Api Rustdoc Rust Version

Library for reading and writing Nintendo Optical Disc (GameCube and Wii) images.

Originally based on the C++ library nod, but with extended format support and many additional features.

Currently supported file formats:

  • ISO (GCM)
  • WIA / RVZ
  • WBFS (+ NKit 2 lossless)
  • CISO (+ NKit 2 lossless)
  • NFS (Wii U VC, read-only)
  • GCZ
  • TGC

CLI tool

This crate includes a command-line tool called nodtool.

Download the latest release from the releases page, or install it using Cargo:

cargo install --locked nodtool

info

Displays information about a disc image.

nodtool info /path/to/game.iso

extract

Extracts the contents of a disc image to a directory.

nodtool extract /path/to/game.iso [outdir]

For Wii U VC titles, use content/hif_000000.nfs:

nodtool extract /path/to/game/content/hif_000000.nfs [outdir]

convert

Converts a disc image to any supported format.

See nodtool convert --help for more information.

nodtool convert /path/to/game.iso /path/to/game.rvz

verify

Verifies a disc image against an internal Redump database.

nodtool verify /path/to/game.iso

Library example

Opening a disc image and reading a file:

use std::io::Read;

use nod::{
    common::PartitionKind,
    read::{DiscOptions, DiscReader, PartitionOptions},
};

// Open a disc image and the first data partition.
let disc =
    DiscReader::new("path/to/file.iso", &DiscOptions::default()).expect("Failed to open disc");
let mut partition = disc
    .open_partition_kind(PartitionKind::Data, &PartitionOptions::default())
    .expect("Failed to open data partition");

// Read partition metadata and the file system table.
let meta = partition.meta().expect("Failed to read partition metadata");
let fst = meta.fst().expect("File system table is invalid");

// Find a file by path and read it into a string.
if let Some((_, node)) = fst.find("/MP3/Worlds.txt") {
    let mut s = String::new();
    partition
        .open_file(node)
        .expect("Failed to open file stream")
        .read_to_string(&mut s)
        .expect("Failed to read file");
    println!("{}", s);
}

Converting a disc image to raw ISO:

use nod::read::{DiscOptions, DiscReader, PartitionEncryption};

let options = DiscOptions {
    partition_encryption: PartitionEncryption::Original,
    // Use 4 threads to preload data as the disc is read. This can speed up sequential reads,
    // especially when the disc image format uses compression.
    preloader_threads: 4,
};
// Open a disc image.
let mut disc = DiscReader::new("path/to/file.rvz", &options).expect("Failed to open disc");

// Create a new output file.
let mut out = std::fs::File::create("output.iso").expect("Failed to create output file");
// Read directly from the DiscReader and write to the output file.
// NOTE: Any copy method that accepts `Read` and `Write` can be used here,
// such as `std::io::copy`. This example utilizes `BufRead` for efficiency,
// since `DiscReader` has its own internal buffer.
nod::util::buf_copy(&mut disc, &mut out).expect("Failed to write data");

Converting a disc image to RVZ:

use std::fs::File;
use std::io::{Seek, Write};
use nod::common::{Compression, Format};
use nod::read::{DiscOptions, DiscReader, PartitionEncryption};
use nod::write::{DiscWriter, DiscWriterWeight, FormatOptions, ProcessOptions};

let open_options = DiscOptions {
    partition_encryption: PartitionEncryption::Original,
    // Use 4 threads to preload data as the disc is read. This can speed up sequential reads,
    // especially when the disc image format uses compression.
    preloader_threads: 4,
};
// Open a disc image.
let disc = DiscReader::new("path/to/file.iso", &open_options)
    .expect("Failed to open disc");
// Create a new output file.
let mut output_file = File::create("output.rvz")
    .expect("Failed to create output file");

let options = FormatOptions {
    format: Format::Rvz,
    compression: Compression::Zstandard(19),
    block_size: Format::Rvz.default_block_size(),
};
// Create a disc writer with the desired output format.
let mut writer = DiscWriter::new(disc, &options)
    .expect("Failed to create writer");

// Ideally we'd base this on the actual number of CPUs available.
// This is just an example.
let num_threads = match writer.weight() {
    DiscWriterWeight::Light => 0,
    DiscWriterWeight::Medium => 4,
    DiscWriterWeight::Heavy => 12,
};
let process_options = ProcessOptions {
    processor_threads: num_threads,
    // Enable checksum calculation for the _original_ disc data.
    // Digests will be stored in the output file for verification, if supported.
    // They will also be returned in the finalization result.
    digest_crc32: true,
    digest_md5: false, // MD5 is slow, skip it
    digest_sha1: true,
    digest_xxh64: true,
};
// Start processing the disc image.
let finalization = writer.process(
    |data, _progress, _total| {
        output_file.write_all(data.as_ref())?;
        // One could display progress here, if desired.
        Ok(())
    },
    &process_options
)
.expect("Failed to process disc image");

// Some disc writers calculate data during processing.
// If the finalization returns header data, seek to the beginning of the file and write it.
if !finalization.header.is_empty() {
    output_file.rewind()
        .expect("Failed to seek");
    output_file.write_all(finalization.header.as_ref())
        .expect("Failed to write header");
}
output_file.flush().expect("Failed to flush output file");

// Display the calculated digests.
println!("CRC32: {:08X}", finalization.crc32.unwrap());
// ...

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Rust crate for reading GameCube and Wii disc images

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Languages