chd 0.0.7

Rust implementation of the CHD File Format
Documentation

chd-rs

Latest Version Docs License Minimum Supported Rust Version 1.59

Reimplementation of the CHD file format in pure Safe Rust, drop-in compatible with libchdr.

chd-rs aims to be a memory-safe, well documented, and clean from-scratch implementation of CHD, verifiable against chd.cpp while being easier to read and use as documentation to implement the format natively in other languages. It is standalone and can be built with just a Rust compiler, without the need for a full C/C++ toolchain.

Performance is competitive but a little slower than libchdr in benchmarks from using more immature (but fully correct) pure Rust implementations of compression codecs. Deflate (zlib) compression is backed by flate2, LZMA is backed by lzma-rs (modified slightly to allow headerless decoding of LZMA chunks), and FLAC decompression is backed by claxon. While performance is not ignored, the focus is on readability and correctness.

Usage

Open a ChdFile with ChdFile::open_stream, then iterate hunks from 0 to chd.header().hunk_count() to read hunks.

The size of the destination buffer must be exactly chd.header().hunk_size() to decompress with hunk.read_hunk_in, which takes the output slice and a buffer to hold compressed data.

fn main() -> Result<()> {
    let mut f = BufReader::new(File::open("image.chd")?;
    let mut chd = ChdFile::open_stream(&mut f, None)?;
    let hunk_count = chd.header().hunk_count();
    let hunk_size = chd.header().hunk_size();
    
    // buffer to store decompressed hunks
    let mut out_buf = vec![0u8; hunk_size as usize];
    
    // buffer for temporary compressed
    let mut temp_buf = Vec::new();
    for hunk_num in 0..hunk_count {
        let mut hunk = chd.hunk(hunk_num)?;
        hunk.read_hunk_in(&mut temp_buf, &mut out_buf)?;
    }
}

For more ergonomic but slower usage, chd::read provides buffered adapters that implement Read and Seek at the hunk level. A buffered adapter at the file level is also available.

Lending Iterators

With unstable_lending_iterators, hunks and metadata can be slightly more ergonomically iterated over albeit with a while let loop. This API is unstable until Generalized Associated Types and the LendingIterator trait is stabilized.

[dependencies]
chd = { version = "0.0.7", features = ["unstable_lending_iterators"] }

Then hunks can be iterated like so.

fn main() -> Result<()> {
    let mut f = BufReader::new(File::open("image.chd")?;
    let mut chd = ChdFile::open_stream(&mut f, None)?;
    
    // buffer to store decompressed hunks
    let mut out_buf = chd.get_hunksized_buffer();
    
    // buffer for temporary compressed
    let mut temp_buf = Vec::new();
    let mut hunk_iter = chd.hunks();
    while let Some(mut hunk) = hunk_iter.next() {
        hunk.read_hunk_in(&mut temp_buf, &mut out_buf)?;
    }
}

A similar API exists for metadata in ChdFile::metadata.

Verifying Block Checksums

By default, chd-rs does not verify the checksums of decompressed hunks for performance. The feature verify_block_crc should be enabled to verify hunk checksums.

[dependencies]
chd = { version = "0.0.7", features = ["verify_block_crc"] }

Supported Codecs

chd-rs supports the following compression codecs, with wider coverage than libchdr. For implementation details, see the chd::compression module.

V1-4 Codecs

⚠️V1-4 support has not been as rigorously tested as V5 support. ⚠️

  • None (CHDCOMPRESSION_NONE)
  • Zlib (CHDCOMPRESSION_ZLIB)
  • Zlib+ (CHDCOMPRESSION_ZLIB)

V5 Codecs

  • None (CHD_CODEC_NONE)
  • LZMA (CHD_CODEC_LZMA)
  • Deflate (CHD_CODEC_ZLIB)
  • FLAC (CHD_CODEC_FLAC)
  • Huffman (CHD_CODEC_HUFF)
  • CD LZMA (CHD_CODEC_CD_LZMA)
  • CD Deflate (CHD_CODEC_CD_ZLIB)
  • CD FLAC (CHD_CODEC_CD_FLAC)
  • AV Huffman (CHD_CODEC_AVHUFF)

Codecs and Huffman API

By default, the codecs and static Huffman implementations are not exposed as part of the public API, but can be enabled with the codec_api and huffman_api features respectively.

libchdr API (WIP)

⚠️The C API is incomplete and heavily work in progress. ⚠️

chd-rs provides a C API compatible with chd.h. It makes no guarantees of ABI compatibility, and if your project links dynamically with libchdr, the output library will not work. However, chd-rs provides a CMakeLists.txt that will link your project statically against chd-rs, and provides mostly the exact same API as libchdr.

core_file* support

The functions chd_open_file, and chd_core_file will not be available unless the feature unsafe_c_file_streams is enabled.

This is because core_file* is not an opaque pointer and is a C FILE* stream. This allows the underlying file pointer to be changed unsafely beneath the memory safety guarantees of chd-rs. We strongly encourage using chd_open instead of chd_open_file.

If you need core_file* support, chd-capi should have the unsafe_c_file_streams feature enabled. This will attempt to use C FILE* streams as the underlying stream for a chd_file*. This is highly unsafe and not supported on all platforms.

ABI compatibility

chd-rs makes no guarantees of ABI-compatibility unless otherwise documented, and will only provide source-level compatibility with chd.h. Other APIs exposed by libchdr are also not provided.