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.
Open a ChdFile
with ChdFile::open
, 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.
```rust fn main() -> Result<()> { let mut f = BufReader::new(File::open("image.chd")?; let mut chd = ChdFile::open(&mut f, None)?; let hunkcount = chd.header().hunkcount(); let hunksize = chd.header().hunksize();
// 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](https://github.com/SnowflakePowered/chd-rs/blob/master/chd-rs/src/read.rs) provides buffered adapters that implement
Readand
Seek` at the
hunk level. A buffered adapter at the file level is also available.
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.
toml
[dependencies]
chd = { version = "0.0.10", features = ["unstable_lending_iterators"] }
Then hunks can be iterated like so.
```rust fn main() -> Result<()> { let mut f = BufReader::new(File::open("image.chd")?; let mut chd = ChdFile::open(&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
.
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.
toml
[dependencies]
chd = { version = "0.0.10", features = ["verify_block_crc"] }
chd-rs supports the following compression codecs, with wider coverage than libchdr. For implementation details,
see the chd::compression
module.
⚠️V1-4 support has not been as rigorously tested as V5 support. ⚠️
* None (CHDCOMPRESSION_NONE
)
* Zlib (CHDCOMPRESSION_ZLIB
)
* Zlib+ (CHDCOMPRESSION_ZLIB
)
CHD_CODEC_NONE
)CHD_CODEC_LZMA
)CHD_CODEC_ZLIB
)CHD_CODEC_FLAC
)CHD_CODEC_HUFF
)CHD_CODEC_CD_LZMA
)CHD_CODEC_CD_ZLIB
)CHD_CODEC_CD_FLAC
)CHD_CODEC_AVHUFF
)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. These APIs are subject
to change but should be considered mostly stable.
In particular the type signature for HuffmanDecoder
is subject to change once generic_const_exprs
is stabilized.
libchdr
API (WIP)⚠️The C API is incomplete and heavily work in progress. ⚠️
chd-rs provides a C API compatible with chd.h. ABI compatibility is detailed below but is untested when compiling as a dynamic library.
core_file*
supportThe functions chd_open_file
, and chd_core_file
will not be available unless the feature chd_core_file
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 chd_core_file
feature enabled, which will wrap
FILE*
to be usable in Rust with a lightweight wrapper in libchdcorefile
. If the default implementation
is not suitable, you may need to implement libchdcorefile
yourself. The chd_core_file
feature requires
CMake and Clang to be installed.
chd-rs makes the following ABI-compatibility guarantees compared to libchdr when compiled statically.
* chd_error
is ABI and API-compatible with chd.h
* chd_header
is ABI and API-compatible chd.h
* chd_file *
is an opaque pointer. It is not layout compatible with chd.c
* The layout of core_file *
is user-defined when the chd_core_file
feature is enabled.
* Freeing any pointer returned by chd-rs with free
is undefined behaviour. A chd_file *
pointer can be safely freed with chd_close
.