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

OffsetSizeFieldValue / Notes
04 bytesmagicVWR1 (ASCII, no null terminator)
4uint8chunksPerAxisNormally 10
5uint32 LEnonEmptyChunkCountNumber 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.

SizeFieldNotes
uint8cxChunk X index, 0 … chunksPerAxis-1
uint8cyChunk Y index, 0 … chunksPerAxis-1
uint8czChunk Z index, 0 … chunksPerAxis-1
uint64 LEoffsetByte 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:

SizeFieldNotes
4 bytesmagicVCH1
uint8bitsPerBlock0 = uniform (palette size 1, no packed data)
uint8paletteCountNumber of unique block values (1–256)
paletteCount × blockSizepalette[]Block values; blockSize = sizeof(Block) = 2 bytes
ceil(1000 × bitsPerBlock / 8)packedIndicesOmitted if bitsPerBlock == 0
0 or moreBMD1 sectionOptional 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:

SizeFieldNotes
4 bytesmagicBMD1
uint32 LEmetaLengthByte length of metadata payload
metaLength bytesmetadataReserved 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