This document describes the LEP project file format, used to store map/project data in a compact, binary format for editors and runtime usage.
LEP files are binary files designed for efficient storage and fast read/write operations. Each file consists of:
- Header - identifies the file and format version
- Metadata - file name, date, editor version
- Map dimensions - width and height of the tile grid
- Tile data - sparse list of tile entries
| Field | Type | Size | Description |
|---|---|---|---|
| Magic | byte[4] | 4 | Always 'L' 'E' 'P' 0 – identifies LEP files |
| Format Version | uint32 | 4 | Current version = 3. Used for backward compatibility |
| Field | Type | Size | Description |
|---|---|---|---|
| File Name | string | variable | UTF-8 encoded string. Length prefixed with int32 |
| Date | Int64 | 8 | File creation date in UTC ticks (DateTime.Ticks) |
| Editor Version | string | variable | UTF-8 encoded string. Length prefixed with int32 |
ℹ Notes:
- Strings are stored as:
[int32 length][UTF-8 bytes]- Maximum string length: 1 MB
| Field | Type | Size | Description |
|---|---|---|---|
| Width | ushort | 2 | Number of columns (max 16384) |
| Height | ushort | 2 | Number of rows (max 16384) |
ℹ Notes:
- In versions < 3, width and height were stored as
uint32(4 bytes)- In version 3, they are
ushort(2 bytes) to reduce file size
Tiles are stored as a sparse list of entries. Only tiles with non-default values need to be written.
Default tile color:
R = 0; G = 0; B = 0; A = 255
Tiles are stored sequentially after:
int32 tileCount
The interpretation of tile entries depends on the format version.
Version 3 introduces palette-based compression using operation codes.
Each tile entry starts with:
| Field | Type | Size | Description |
|---|---|---|---|
| OpCode | byte | 1 | Determines how tile color is encoded |
Two encoding modes are supported.
Stores a full color value.
| Field | Type | Size | Description |
|---|---|---|---|
| OpCode | byte | 1 | Value = 0 (OP_DATA) |
| X | ushort | 2 | Column position (0-based) |
| Y | ushort | 2 | Row position (0-based) |
| R | byte | 1 | Red component (0–255) |
| G | byte | 1 | Green component (0–255) |
| B | byte | 1 | Blue component (0–255) |
| A | byte | 1 | Alpha component (can store additional properties) |
Total size: 9 bytes
After reading this tile:
- The color is inserted into the palette cache
- Future tiles with the same color may use OP_INDEX
References a previously stored color from the palette.
| Field | Type | Size | Description |
|---|---|---|---|
| OpCode | byte | 1 | Value = 1 (OP_INDEX) |
| X | ushort | 2 | Column position (0-based) |
| Y | ushort | 2 | Row position (0-based) |
| Palette Index | byte | 1 | Index of color stored in palette |
Total size: 6 bytes
The palette index refers to an entry in the 64-slot palette cache.
Version 3 introduces a small color palette cache.
Properties:
- Fixed size: 64 entries
- Initially filled with zeroed entries
- Updated when
OP_DATAtiles are read
Hash function used to compute palette slot:
(r * 3 + g * 5 + b * 7 + a * 11) & 63When OP_DATA is processed:
- The color is hashed
- The palette entry at that index is replaced
ℹ Notes:
- Palette entries may be overwritten when hash collisions occur.
In versions 2, tiles were stored without compression.
Each tile stored its full color directly, with no palette.
| Field | Type | Size | Description |
|---|---|---|---|
| X | int32 | 4 | Column position (0-based) |
| Y | int32 | 4 | Row position (0-based) |
| R | byte | 1 | Red component (0–255) |
| G | byte | 1 | Green component (0–255) |
| B | byte | 1 | Blue component (0–255) |
| A | byte | 1 | Alpha component (can store additional properties) |
Total size: 12 bytes
ℹ Notes:
- No operation code or palette was used.
- Tiles were simply stored sequentially.
Deprecated and no longer supported by the current reader.
Steps:
- Open file as a binary stream.
- Read the header (magic + version).
- Parse metadata: file name, date, editor version.
- Read map dimensions (width, height)
- Read tile count and allocate array
- Decode tiles depending on version.
- Tiles not stored in the file should be initialized to default color (R=0, G=0, B=0, A=255)
Each tile is read as:
int32 x
int32 y
byte r
byte g
byte b
byte aValues of x and y are cast to ushort.
Each tile begins with an OpCode.
If OpCode is:
0 -> OP_DATA
1 -> OP_INDEX
The tile is decoded accordingly and the palette cache is updated when necessary.
When writing version 3 files:
- Write magic bytes (
'L' 'E' 'P' 0) and format version (3) - Write metadata strings as length-prefixed UTF-8
- Write date in UTC ticks
- Write map width and height as
ushort - Write tile count
- Initialize palette[64]
- For each tile:
- compute palette hash
- if palette contains same color then write
OP_INDEX - otherwise write
OP_DATAand update palette
ℹ Notes:
- Validate that width/height ≤ 16384 and tile array is non-null
- Stored in binary for efficiency
| Version | Differences |
|---|---|
| 1 | Deprecated format. Not supported. Better not use. |
| 2 | Original binary format. |
| 3 | ushort positions + palette compression |
Readers should:
- detect the format version
- select the correct tile decoding method
- Compact: small footprint due to binary storage and ushort for positions
- Versioned: backward-compatible reading of older versions
- Editable: metadata and tile color data allow easy inspection/modification
ProjectData project = new ProjectData
{
fileName = "MyLevel.lep",
date = DateTime.UtcNow,
editorVersion = "1.0.5",
width = 128,
height = 128,
tiles = new TileData[width * height]
};
// Example tile
tiles[0] = new TileData
{
x = 0,
y = 0,
color = new ColorData
{
r = 255,
g = 0,
b = 0,
a = 255
}
};ℹ Notes:
- Each tile corresponds to a single cell in the map grid.