/*
 * tombraider.bfft - Tomb Raider 1 - 3 Level File Format Description
 * 2014, Thomas Buck <xythobuz@xythobuz.de>
 *
 * Based on the TR Rosetta Stone (http://www.tnlc.com/eep/tr/TRosettaStone.html)
 * Developed for Binspector (https://github.com/binspector/binspector)
 */

// ########## General data structures ##########

// Rectangular faces
struct face4_t {
    // These are indices into the appropriate vertex list
    unsigned 16 little vertices[4];

    /*
     * Index into the object texture list (for textured objects)
     * or into the 8 or 16bit palette (for colored object).
     * When coloring is used, texture contains two indices:
     *   (texture & 0x00FF) = index into 8bit palette
     *   ((texture & 0xFF00) >> 8) = index into 16bit palette
     */
    unsigned 16 little texture;

    summary texture, ": ", vertices[0], " - ", vertices[1], " - ", vertices[2], " - ", vertices[3];
}

// Triangular faces
struct face3_t {
    // See face4_t for a description of the elements
    unsigned 16 little vertices[3];
    unsigned 16 little texture;

    summary vertices[0], " - ", vertices[1], " - ", vertices[2];
}

struct vertex_t {
    // These coordinates are relative!
    signed 16 little x;
    signed 16 little y;
    signed 16 little z;

    summary "X: ", x, " Y: ", y, " Z: ", z;
}

// ########## Room data structures ##########

struct room_vertex_t {
    /* 
     * Position of this vertex, relative to the x/z coordinates
     * of the room this vertex is a part of.
     */
    vertex_t vertex;

    /*
     * Room lighting is internal vertex lighting, except
     * for necessarily external sources like flares.
     * Room ambient lights and point sources are ignored.
     */
    signed 16 little lighting1;

    // TR1 lacks these attributes
    if (TRversion > 1) {
        /*
         * A set of flags for special rendering effects:
         *   0x8000 - Has something to do with water surfaces
         *   0x4000 - Underwater lighting modulation and movement,
         *            if viewed from above the water surface.
         *   0x2000 - Water/Quicksand surface movement
         *   0x0010 - Normal
         */
        unsigned 16 little attributes;

        // Almost always equal to lighting1
        signed 16 little lighting2;
    }

    summary summaryof(vertex);
}

struct room_sprite_t {
    // Index into the vertex list of the current room
    signed 16 little vertex;

    // Index into the sprite texture list
    signed 16 little texture;

    summary vertex, "-", texture;
}

struct room_door_t {
    // Which room this portal leads to
    unsigned 16 little adjoiningRoom;

    /*
     * Which way the portal faces (points away from adjacent room,
     * to be seen though it must point toward the viewpoint)
     */
    vertex_t normal;

    // The corners of this portal (right-hand rule applies wrt normal)
    vertex_t vertices[4];

    summary "To: ", adjoiningRoom;
}

struct room_sector_t {
    unsigned 16 little floorDataIndex;
    unsigned 16 little boxIndex;
    unsigned 8 little roomBelow;
    signed 8 little floor;
    unsigned 8 little roomAbove;
    signed 8 little ceiling;

    summary "Be.: ", roomBelow, " Ab.: ", roomAbove;
}

struct room_light_t {
    signed 32 little x;
    signed 32 little y;
    signed 32 little z;
    unsigned 16 little intensity1;

    if (TRversion > 1)
        unsigned 16 little intensity2;

    unsigned 32 little fade1;

    if (TRversion > 1)
        unsigned 32 little fade2;

    summary "X: ", x, " Y: ", y, " Z: ", z;
}

struct room_mesh_t {
    unsigned 32 little x;
    unsigned 32 little y;
    unsigned 32 little z;
    unsigned 16 little rotation;
    unsigned 16 little intensity1;
    
    if (TRversion > 1)
        unsigned 16 little intensity2;

    unsigned 16 little objectID;

    summary "ID: ", objectID, " X: ", x, " Y: ", y, " Z: ", z;
}

struct room_t {
    // Room Header
    signed 32 little x;
    signed 32 little z;
    signed 32 little yBottom;
    signed 32 little yTop;

    unsigned 32 little numDataToFollow;

    unsigned 16 little numVertices;
    room_vertex_t vertices[numVertices];

    unsigned 16 little numRectangles;
    face4_t rectangles[numRectangles];

    unsigned 16 little numTriangles;
    face3_t triangles[numTriangles];

    unsigned 16 little numSprites;
    room_sprite_t sprites[numSprites];

    unsigned 16 little numDoors;
    room_door_t doors[numDoors];

    unsigned 16 little numZSector;
    unsigned 16 little numXSector;
    room_sector_t sectorData[numZSector * numXSector];

    signed 16 little intensity1;

    if (TRversion > 1)
        signed 16 little intensity2;

    if (TRversion == 2)
        signed 16 little lightMode;

