Below is a description of the Cosmic Reach region format, as a reference for external save editors.
Types
All numbers are encoded in big endian (most significant byte first) unless otherwise explicitly stated. These are different types of data that the save system uses. Most integer types are signed. Single bytes, nibbles & half nibbles are unsigned. The data types used by the game may change in future versions of the save format, and some may be unused despite being defined by the game.
Arrays are represented as the type's name and size, e.g. s32[4096] represents 4096 s32 values in order.
Data types
Type |
Canonical Name |
Description
|
s32 |
int |
A signed 32 bit (4 byte) integer.
|
s16 |
short |
A signed 16 bit (2 byte) integer.
|
u8 |
byte |
An unsigned 8 bit (1 byte) integer.
|
u4 |
nibble |
An unsigned 4 bit (half-byte) integer.
|
u2 |
half nibble |
An unsigned 2 bit (quarter-byte) integer.
|
f32 |
float |
A 32 bit float.
|
string |
String |
A variable-length piece of text prefixed by its length encoded as an s32.
|
f32[3] |
Vector3 |
Three floats typically used to encode things like precise positions (XYZ), colors (RGB), etc.
|
Region header
Field |
Type |
Possible values |
Comment
|
Magic |
s32 |
0xFFECCEAC |
Never changes, used for validation/identification.
|
FileVersion |
s32 |
0 |
Increments when the format changes in a non-backwards compatible way. Currently at 0.
|
CompressionType |
s32 |
0: UNCOMPRESSED |
Not yet used, support for file compression has not been implemented.
|
ChunkCount |
s32 |
0-4096 |
The number of chunks that are encoded in the data.
|
Chunk offset table
Each chunk has an offset to its data stored in the file. The game uses these offsets to decide where in the file to begin reading chunk data.
Chunk offsets
Field |
Type |
Possible values |
Comment
|
ChunkOffsets |
s32[4096] |
-1 or absolute offset |
The positions in the data where each chunk's data begins. A value of -1 means the chunk has not been saved in the file.
|
Chunk data
The following data is repeated for every chunk in the file. The number of chunks that are encoded are reflected in the header's encoded chunk count.
Every chunk has a header. This header is used to perform validation. If the index of the chunk data being read does not match the index computed from the chunk's XYZ position the game will generate an exception.
Chunk Header
Field |
Type |
Possible values |
Comment
|
ChunkSize |
s32 |
positive integer |
The size of the chunk's data in bytes. The game does not currently use this value but does generate it.
|
FileVersion |
s32 |
0 |
Always equal to the file version of the region when that chunk was last saved.
|
ChunkX |
s32 |
any integer |
The X coordinate of the chunk in the chunk grid, used for validation.
|
ChunkY |
s32 |
any integer |
The Y coordinate of the chunk in the chunk grid, used for validation.
|
ChunkZ |
s32 |
any integer |
The Z coordinate of the chunk in the chunk grid, used for validation.
|
Block data
Each chunk stores block data in it.
Block data type
Field |
Type |
Possible values |
Comment
|
ChunkType |
s8 |
1: SINGLE_BLOCK, 2: LAYERED |
The type of chunk block data being stored. SINGLE_BLOCK chunks have a singular block which fills them. LAYERED chunks have a series of block layers.
|
If the block data type is SINGLE_BLOCK:
Block data type
Field |
Type |
Possible values |
Comment
|
BlockState |
string |
E.g. base:air[default] |
The namespaced ID and block state of the block which fills this chunk.
|
If the block data type is LAYERED:
LAYERED BLOCK DATA
Field |
Type |
Possible values |
Comment
|
PaletteSize |
s32 |
1-4096 |
The number of unique block state keys in the palette.
|
Palette |
string[PaletteSize] |
Array of strings |
All unique namespaced ID and block states in the chunk. May contain block states that are never referenced.
|
Each layer is structured in the following way:
BlockLayer
Field |
Type |
Possible values
|
LayerType |
s8 |
1: LAYER_SINGLE_BYTE,
|
|
|
2: LAYER_SINGLE_INT,
|
|
|
3: LAYER_HALFNIBBLE,
|
|
|
4: LAYER_NIBBLE
|
|
|
5: LAYER_BYTE
|
LayerBytes |
Depends on layer type, see below |
|
1: LAYER_SINGLE_BYTE
Field |
Type |
Possible values |
Comment
|
Value |
u8 |
0-255 |
The index into the palette all blocks in this layer reference.
|
2: LAYER_SINGLE_INT
Field |
Type |
Possible values |
Comment
|
Value |
s32 |
0-4095 |
The index into the palette all blocks in this layer reference.
|
3: LAYER_HALF_NIBBLE
Field |
Type |
Possible values |
Comment
|
Value |
u2[256] |
256 half-nibbles (64 bytes), 0-3 |
The index into the palette all blocks in this layer reference.
|
4: LAYER_NIBBLE
Field |
Type |
Possible values |
Comment
|
Value |
u4[256] |
256 nibble values (128 bytes), 0-15 |
The index into the palette all blocks in this layer reference.
|
LAYER_BYTE
Field |
Type |
Possible values |
Comment
|
Value |
u8[256] |
0-255 |
The index into the palette all blocks in this layer reference.
|
Sky light data
The sky light levels for each block in the chunk.
SkyLight
Field |
Type |
Possible values |
Comment
|
Type |
u8 |
1: NONE, 2: LAYERED, 3: SINGLE |
The type of sky light data stored in the chunk.
|
If the type field is NONE (1) there will be no sky light data.
If the type field is SINGLE (3) the sky light data will be encoded as follows:
SkyLightSingle
Field |
Type |
Possible values |
Comment
|
LightLevel |
u8 |
0-15 |
The light level in the entire chunk. Values should not go above 15, although the game does not enforce this.
|
If the field is LAYERED (2) the sky light data will be encoded as follows:
SkyLightLayered
Field |
Type |
Possible values
|
Layers |
SkyLightLayer[16] |
See below
|
SkyLightLayer
Field |
Type |
Possible values |
Comment
|
Type |
u8 |
1: SINGLE, 2: NIBBLE |
The type of layer data being encoded.
|
1: SINGLE
Field |
Type |
Possible values |
Comment
|
Type |
u8 |
0-15 |
The light level in the entire layer. Values should not go above 15, although the game does not enforce this.
|
2: NIBBLE
Field |
Type |
Possible values |
Comment
|
Type |
u4[256] |
256 nibble values (128 bytes), 0-15 |
The light levels for each block in the layer.
|
Block light data
The block light levels & colors for each block in the chunk.
BlockLight
Field |
Type |
Possible values |
Comment
|
Type |
u8 |
1: NONE, 2: LAYERED |
The type of block light data stored in the chunk.
|
If the type field is NONE (1) there will be no block light data.
If the field is LAYERED (2) the sky light data will be encoded as follows:
BlockLight
Field |
Type |
Possible values
|
Layers |
BlockLightLayer[16] |
See below
|
BlockLightLayer
Field |
Type |
Possible values |
Comment
|
Type |
u8 |
1: SINGLE, 2: SHORT |
The type of block light layer being encoded.
|
1: SINGLE (Light levels per color for the entire layer)
Field |
Type |
Possible values
|
red |
u8 |
0-15 |
Red light level. Values should not go above 15, although the game does not enforce this.
|
green |
u8 |
0-15 |
Green light level. Values should not go above 15, although the game does not enforce this.
|
blue |
u8 |
0-15 |
Blue light level. Values should not go above 15, although the game does not enforce this.
|
1: SHORT (Light levels per block)
Field |
Type |
Possible values |
Comment
|
Light levels |
LightLevel[256] |
See below |
Light level/color, 4 bits per channel
|
LightLevel
Field |
Type |
Possible values |
Comment
|
red |
u4 |
0-15 |
Red light level
|
green |
u4 |
0-15 |
Green light level
|
blue |
u4 |
0-15 |
Blue light level
|
reserved |
u4 |
0-15 |
Not used
|