A library and CLI for the Bedrock Archive (.brarchive) format, the bundling format
Mojang uses to pack the files inside Minecraft Bedrock Edition resource and behavior packs.
Entries are usually JSON, but newer packs also embed compiled binary data, and brarchive-rs
handles both.
It ships in three flavours:
| Package | Install | |
|---|---|---|
| CLI | brarchive-cli on crates.io |
cargo install brarchive-cli |
| Rust library | brarchive on crates.io |
cargo add brarchive |
| JavaScript / TypeScript | @bedrock-crustaceans/brarchive on JSR |
deno add jsr:@bedrock-crustaceans/brarchive |
The binary format itself is documented in FORMAT.md.
Install from crates.io:
cargo install brarchive-cliNote: the package is called
brarchive-cli(the plainbrarchivename is the library), but the installed binary is justbrarchive.
Prebuilt binaries for Linux, macOS, and Windows are also attached to every
GitHub release, so you can skip the
build entirely: download the one for your platform and put it somewhere on your PATH.
Verify it works:
brarchive --helpcargo add brarchiveThe same code is compiled to WebAssembly and published to JSR:
deno add jsr:@bedrock-crustaceans/brarchiveFor Node, Bun, or other npm-based projects, use the JSR installer instead:
npx jsr add @bedrock-crustaceans/brarchiveSee crates/wasm/README.md for the full API.
| Subcommand | Description |
|---|---|
encode |
Bundle a folder into a .brarchive |
decode |
Extract a .brarchive into a folder |
list |
Print the entry names in an archive without extracting anything |
Each subcommand takes either a single .brarchive file or, with --recursive, a whole
pack whose archives live under __brarchive/.
| Option | Applies to | Description |
|---|---|---|
--recursive |
all | Operate on a whole pack: every archive under __brarchive/ |
--skip-root |
encode |
Leave files in the pack's top-level directory loose, like a vanilla pack |
--dedup |
encode |
Store identical file contents only once |
--pretty |
decode |
Write JSON entries with a 2-space indent instead of minified |
--overwrite |
decode |
Replace files that already exist instead of stopping |
--delete-source |
encode, decode |
Remove the originals once the archive is written / extracted |
Bundle a folder into one archive:
brarchive encode path/to/dir output.brarchiveWalk a whole pack and mirror its directory tree into __brarchive/, writing one archive
per folder:
brarchive encode path/to/pack --recursiveFiles in the top-level directory itself (manifest.json, pack_icon.png, and so on) are
bundled into __brarchive/__root__.brarchive, and decode --recursive puts them back at
the root. Mojang leaves those files loose, so pass --skip-root if you want the same
layout as a vanilla pack:
brarchive encode path/to/pack --recursive --skip-rootAdd --dedup to store identical file contents only once, and --delete-source to remove
the originals once the archive is written:
brarchive encode path/to/dir output.brarchive --dedup --delete-sourceExtract a single archive into a folder:
brarchive decode output.brarchive path/to/out/Extract every archive under a pack's __brarchive/ folder in one go:
brarchive decode path/to/pack --recursiveEach archive is unpacked into the directory its path under __brarchive/ mirrors, so
__brarchive/textures/ui.brarchive lands in textures/ui/. That directory is merged
into, not emptied, so loose files that were never archived (textures, sounds, and so on)
are left alone.
If an entry would replace a file that already exists, the decode stops before writing
anything. Pass --overwrite to replace those files instead, for example when
re-extracting a pack:
brarchive decode path/to/pack --recursive --overwriteMojang ships the JSON inside these archives minified, and by default the CLI writes each
entry back exactly as stored. Pass --pretty to get readable JSON with a 2-space indent.
Entries that are not JSON, such as the compiled binary MCB files Bedrock now embeds, are
always written untouched so nothing gets corrupted:
brarchive decode output.brarchive path/to/out/ --pretty--delete-source works here too and removes the archive after a successful decode.
Print the entry names in an archive without extracting anything:
brarchive list output.brarchiveOr list the contents of every archive in a pack:
brarchive list path/to/pack --recursiveuse std::collections::BTreeMap;
// Walk the entries without copying: each one is a name and the byte range of its
// contents in the archive, checked to lie inside it
for entry in brarchive::entries(&bytes)? {
let entry = entry?;
let contents: &[u8] = &bytes[entry.range];
}
// Just the names
let names = brarchive::entries(&bytes)?
.map(|entry| entry.map(|entry| entry.name))
.collect::<Result<Vec<&str>, _>>()?;
// Deserialize into anything that implements FromIterator<(String, Vec<u8>)>.
// Content comes back as raw bytes, since an archive can hold binary entries.
let map: BTreeMap<String, Vec<u8>> = brarchive::deserialize(&bytes)?;
let vec: Vec<(String, Vec<u8>)> = brarchive::deserialize(&bytes)?;
// Entries that hold text can be turned back into a String when you need one
let text = String::from_utf8(map["entity.json"].clone())?;
// Serialize any iterable of key/value pairs. Keys are entry names; values are
// bytes, so &str, String, Vec<u8>, and &[u8] all work.
brarchive::serialize([("entity.json", r#"{"id":"zombie"}"#)])?;
brarchive::serialize(&map)?;
// Serialize with options, for example deduplicating identical content
brarchive::serialize_with(&map, brarchive::SerializeOptions { dedup: true })?;import { deserialize, list, serialize } from "@bedrock-crustaceans/brarchive";
const bytes = serialize({ "entity.json": '{"id":"zombie"}' });
list(bytes); // ["entity.json"]
deserialize(bytes).get("entity.json"); // Uint8ArraySee crates/wasm/README.md for the full API.
See FORMAT.md for the binary format specification.
See LICENSE.