cbvault 0.1.4

Read ChessBase databases as moves2 streams: Database, GameIter, PGN writer, and .cbv/.cbz archives that unarchive in full
Documentation

cbvault

Read ChessBase databases as moves2 streams, and unarchive .cbv/.cbz containers in full. MIT licensed, read-only, and built for throughput.

This is the consumer-facing crate. It ties together cbvault-format (bytes) and cbvault-chess (gigachess) into one API: open a database, stream its games, read annotations, write PGN, or unarchive a container.

Open and read

use cbvault::bridge::Database;

let db = Database::open("Mega Database 2025/Mega Database 2025")?;
for header in db.headers().iter().take(5) {
    println!("{} {}", header.id, header.white);
}

Listing never opens the moves file, so inspecting a database header does not pay for its gigabytes.

Convert through a sink

for_each_game walks the database and hands each game to a sink that declares what it needs. Asking for less makes the walk cheaper: no keys means the fast move generator, keys means the hash-maintaining one, annotations off means the annotations file is never opened.

use cbvault::bridge::{for_each_game, Database, GameRef, GameSink};

struct Counted(u64);
impl GameSink for Counted {
    fn game(&mut self, _: GameRef<'_>) {
        self.0 += 1;
    }
}

let db = Database::open("Mega Database 2025/Mega Database 2025")?;
let mut count = Counted(0);
for_each_game(&db, &mut count)?;
println!("{} games", count.0);

convert_parallel is the Rayon-parallel form. Both are byte-identical, and that is a test rather than a claim: the same sink sees the same games in the same order at every thread count.

Export PGN

use cbvault::pgn::PgnWriter;

let mut writer = PgnWriter::new();
writer.write_game(&mut out, &header, &entities, &game, annotations)?;

cbvault::pgn::export_parallel is the Rayon-parallel form and produces byte-identical output to the sequential one, which is a test rather than a claim.

Unarchive a .cbv

use cbvault_format::archive::Archive;

let archive = Archive::open("Mega.cbv")?;
archive.extract_parallel("./out", 4)?;

All four block modes decode: 3,871 of 3,871 members and 100 % of the reference archive's 3.61 GB, every member byte-identical to the reference extractor. .cbz opens with a password.

Design notes

  • Read-only, permanently. Nothing opens a database for writing; no extraction ever writes bytes the reader did not decode.
  • Zero allocation in hot paths. Games stream through caller-owned buffers.
  • One chess core. gigachess, and only gigachess.
  • Malformed input never panics. Every failure is a typed error.

Not supported

  • 2CBH. cbvault_format::twocbh reads the container's framing for inspection, but the .2cbg move codec is not decoded. Database::open refuses a 2CBH set with a typed error rather than returning a half-read database.
  • Writing. This project never writes ChessBase data.
  • Derived accelerators (.cko, .cpo) are tolerated and ignored.

Licence

MIT. The .cbh readers are ported from cbformat in oschess-cb-bridge (MIT, "Copyright (c) 2026 the oschess-cb-bridge contributors") with attribution; the .cbv/.cbz codec was written from the published format facts. No ChessBase code is used and no ChessBase data is redistributed. ChessBase is a trademark of its owner, used only to name the formats read here.