    unsigned 16 little numLights;
    room_light_t lights[numLights];

    unsigned 16 little numStaticMeshes;
    room_mesh_t staticMeshes[numStaticMeshes];

    signed 16 little alternateRoom;
    unsigned 16 little flags;

    if (TRversion == 3)
        unsigned 24 little roomLightColor;

    summary "X: ", x, " Ybot: ", yBottom, " Ytop: ", yTop, " Z: ", z;
}

// ########## Actor data structures ##########

struct animation_t {
    unsigned 32 little frameOffset;
    unsigned 8 little frameRate;
    unsigned 8 little frameSize;
    unsigned 16 little stateID;
    skip unknown[8];
    unsigned 16 little frameStart;
    unsigned 16 little frameEnd;
    unsigned 16 little nextAnimation;
    unsigned 16 little nextFrame;
    unsigned 16 little numStateChanges;
    unsigned 16 little stateChangeOffset;
    unsigned 16 little numAnimCommands;
    unsigned 16 little animCommandOffset;

    summary "State: ", stateID, " To: ", nextAnimation, "-", nextFrame;
}

struct state_change_t {
    unsigned 16 little stateID;
    unsigned 16 little numAnimDispatches;
    unsigned 16 little animDispatchOffset;

    summary "State: ", stateID;
}

struct anim_dispatch_t {
    signed 16 little low;
    signed 16 little high;
    signed 16 little nextAnimation;
    signed 16 little nextFrame;

    summary low, "-", high, " To: ", nextAnimation, "-", nextFrame;
}

struct moveable_t {
    unsigned 32 little objectID;
    unsigned 16 little numMeshes;
    unsigned 16 little startingMesh;
    unsigned 32 little meshTree;
    unsigned 32 little frameOffset;
    unsigned 16 little animation;

    summary "ID: ", objectID, " Anim: ", animation;
}

// ########## Other data structures ##########

struct static_mesh_t {
    unsigned 32 little objectID;
    unsigned 16 little mesh;
    vertex_t boundingBox[4];
    unsigned 16 little flags;

    summary "ID: ", objectID;
}

struct object_texture_vert_t {
    unsigned 8 little xCoordinate;
    unsigned 8 little xPixel;
    unsigned 8 little yCoordinate;
    unsigned 8 little yPixel;

    summary xCoordinate, "-", yCoordinate, ", ", xPixel, "-", yPixel;
}

struct object_texture_t {
    unsigned 16 little attribute;
    unsigned 16 little tile;
    object_texture_vert_t vertices[4];

    summary "Tile: ", tile;
}

struct sprite_texture_t {
    unsigned 16 little tile;
    unsigned 8 little x;
    unsigned 8 little y;
    unsigned 16 little width;
    unsigned 16 little height;
    signed 16 little leftSide;
    signed 16 little topSide;
    signed 16 little rightSide;
    signed 16 little bottomSide;

    summary x, "-", y, ", ", width, "-", height;
}

struct sprite_sequence_t {
    signed 32 little objectID;
    signed 16 little negativeLength;
    signed 16 little offset;

    summary "ID: ", objectID;
}

struct camera_t {
    signed 32 little x;
    signed 32 little y;
    signed 32 little z;
    signed 16 little room;
    unsigned 16 little unknown;

    summary "X: ", x, " Y: ", y, " Z: ", z;
}

struct sound_source_t {
    signed 32 little x;
    signed 32 little y;
    signed 32 little z;
    unsigned 16 little soundID;
    unsigned 16 little flags;

    summary "X: ", x, " Y: ", y, " Z: ", z;
}

struct box_t {
    if (TRversion > 1) {
        /*
         * In TR2 & TR3, these values are based on sectors,
         * so they need to be scaled (* 1024 units)
         */
        unsigned 8 little zMin;
        unsigned 8 little zMax;
        unsigned 8 little xMin;
        unsigned 8 little xMax;
    } else {
        // In TR1 there is no scaling
        signed 32 little zMin;
        signed 32 little zMax;
        signed 32 little xMin;
        signed 32 little xMax;
    }

    // Y value (no scaling)
    signed 16 little trueFloor;

    /*
     * Index into overlaps. The high bit is sometimes set,
     * this occurs in front of swinging doors and the like
     */
    signed 16 little overlapIndex;

    summary "X: ", xMin, "-", xMax, " Z: ", zMin, "-", zMax;
}

struct item_t {
    signed 16 little objectID;
    signed 16 little room;
    signed 32 little x;
    signed 32 little y;
    signed 32 little z;
    signed 16 little angle;
    signed 16 little intensity1;

    if (TRversion > 1)
        signed 16 little intensity2;

    unsigned 16 little flags;

    summary "ID: ", objectID, " X: ", x, " Y: ", y, " Z: ", z;
}

