VWR Binary Format
Complete specification for the .vwr voxel world file.
This spec is derived directly from
World Format.md in the source repository
and the implementation in src/GalaxyEggbert/Worlds/World.cpp
and Chunk.cpp.
Overall Structure
[World Header]
[Chunk Table] — sparse list of non-empty chunk entries
[Chunk Payloads] — one VCH1 block per entry in the chunk table
World Header
| Offset | Size | Field | Value / Notes |
|---|---|---|---|
| 0 | 4 bytes | magic | VWR1 (ASCII, no null terminator) |
| 4 | uint8 | chunksPerAxis | Normally 10 |
| 5 | uint32 LE | nonEmptyChunkCount | Number of entries in chunk table |
Chunk Table
Immediately follows the world header. Contains nonEmptyChunkCount entries,
each describing one non-empty chunk. Empty chunks (all-air) are not stored.
| Size | Field | Notes |
|---|---|---|
| uint8 | cx | Chunk X index, 0 … chunksPerAxis-1 |
| uint8 | cy | Chunk Y index, 0 … chunksPerAxis-1 |
| uint8 | cz | Chunk Z index, 0 … chunksPerAxis-1 |
| uint64 LE | offset | Byte offset from start of file to this chunk's VCH1 block |
Chunk table entry size: 11 bytes each.
VCH1 Chunk Payload
Each chunk payload begins with the magic bytes VCH1:
| Size | Field | Notes |
|---|---|---|
| 4 bytes | magic | VCH1 |
| uint8 | bitsPerBlock | 0 = uniform (palette size 1, no packed data) |
| uint8 | paletteCount | Number of unique block values (1–256) |
| paletteCount × blockSize | palette[] | Block values; blockSize = sizeof(Block) = 2 bytes |
| ceil(1000 × bitsPerBlock / 8) | packedIndices | Omitted if bitsPerBlock == 0 |
| 0 or more | BMD1 section | Optional metadata (see below) |
Block Type
struct Block {
uint16_t typeId; // matches BlockTypes constants / object-m.png icon index
};
typeId == 0 means Air. All other values are solid blocks.
BMD1 Metadata Section (Optional)
If present, immediately follows the packed index data:
| Size | Field | Notes |
|---|---|---|
| 4 bytes | magic | BMD1 |
| uint32 LE | metaLength | Byte length of metadata payload |
| metaLength bytes | metadata | Reserved for future per-block metadata (e.g., orientation, light level) |
Needs verification: Whether BMD1 sections are written by the current implementation
or reserved for future use.
Bit-packing Detail
Palette indices are stored little-endian, consecutive, bitsPerBlock bits each,
packed into bytes from the least-significant bit first.
Index for flat position i = lx + ly*10 + lz*100:
// conceptual read
uint64_t bitOffset = i * bitsPerBlock;
uint8_t byteIdx = bitOffset / 8;
uint8_t bitShift = bitOffset % 8;
uint8_t mask = (1u << bitsPerBlock) - 1;
uint8_t idx = (packedIndices[byteIdx] >> bitShift) & mask;
Example: 2-block Chunk
// A chunk with 500 Ground blocks and 500 Air blocks:
VCH1
bitsPerBlock = 1 (2 palette entries)
paletteCount = 2
palette[0] = 0x0000 (Air)
palette[1] = 0x000A (Ground, typeId=10)
packedIndices: 125 bytes, each bit = index into palette
Related Files
include/GalaxyEggbert/Worlds/World.hpp— saveToFile/loadFromFileinclude/GalaxyEggbert/Worlds/Chunk.hpp— write/read per chunkWorld Format.md— canonical spec in source repo- World class reference
- Chunk class reference