Skip to content

Level records

Zalasus edited this page Sep 21, 2018 · 3 revisions

This documents what we know about the record types found in *.lvl files.

(0x0000) Level name and database references

struct db_ref 
{ 
    uint16_t index; 
    uint16_t ???; 
    string path;   // relative to *.lvl file
};

string                  level_name;
uint32_t                max_width;
uint32_t                max_height;
uint32_t                db_ref_count;
db_ref[db_ref_count]    db_refs;

(0x0001) Layers

struct color4b 
{ 
    uint8_t red; 
    uint8_t green; 
    uint8_t blue; 
    uint8_t dummy = 0;
};

struct layer_def
{
    uint32_t                        id;
    uint32_t                        width; // (#vertices - 1); a layer with width and height of 1 is a square with 1 vertex in each corner
    uint32_t                        height;
    uint32_t                        type;  // 0 = floor, 1 = ceiling, 2 = between
    uint32_t                        origin_x; // position of layer's top left corner in the level, where 0/0 is the top left corner of the level
    uint32_t                        origin_z; 
    float                           world_height;
    string                          layer_name;
    uint32_t                        flags; // 2 = member of alternate blending group
    float                           light_direction; // in radians. east to west is 0, north to south is 0.5*pi etc.
    float                           light_ascension; // in radians. horizontal is 0, straight down is 0.5*pi, straight up is -0.5*pi
    color4b                         light_color;
    color4b                         ambient_color;
    uint32_t                        light_dropoff_type; // 0 = none; 1 = from north to south; 2 = E->W; 3 = S->N; 4 = W->E
    uint32_t                        visible_layer_count;
    uint32_t[visible_layer_count]   visible_layers; // this lists layers by their index in the definition block (not the layer's ID!)
};

struct layer_data_block
{
    uint32_t                 compressed_size;
    uint8_t[compressed_size] layer_data; // zlib-compressed
};

uint32_t                        layer_count;
layer_def[layer_count]          layer_definitions;
uint32_t                        ???; // could be compression level of layer data. seems to be always 1
layer_data_block[layer_count]   data_blocks;

The layer data block is zlib-compressed. The data is divided into two blocks without length fields. The length of these blocks is determined by the layer dimensions.

First block is vertex data. 4 bytes per vertex. Top left vertex appears first, then filled left to right, top to bottom.

  • First byte is type. Type 0 is normal. Values 0-75 are defined. This indicates water etc.
  • Second byte is always 0x01. No idea what it does yet.
  • Third and fourth byte seems to be height offset divided by two (can't change height in smaller units). Value is biased, so 0x8000 means an offset of 0, 0x7FFF means -2 and 0x8002 means +4 etc.

Second block is cell data. Four vertices make once cell, each cell consists of two triangles. 26 bytes per cell, top left cell appears first, then filled left to right, top to bottom.

First 2 bytes seem to be flags. Low bit is cell division:

       +-+      +-+
    0= |/|   1= |\|
       +-+      +-+

Flags are followed by two 4-byte-words which are database references for the face textures. First is for left triangle, second is right triangle.

The texture references are followed by 16 bytes for texture coordinates. Each coordinate is represented by a uint16_t where 0x0000 means 0.0 in texture space and 0xffff means 1.0. The coordinates are stored as:

  Uc Ud Ub Ua Vc Vd Vb Va

with corners defined as such:

 a-b
 | |
 c-d

(0x0002) Layer groups

This probably is an editor thing without relevance for the game.

struct layer_group
{
    string                  group_name;
    uint32_t                layer_count;
    uint32_t[layer_count]   layers; // lists IDs of all member layers
};

uint32_t                    group_count;
layer_group[group_count]    groups;

(0x0020) Objects

This record does not appear if the level does not contain any objects.

struct object
{
    uint32_t                    id;
    uint16_t                    class_id;
    uint16_t                    class_db_index;
    uint32_t                    lighting_layer_id;
    float                       x_position;
    float                       y_position; // elevation axis, always relative to model origin
    float                       z_position;
    uint32_t                    flags; // 0x01 = visible in-game, 0x100 = scaled
    uint16_t                    initial_event_count;
    uint16_t                    link_count;
    uint16_t[link_count]        linked_objects; // list of indices in object table, not object IDs!
    uint16_t                    x_rotation; // in degrees
    uint16_t                    y_rotation; //    "
    uint16_t                    z_rotation; //    "
    if(flags & 0x100) // these only appear if scaled flag is set
    {
        float                   x_scaling; 
        float                   y_scaling;
        float                   z_scaling;
    }
    uint32_t                    data_area_size; // in dwords (4 byte blocks)
    uint32_t                    parameter_count;  // how many parameters in this object are changed compared to the class
    uint32_t[data_area_size]    parameter_data;   // note that compared to classes, count and data are swapped
    uint16_t[~parameter_count]  parameter_indices; // location x of this contains the index field x as defined here has in this object's class
    field_def[parameter_count]  parameter_definitions; // this has the same format as the one of classes
};

uint16_t    object_count;
object[~]   objects;

Clone this wiki locally