
+================================+
|Riot Engine database file format|
+================================+

This file documents the database file format ("SRSC") used by the Riot Game Engine by Surreal Software.

All of the following information has been aquired through reverse engineering. 
Some basic information was taken from a script found at: http://aluigi.altervista.org/bms/drakan.bms.

This document uses a pseudo-C syntax for data structure definitions. You'll figure it out.
    "T[uint16]" refers to an array of T with a uint16 length prefix
    "ref" refers to another resource and is a struct { uint16 id, db_index; }.
    All structs are packed. No alignment padding unless explicitly specified.

PLEASE NOTE: This documentation is not complete. I could not identify all fields used in all files
and am still working on figuring out things like animations. Feel free to make a pull request should
you find out things not documented here.


How the engine manages it's assets
==================================
    
    The Riot Engine stores all it's assets in database files with a common format. All these files use
    the magic number "SRSC", so that's what we are gonna call the format from now on.
    
    An SRSC file consists of a list of records of which one or more may represent a single game asset.
    Sounds for instance consist of only one record, while models are made up of at least 6 records each.
    
    Assets are managed in so called "databases", defined by a *.db file. The name of that file (without extension) 
    is that database's name. The *.db file is plaintext formatted. More about that file can be found in chapter "Database definition".

    A database's different types of assets are stored in seperate SRSC files stored in the same directory as the defining *.db file.
    The type of asset contained in an SRSC file is identified by it's file extensions.
    These are the possible file extensions and their associated asset types for databases:
    
        *.mod    Models
        *.adb    Character model animations
        *.ssd    "STOMP"-Sequences
        *.odb    Objects
        *.sdb    Sounds
        *.txd    Textures

    Apart from those, the following files are also SRSC formatted, but aren't part of databases:

        *.rrc   Contains various resources like Music; found in engine toplevel
        *.lvl   Contains level geometry, object placements etc.


Database definition
===================
    A database is defined by it's *.db file. It is a plaintext file with the structure:
        
        version <version number>
        dependencies <dependency count>
        <index of dependency 1>  <path of dependency 1>
        <index of dependency 2>  <path of dependency 2>
            ...
        <index of dependency n>  <path of dependency n>

    The listed database paths omit the *.db extension and are relative to the location of the current *.db file.
    Note that the given indices aren't necessarily consecutive or start at a given number (e.g. 2,4,6). The only
    rule is that these are 1-based. Index 0 is used to refer to the current database and can not be used for 
    dependencies.
    
    These dependencies are used for internally referencing records. See subchapter "Referencing a record"
    in chapter "Basic SRSC format".


Basic SRSC format
=================

    Data formatting
    ---------------
        All fields are little-endian.

        SRSC files are word-aligned, so all fields start at an even offset.

        Strings are plain ASCII, prefixed with a 16-bit length field. Strings with an odd length
        are padded with a single 0 byte so the aligment is preseved. This padding is also included
        in the length field, so a string length can only ever be even. Strings are not zero-terminated.

        Floats use standard IEEE 754 format, single precision.

   
    File structure
    --------------
        Header:

            uint8[4]    magic = 'SRSC';
            uint16      version = 0x0100; // speculative
            uint32      directory_offset;
            uint16      record_count;

        This is followed by a block containing the record bodies. After that,
        starting at directory_offset one can find a list of all records with pointers to the data area above:

            struct directory_entry
            {
                uint16  record_type;
                uint16  record_id;   // see "Record ID and grouping"
                uint16  group_id;
                uint32  body_offset;
                uint32  body_size;
            }

            directory_entry[record_count]   directory;

            // end of file

        
    Record types
    ------------
        All records have a 16-bit type. The high byte of that type defines the associated asset type, while the interpretation
        of the low byte depends on the given asset type. This type also defines the structure of the record body.
        See top chapter "Record types" for a list of all known record structures.

    Record ID and grouping
    ----------------------
        Most records are identified by an ID, found in the SRSC directory. This is a running number that is
        unique per SRSC file. If a record has no ID, the ID field has a value of 0.

        Some asset types allow grouping of records. These are namely models, classes and sounds. These asset types
        define a special record type for group definitions. This seems to always be the type with the low byte 0x01.
        That record's body consists of a single string, which is the groups name.
        The record ID of that group definition record is that group's ID. All records whose directory entry states a group ID
        different than 0 are part of the group with the ID stated. 
        Group definition records can also be part of a group, making them subgroups of the given group. Record that are children of
        those subgroup use the subgroup's ID instead.
        Note that even across groups, all record IDs must be unique, IDs of groups included.

        Grouping seems like it's only use is convenience for the level designer. Group definitions only ever show up in the editor,
        and group IDs are not needed for identifiying a record since record IDs are unique across groups.


    Referencing a record
    --------------------
        Records are referenced by their ID. Since these IDs aren't unique across different SRSC files, all references to other records
        include an index of their database's dependency table. The database found at that index is the one containing the referenced record.
        Note that this index is 1-based, as an index of 0 refers to the current database.

        While IDs also aren't unique across a databases different asset archives, this is not a problem since all references state the type
        of the referenced record either implicitly (e.g. model field in class record) or explicitly (other class fields).


