2 releases

0.1.1 Oct 27, 2024
0.1.0 Oct 27, 2024

#127 in Compression

Download history 179/week @ 2024-10-21 87/week @ 2024-10-28

266 downloads per month
Used in gcn_disk

MPL-2.0 OR LGPL-2…

70KB
1.5K SLoC

rvz

rvz is a library for reading Wii RVZ compressed disks.

The RVZ file format was introduced by the Dolphin emulator as an improvement over the WIA file format, and is documented here.

RVZ files compress GameCube and Wii disk images, in some cases drastically reducing the overall file size. It achieves this by storing the decrypted contents of the disks and applying compression where it is beneficial. Additionally, a lot of the padding in these disk images can be modeled as bytes from a Lagged Fibonacci Generator, drastically reducing the storage requirement of these random-looking sequences of bytes.

This storage format is lossless, as it stores all of the information required to reconstruct the original disk. However, not all data is cheaply reconstructed. The format lends itself well to the use case of games, as partition data is already decrypted. To reconstruct the original disk, all hashes in Wii disks must be re-computed, which requires reading large portions of the archive.

Usage

use std::io;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;

let file = File::open("path/to/rvz.rvz").expect("Failed to open RVZ");
let mut rvz = rvz::Rvz::new(file).expect("Failed to parse RVZ metadata");

// This contains (ideally) the metadata of the RVZ image. `Rvz::new` only does
// minimal error checking, additional validity checks need to be done with the
// metadata to confirm that the file is valid (e.g. check that size matches,
// check RVZ magic, etc.)
let metadata = &rvz.metadata;

let partition = &metadata.partitions[0];
let first_sector = partition.partition_data[0].first_sector;
let partition_offset = u64::from(first_sector) * 0x8000;
let partition_size =
    u64::from(partition.partition_data[0].sector_count +
    partition.partition_data[1].sector_count) * 0x800;

// This takes a long time, but is required to get proper hashes when reading
// from partitions, as the RVZ archive doesn't actually store hashes unless
// they differ from what can be computed from data.
rvz.compute_hashes().expect("Error while computing partition hashes");

rvz.seek(SeekFrom::Start(partition_offset)).expect("Unable to seek");

// This dumps the contents of the first partition to a file, decrypted
let mut file = File::create("path/to/partition/dump.raw")
    .expect("Failed to open output file");
io::copy(&mut rvz.by_ref().take(partition_size), &mut file)
    .expect("Failed to copy partition to file");

TODO

  • Support GCN disks
  • Support more than just ZSTD compression
  • Better unit testing
  • Allow encrypting partitions, to be able to dump a full ISO
  • Apply exception lists (as far as I can tell, these are only used possibly for hacks or homebrew that have bad/incorrect hashes on disk?)

License

This module is released under the following licenses:

  • GPL v2 or later
  • LGPL v2.1 or later
  • MPL 2.0

Refer to the LICENSE file for more information.

Dependencies

~5.5MB
~99K SLoC