struct cinematic_frame_t {
    signed 16 little rotY;
    signed 16 little rotZ;
    signed 16 little rotZ2;
    signed 16 little posZ;
    signed 16 little posY;
    signed 16 little posX;
    signed 16 little unknown;
    signed 16 little rotX;
}

struct sound_details_t {
    signed 16 little sample;
    signed 16 little volume;
    signed 16 little unknown1;
    signed 16 little unknown2;

    summary "Sample: ", sample, ", Vol: ", volume * 100 / 32767, "%";
}

// ########## Main file layout ##########

struct main {
    unsigned 32 little version;

    if (version == 0x20) {
        notify "Tomb Raider 1 file detected...";
        const TRversion = 1;
    } else if (version == 0x2D) {
        notify "Tomb Raider 2 file detected...";
        const TRversion = 2;
    } else if ((version == 0xFF080038) || (version == 0xFF180038)) {
        notify "Tomb Raider 3 file detected...";
        const TRversion = 3;
    } else if ((version == 0xFFFFFFF0) || (version == 0x00345254)) {
        die "Tomb Raider 4 file detected but not supported!";
    } else {
        die "Unknown version number found!";
    }

    // TR2 & TR3 have their 8 & 16bit palettes here
    if (TRversion > 1)
        skip palettes[256 * 7];

    // 8bit textures
    unsigned 32 little numTextiles;
    skip textiles[256 * 256 * numTextiles];

    // TR1 does not have any 16bit textures
    if (TRversion > 1)
        skip textiles16[256 * 256 * numTextiles * 2];

    unsigned 32 little unused;

    unsigned 16 little numRooms;
    room_t rooms[numRooms];

    unsigned 32 little numFloorData;
    skip floorData[numFloorData * 2];

    unsigned 32 little numMeshData;
    skip meshData[numMeshData * 2];
    unsigned 32 little numMeshPointers;
    unsigned 32 little meshPointers[numMeshPointers];

    unsigned 32 little numAnimations;
    animation_t animations[numAnimations];

    unsigned 32 little numStateChanges;
    state_change_t stateChanges[numStateChanges];

    unsigned 32 little numAnimDispatches;
    anim_dispatch_t animDispatches[numAnimDispatches];

    unsigned 32 little numAnimCommands;
    signed 16 little animCommands[numAnimCommands];

    unsigned 32 little numMeshTrees;
    signed 32 little meshTrees[numMeshTrees];

    unsigned 32 little numFrames;
    skip frames[numFrames * 2];

    unsigned 32 little numMoveables;
    moveable_t moveables[numMoveables];

    unsigned 32 little numStaticMeshes;
    static_mesh_t staticMeshes[numStaticMeshes];

    // TR1 & TR2 have their object textures placed here
    if (TRversion < 3) {
        unsigned 32 little numObjectTextures;
        object_texture_t objectTextures[numObjectTextures];
    }

    unsigned 32 little numSpriteTextures;
    sprite_texture_t spriteTextures[numSpriteTextures];

    unsigned 32 little numSpriteSequences;
    sprite_sequence_t spriteSequences[numSpriteSequences];

    unsigned 32 little numCameras;
    camera_t cameras[numCameras];

    unsigned 32 little numSoundSources;
    sound_source_t soundSources[numSoundSources];

    unsigned 32 little numBoxes;
    box_t boxes[numBoxes];

    unsigned 32 little numOverlaps;
    skip overlaps[numOverlaps * 2];

    // TR1 has 6 bytes per zone entry, TR2 & TR3 have 10
    if (TRversion > 1)
        skip zones[numBoxes * 10 * 2];
    else
        skip zones[numBoxes * 6 * 2];

    unsigned 32 little numAnimatedTextures;
    skip animatedTextures[numAnimatedTextures * 2];

    // TR3 has its object textures placed here
    if (TRversion == 3) {
        unsigned 32 little numObjectTextures;
        object_texture_t objectTextures[numObjectTextures];
    }

    unsigned 32 little numItems;
    item_t items[numItems];

    skip lightMap[32 * 256];

    // TR1 places its 8bit palette here
    if (TRversion == 1)
        skip palette[256 * 3];

    unsigned 16 little numCinematicFrames;
    cinematic_frame_t cinematicFrames[numCinematicFrames];

    unsigned 16 little numDemoData;
    skip demoData[numDemoData];

    // TR1s sound map has 256 entries, TR2 & TR3 have 370
    if (TRversion > 1)
        signed 16 little soundMap[370];
    else
        signed 16 little soundMap[256];

    unsigned 32 little numSoundDetails;
    sound_details_t soundDetails[numSoundDetails];

    // TR1 has the sample data embedded here (WAV files)
    if (TRversion == 1) {
        unsigned 32 little numWAVSamples;
        skip wavSamples[numWAVSamples];
    }

    unsigned 32 little numSampleIndices;
    unsigned 32 little sampleIndices[numSampleIndices];
}