Record types
============

    The following is an incomplete list of record types, grouped by asset type.

    Level (0x0000 - 0x0020)
    -----------------------
    
        see lvl_format.txt
        

    Texture (0x0030-0x0040)
    -----------------------

    [0x0030] Palette
        struct color { uint8 r; uint8 g; uint8 b; uint8 flags; }; // these are copied verbatim from the RIFF PAL file
        color[uint16]   palette_colors;

    [0x0040] Texture data (record ID is texture ID)
        uint32        width; // editor only allows 32, 64, 128 or 256
        uint32        height; // editor allows arbitrary height
        uint32        pitch;
        uint16        bits_per_pixel; // indicates 5 5 5+1, 4 4 4+4 or 3 3 2+8 formats for alpha channels. 8 8 8+8 format is reported as "not yet implemented"
        uint32        alpha_bits_per_pixel;
        uint32        colour_key;
        ref           mip_map;
        ref           alternate_texture;
        ref           bump_map_texture;
        uint8         animation_fps;  // Only valid if not "next frame".
        // Flags (the following are partially speculative)
        // 0x80 "keep high quality",
        // 0x40 "dynamic texture" is ticked, 
        // 0x20 "next frame", 
        // 0x08 this is a mipmap
        // 0x04 this is an alternate of another texture,
        // 0x02 an alpha channel is integrated into the image data
        // 0x01 this has a seperate alpha map attached after the image data
        uint8         flags;  
        uint16        mip_map_number; // as in "this texture is mip map number ..., seems to have other uses: this is 0x10 for bump maps         
        ref           material_object;
        uint32        ref_count;
        uint32        compression_level; // (0 = none, 1 = lowest, 9 = highest)
        uint8[uint32] zlib_compressed_data;

        if flags & 1:
            // Unknown interpretation.
            // This data might need to be appended to the above, or maybe not.
            // Or maybe this data is corrupt and unusable.
            uint8[uint32] separate_alphamap_data;
            
        // Texture animations are built by appending consecutive "next frame" textures to the first frame.

    Class (0x0100 - 0x0102)
    -----------------------

    [0x0100] Riot Function Library reference
        uint8[8]    ???
        string      relative_path_to_rfl_file; // for Drakan, this is always Dragon.rfl in the engine root dir
     
    [0x0101] Class group definition
        string      group_name;

    [0x0102] Class data
        struct field_def { uint32 type; string name; };
        /*LSB of type is base type 
         type 0x01 = integer (signed), 
         type 0x02 = float,
         type 0x03 = class,
         type 0x04 = model
         type 0x05 = sound
         type 0x06 = ??
         type 0x07 = enum (possible items depends on field; are stored as uint32)
         type 0x08 = character channel (probably bones)
         type 0x09 = animation
         type 0x0a = string
         type 0x0b = sequence
         type 0x0c = landscape layer
         type 0x0d = ??
         type 0x0e = texture
         type 0x0f = colour
         if second byte is 0x10, field is an array of that type
         */
        string          class_name;
        uint16          unk0;
        ref             model; // none if zero
        uint16          rfl_class_id;
        uint16          icon_number; // Selected class icon in the editor. Should have no effect on gameplay
        uint32          number_of_fields;
        uint32[uint32]  field_value_data; // for layout see "Class format"
        field_def[number_of_fields]  field_definitions;


    Model (0x0200 - 0x0209)
    -----------------------

    struct vect2f { float x, y; };
    struct vect3f { float x, y, z; };
    struct mat3f { vect3f axes[3]; };
    struct mat3x4f { mat3f linear; vect3f translation; };
    struct sphere { vect3f center; float radius; };
    struct obb { vect3f center; mat3f xform; };
    struct obb_ex { obb obb; uint16[uint16] unk_poly_indices; };
    struct bounding_info { sphere sphere; obb obb; };
    
    [0x0200] Model name ( this one is always the first record in the directory with the model's ID)
        string      model_name;
        uint32      shading_type; (0 = none, 09 = flat, 0a = smooth, flags seem to change this in weird ways)
    
    [0x0201] Model group definition
        string      group_name;

    [0x0202] ???
        (Doesn't seem to be associated with a particular model. Has ID and GID = 0)

    The following records must all appear once per model (also 0x0200) and thus have the same record ID.

    [0x0203] Vertices
        vect3f[uint16]   vertices;
        
    [0x0204] Faces
        struct face_vertex
        {
            uint16 vertex_index;
            vect2f uv;
        };
        
        struct face
        {
            uint16 unk0;
            uint16 vertex_count; // 3 or 4.
            uint16 texture_index; // Indexes into the model's texture list.
            face_vertex[vertex_count] vertex_list;
        };
        
        face[uint16]  faces;
        
    [0x0205] ??? table // Rare. See Resources Powerups Fire02 for an example.
        struct table_entry
        {
            uint16[3]           vertices;
            uint16[2 + uint16]  polys; // Typically, there's 4 or 5 polys in this list.
        };
        table_entry[uint16]  unk_table;
    
    [0x0206] Texture list
        ref[uint32]  textures;
        
    [0x0207] Bounding data // Possibly for culling. Possibly for physics.
        bounding_info bounding_info;
        uint16        num_shapes;
        uint16        is_obb;
        pair<uint16, uint16>[num_shapes] unk_links; // These are unknown shape indices.
        if is_obb:
            obb_ex[num_shapes] boxes;
        else:
            sphere[num_shapes] spheres;
            
    [0x0208] Character data // LOD, scene tree, channels, animation refs, bounding info
        struct node_info
        {
            char[32]  name;
            int32     parent_index; // negative if no parent
        };
        struct node_more_info
        {
            mat3x4f   inverted_model_rel_xform; // Inverted!!
            int32     mesh_index; // Unknown use. These values can be seen in the editor.
            int32     first_child_index; // negative if no children
            int32     next_sibling_index; // negative if end of parent's child list
            vertex_weight[uint16][num_lods] vertex_weights_per_lod; // A list of vertex weights for each LOD that are affected by this bone.
        };
        struct vertex_weight
        {
            uint32    vertex_index;
            float     weight;
        };
        struct lod_mesh_info
        {
            uint16    mesh_index;
            uint16    unk;
            uint32    usage;
            uint32    node_index;
            uint32    first_vertex_index;
            uint32    num_vertices;
            uint32    first_poly_index;
            uint32    num_polys;
        };
        struct channel // Weapon attachment and dismemberment?
        {
            // Dismemberment.
            struct lod_cap
            {
                uint32 first_cap_poly_index;
                uint32 num_cap_polys;
                uint32 first_part_poly_index;
                uint32 num_part_polys;
                uint32 unk;
                vertex_weight[uint16] vertex_weights;
            };
            
            int32           node_index;
            mat3x4f         xform_a; // Unknown.
            mat3x4f         xform_b; // Unknown.
            lod_cap[uint16] lod_caps;
        };
        
        bounding_info                    bounding_info;
        uint16                           num_lods;
        string[num_lods - 1]             lod_names;  // The first LOD is unnamed.
        node_info[uint16]                node_infos; // Contains some empty entries at the end.
        node_more_info[uint16]           more_node_infos; // The size of this array should be no greater than the size of node_infos.
        lod_mesh_info[uint16][num_lods]  lod_mesh_infos; // An array of mesh infos for each LOD.
        uint16                           anim_ids; // Just IDs. It seems that only animations in the same DB can be referenced.
        channel[uint16]                  channels;
        
    [0x0209] ???
        uint8[6]    ???
        (these records are consistently 6 bytes long and contained only zeroes in at least one tested model)
        

    Sound (0x0301 - 0x0302)
    -----------------------

    [0x0301] Sound group definition
        string      group_name;
        
    [0x0302] Sound data;
        string     sound_name;
        uint32     flags; // 0x04 = flush after playing, 0x08 = play looping
        uint16     channels;
        uint16     bits;
        uint32     frequency;
        int32      volume; // 0 == full volume, -5000 == silent. Decibels * 100 (millibels). Amplitude factor = 10^(volume / 100 / 20)
        float      dropoff; // min 0, max 30
        uint32     priority; // range 0-10
        uint32     decompressed_size; // the editor alters these three fields globally for a database!
        uint32     compression_level; // 0 = none, 1 = lowest, 9 = highest
        if compression_level > 0:
            uint8[uint32] audio_data;
        else:
            uint8[decompressed_size] audio_data;
        
    Sequence (0x0310 - 0x0312)
    --------------------------
    
    [0x0310] ???
        // 8 bytes. not all zero
        
    [0x0311] Sequence
        struct quat { float x, y, z, w; };
        
        struct action
        {
            struct xform_key_frame
            {
                quat rotation;
                vect3f position;
                uint32 flags; // Probably. Always 0.
                uint16 interpolation; // 0x00 == NONE, 0x10 == LINEAR_LINEAR, 0x30 == SINE_SPLINE
                uint16 relative_to; // Probably. Always 0.
            };
            
            struct start_anim
            {
                uint32 channel_index;
                ref anim;
                uint32 unk08; // Always 0.
                float transition_time;
                float speed;
                uint32 root_node_translation_mode_flags; // 2 bits for each axis. 0b00 == MESH, 0b01 == FRAME, 0b10 == NONE, 0b11 = INVALID.
            };
            
            struct play_sound
            {
                ref sound;
                float duration;
                uint16 volume; // [0..10000]
                uint16 flags; // 0x8 == flush from memory after playing, 0x10 == mono, 0x30 == notify actor (what does this do?)
            };
            
            struct attach
            {
                uint32 other_actor_id = 0;
                uint32 this_actor_channel_id = 0;
            };
            
            struct run_stop_ai
            {
                uint32 enable_ai;
            };
            
            struct show_hide
            {
                uint32 is_visible;
            };
            
            struct message
            {
                uint32 message_number; // Unknown meaning. Possibly hard-coded behaviour.
                uint32 unk0; // Always 0.
            };
            
            uint16 type;
            float t;

            if type == 0: xform_key_frame data;
            if type == 1: start_anim data;
            if type == 3: play_sound data;
            if type == 4: attach data;
            if type == 5: run_stop_ai data;
            if type == 6: show_hide data;
            if type == 7: message data;
            // Type 2 is probably texture anims, and 8 is probably music, else, you've found something new.
        };
        
        struct actor
        {
            string name;
            uint32 id;
            uint32 unk0; // Always 1
            uint32 unk1; // Always 0
            uint32 level_entity_id; // probably
            action[uint32] actions;
        };
        
        string        sequence_name;
        uint32        unk0; // flags?
        uint32        unk1;
        uint32        unk2;
        actor[uint32] actors;
        
    
    [0x0312] Sequence group definition
        string      group_name;
        
    
    Misc
    ----

    [0x0402] End record?
        (one of these appears in many databases, often last. it's size seems to always be 4)
        
    Animation (0x0501 - 0x0503)
    ---------------------------

    [0x0501] Info
        float duration;
        uint32 original_num_frames;
        uint32 num_frames;
        uint32 unk0;
        uint32 model_num_nodes;
        uint32 model_num_channels;
        uint32 unk1; // Non-Looping flag?
        uint32 ref_count;
        float rotation_threshold;
        float scale_threshold;
        float translation_threshold; // Probably.
        
    [0x0202] Data
        struct frame
        {
            float t;
            mat3x4f xform; // Might be model-relative or parent-relative. Not sure yet.
        };
        
        frame[uint16] frames;
        
    [0x0503] Frame Lookup
        struct node_frames_index_range
        {
            uint32 first_frame_index;
            uint32 num_frames;
        };
        
        node_frames_index_range[uint16] frames_lookup; // One for each node.

Audio format
============
    Audio data is stored as raw PCM samples (optionally zlib-compressed). It is stored sample after sample without
    any metadata.

    Stereo files are stored interleaved, left channel first.

    8 bit samples are stored unsigned with a bias of 128. 16 bit samples are stored signed.


Texture format
==============
    Textures are encoded as zlib-compressed raw pixels.
    Rows are stored bottom-up from left to right.
    
    The pixel format is selected by the bpp-field in the texture header.
    The following pixel formats have been found:
    
    8 bpp
    -----
        Each pixel is represented by one byte that is an index in the colour palette, found in the 0x0030 record.
        
    
    16 bpp
    ------
        Format depends on the type of alpha map used. If no alpha map is present or it is stored seperately (flag 0x01),
        the following format is used:
            5-6-5-format (note the LE byte order!)
            
            RRRRRGGG GGGBBBBB
             Byte 1   Byte 0
         
            Note that the green channel uses 6 bit while red and blue only use 5.
            
        If the alpha map is integrated into the texture (alpha channel, indicated by flag 0x02),
        the format used is determined by the alpha-bpp-field. The following formats have been found:
        
            ABPP    R:G:B+A bits
            1       5:5:5+1
            4       4:4:4+4
            8       3:3:2+8
            
        The 8:8:8+8 is sometimes listed in the editor but reported as "not yet implemented", probably due to the
        fact the 8-alpha-bit setting is already occupied by the 3:3:2+8 format.
        Please note that these formats also use LE encoding.


    24 bpp
    ------
        8-8-8-format

        RRRRRRRR GGGGGGGG BBBBBBBB
         Byte 0   Byte 1   Byte 2
         
        Note that this is not adjusted for any byte order!


    32 bpp
    ------
        GGGGGGGG BBBBBBBB XXXXXXXX RRRRRRRRR
        
        X is unused. Note that this is not adjusted for any byte order!
        Even when adjusted, this order makes little sense. Probably this feature hadn't much work put into it.
        When trying to make a 8:8:8+8 alpha channel texture, the editor reports "not yet implemented". While 32bpp textures with
        no alpha channel can be created, it is doubtful that any of these will ever occur. I couldn't find 32bpp textures in any
        of the Drakan databases.


Class format
============
    Classes are stored as a set of fields. A class record first contains a short header, then a data area consisting of
    dwords (4 bytes). After that, a list of field definitions is stored. Each definition contains the field's data type and
    the field's name. It is likely that the index of the field definition can be used to identify the field (objects in level
    files, which list only modified fields, explicitly state the index these fields have in their class). Otherwise, a field's
    name is a safe albeit inefficient way to identify it.
    
    Each of the field definitions corresponds to one dword in the data area, in the same order as they appear in the field 
    definition list (so the first field defined would correspond to the first dword at the beginning of the data area).
    
    Some fields, however, may require more than a dword to store their contents. These are namely strings and arrays.
    For this reason, the data area may contain more dwords than the number of fields. In this trailing area, the actual string
    or array data is stored, while the dword at the string's/array's index instead looks like this:
    
        uint16 offset;
        uint16 length;

    Where offset is the offset in dwords starting from the beginning of the data area where the string's/array's data can be found,
    and length is the number of dwords contained in that trailing field. For arrays this is equal to the number of elements contained,
    while strings are packed into as many dwords as required, and then padded with zero bytes if the string's size is not divisible by 4.
