#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "coduo_x87emu.h" /* defines EMULATE_X87; x87 shim when it is 1 */
#include "../core_cvar/cvar_private.h"
#include "../core_memory/core_memory_private.h"
#include "../core_runtime/core_runtime_private.h"
#include "../filesystem/fs_private.h"
#include "../physics_collision/cm_collision_globals_private.h"
#include "../physics_collision/cm_patch_private.h"
#include "../physics_collision/cm_terrain_private.h"
#include "../physics_collision/cm_world_sector_private.h"
#include "coduo_engine_structs.h"

#ifdef CODUO_COLLISION_DIGEST
/* Scope-harness collision digest + per-patch parity dump + load timing
 * (cm_collision_digest.c). No effect unless the CODUO_COLLISION_DIGEST build
 * macro is defined and the cd_collisionDigest / cd_collisionParity /
 * cd_loadTiming cvar is set. */
void coduo_engine_emit_collision_digest(const char *mapName);
void coduo_engine_emit_collision_parity(const char *mapName);
void coduo_engine_emit_capsule_patch_digest(const char *mapName);
void coduo_engine_emit_core_math_digest(void);
void coduo_engine_collision_load_timing_begin(void);
void coduo_engine_collision_load_timing_end(const char *mapName);
#endif

enum {
    CM_BSP_VERSION = 59,
    CM_BSP_HEADER_SIZE = 272,
    CM_BSP_LUMP_COUNT = 33,
    CM_BSP_MAX_MAP_MODELS = 512,
    CM_BSP_MAX_GRID_POINTS = 65536,
    CM_BSP_MAX_TRIANGLE_INDICES = 393216,
    CM_BSP_NO_VIS_ALIGN = 32,
    CM_BSP_VIS_HEADER_SIZE = 8,
    CM_BSP_BRUSH_AXIAL_SIDE_COUNT = 6,
    CM_BSP_WORLD_MODEL_INDEX = 0,
    CM_BSP_MAP_NAME_SIZE = 64,
    CM_BSP_HEADER_IDENT_WORD = 0,
    CM_BSP_HEADER_VERSION_WORD = 1,
    CM_BSP_LUMP_HEADER_WORDS = 2,
    CM_BSP_LUMP_FILE_LENGTH_WORD = 0,
    CM_BSP_LUMP_FILE_OFFSET_WORD = 1
};

#define CM_BSP_BRUSH_CONTENTS_MASK 0xdfff7ffbU

typedef enum cm_bsp_lump_index_e {
    CM_BSP_LUMP_MATERIALS = 0,
    CM_BSP_LUMP_PLANES = 2,
    CM_BSP_LUMP_BRUSHSIDES = 3,
    CM_BSP_LUMP_BRUSHES = 4,
    CM_BSP_LUMP_NODES = 20,
    CM_BSP_LUMP_LEAFS = 21,
    CM_BSP_LUMP_LEAFBRUSHES = 22,
    CM_BSP_LUMP_LEAFSURFACES = 23,
    CM_BSP_LUMP_TERRAIN_PATCHES = 24,
    CM_BSP_LUMP_TERRAIN_VERTS = 25,
    CM_BSP_LUMP_TERRAIN_INDICES = 26,
    CM_BSP_LUMP_MODELS = 27,
    CM_BSP_LUMP_VISIBILITY = 28,
    CM_BSP_LUMP_ENTITIES = 29
} cm_bsp_lump_index_t;

typedef struct lump_s {
    int32_t filelen;
    int32_t fileofs;
} lump_t;

typedef struct dheader_s {
    int32_t ident;
    int32_t version;
    lump_t lumps[CM_BSP_LUMP_COUNT];
} dheader_t;

typedef struct dbrush_s {
    int16_t numSides;
    int16_t shaderNum;
} dbrush_t;

typedef struct dbrushside_s {
    union {
        float dist;
        int32_t planeNum;
    } plane;
    int32_t shaderNum;
} dbrushside_t;

typedef struct dnode_s {
    int32_t planeNum;
    int32_t children[2];
    int32_t mins[3];
    int32_t maxs[3];
} dnode_t;

typedef struct dleaf_s {
    int32_t cluster;
    int32_t area;
    int32_t firstLeafTerrainPatch;
    int32_t numLeafTerrainPatches;
    int32_t firstLeafBrush;
    int32_t numLeafBrushes;
    int32_t cellNum;
    int32_t firstLightIndex;
    int32_t lightCount;
} dleaf_t;

typedef struct dmodel_s {
    vec3_t mins;
    vec3_t maxs;
    int32_t firstSurface;
    int32_t numSurfaces;
    int32_t firstLeafSurface;
    int32_t numLeafSurfaces;
    int32_t firstLeafBrush;
    int32_t numLeafBrushes;
} dmodel_t;

typedef struct dterrainPatch_s {
    int16_t shaderNum;
    uint8_t collisionMode;
    uint8_t padding03;
    union {
        struct {
            int16_t width;
            int16_t height;
            int32_t maxError;
            uint32_t firstVert;
        } curve;
        struct {
            int16_t numVerts;
            int16_t numIndexes;
            uint32_t firstVert;
            uint32_t firstIndex;
        } terrain;
    } data;
} dterrainPatch_t;

typedef struct dvis_s {
    int32_t numClusters;
    int32_t clusterBytes;
    uint8_t data[];
} dvis_t;

char cm_mapName[CM_BSP_MAP_NAME_SIZE];
dshader_t *cm_materials;
int32_t cm_numMaterials;
cplane_t *cm_planes;
int32_t cm_numPlanes;
coduo_collision_brush_side_t *cm_brushSides;
int32_t cm_numBrushSides;
coduo_collision_brush_t *cm_brushes;
int32_t cm_numBrushes;
coduo_collision_node_t *cm_nodes;
int32_t cm_numNodes;
coduo_collision_leaf_t *cm_leafs;
int32_t cm_numLeafs;
int32_t *cm_leafbrushes;
int32_t cm_numLeafBrushes;
int32_t *cm_leafsurfaces;
int32_t cm_numLeafSurfaces;
coduo_collision_terrain_patch_t *cm_terrainPatches;
int32_t cm_numTerrainPatches;
coduo_collision_model_t *cm_cmodels;
int32_t cm_numSubModels;
int32_t cm_numClusters;
int32_t cm_numAreas;
int32_t cm_clusterBytes;
uint8_t *cm_visibility;
qboolean cm_visibilityLoaded;
char *cm_entityString;
int32_t cm_entityStringLength;
int32_t cm_checkcount;
int32_t cm_mapChecksum;
void *cm_loadedMapLump;
static const uint8_t *cm_loadedBsp;
coduo_collision_area_t *cm_areas;
int32_t *cm_areaPortals;
coduo_collision_brush_t *cm_boxBrush;
coduo_collision_model_t cm_boxModel;
cvar_t *cm_noCurves;
cvar_t *cm_playerCurveClip;

/* NOT_FROM_ORIGINAL_SOURCE: local decoder for endian-normalized BSP headers. */
static void coduomp_cm_read_bsp_header(const int32_t *fileHeader,
                                       dheader_t *header)
{
    /*
     * ORIGINAL_BINARY_BUG: every stock caller reaches this full-header read
     * without proving the file/read supplied 272 bytes. See
     * original-bugs/coduomp-world-bsp-lump-bounds-unvalidated.md.
     */
    header->ident = (int32_t)CM_LittleLong(
        (uint32_t)fileHeader[CM_BSP_HEADER_IDENT_WORD]);
    header->version = (int32_t)CM_LittleLong(
        (uint32_t)fileHeader[CM_BSP_HEADER_VERSION_WORD]);

    for (int32_t lumpIndex = 0; lumpIndex < CM_BSP_LUMP_COUNT; ++lumpIndex) {
        int32_t wordIndex =
            CM_BSP_LUMP_HEADER_WORDS +
            lumpIndex * (int32_t)(sizeof(lump_t) / sizeof(int32_t));
        header->lumps[lumpIndex].filelen =
            (int32_t)CM_LittleLong((uint32_t)
                                      fileHeader[wordIndex +
                                                 CM_BSP_LUMP_FILE_LENGTH_WORD]);
        header->lumps[lumpIndex].fileofs =
            (int32_t)CM_LittleLong((uint32_t)
                                      fileHeader[wordIndex +
                                                 CM_BSP_LUMP_FILE_OFFSET_WORD]);
    }
}

/* NOT_FROM_ORIGINAL_SOURCE: typed access to a lump inside the loaded BSP. */
static const uint8_t *coduomp_cm_lump_data(const lump_t *lump)
{
    /*
     * ORIGINAL_BINARY_BUG: stock adds the authored offset without checking
     * the resulting range against the loaded file. See
     * original-bugs/coduomp-world-bsp-lump-bounds-unvalidated.md.
     */
    return cm_loadedBsp + lump->fileofs;
}

/* NOT_FROM_ORIGINAL_SOURCE:
 * Explicit reset for globals that the original
 * binary cleared as one contiguous cm memory block.
 */
static void coduomp_cm_clear_map_state(void)
{
    Com_Memset(cm_mapName, 0, sizeof(cm_mapName));
    cm_materials = NULL;
    cm_numMaterials = 0;
    cm_planes = NULL;
    cm_numPlanes = 0;
    cm_brushSides = NULL;
    cm_numBrushSides = 0;
    cm_brushes = NULL;
    cm_numBrushes = 0;
    cm_nodes = NULL;
    cm_numNodes = 0;
    cm_leafs = NULL;
    cm_numLeafs = 0;
    cm_leafbrushes = NULL;
    cm_numLeafBrushes = 0;
    cm_leafsurfaces = NULL;
    cm_numLeafSurfaces = 0;
    cm_terrainPatches = NULL;
    cm_numTerrainPatches = 0;
    cm_cmodels = NULL;
    cm_numSubModels = 0;
    cm_numClusters = 0;
    cm_numAreas = 0;
    cm_clusterBytes = 0;
    cm_visibility = NULL;
    cm_visibilityLoaded = qfalse;
    cm_entityString = NULL;
    cm_entityStringLength = 0;
    cm_checkcount = 0;
    cm_areas = NULL;
    cm_areaPortals = NULL;
    cm_floodValid = 0;
    cm_boxBrush = NULL;
    Com_Memset(&cm_boxModel, 0, sizeof(cm_boxModel));
    cm_staticModelCount = 0;
    cm_staticModels = NULL;
    Com_Memset(cm_worldMins, 0, sizeof(cm_worldMins));
    Com_Memset(cm_worldMaxs, 0, sizeof(cm_worldMaxs));
    cm_freeWorldSectors = NULL;
    Com_Memset(&cm_worldSectorRoot, 0, sizeof(cm_worldSectorRoot));
    Com_Memset(&cm_nullWorldSector, 0, sizeof(cm_nullWorldSector));
    Com_Memset(cm_worldSectorPool, 0, sizeof(cm_worldSectorPool));
}

const char *Com_BuildVersionString(void)
{
    static char version[64];

    sprintf(version, "%d %s %s", 1466, "Feb 10 2005", "15:43:53");
    return version;
}

void CM_LoadMaterials(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const void *materialData = coduomp_cm_lump_data(lump);

    if ((fileLength % (uint32_t)sizeof(dshader_t)) !=
        0U) {
        Com_Error(1, "\x15" "CMod_LoadShaders: funny lump size");
    }

    int32_t materialCount = (int32_t)(
        fileLength / (uint32_t)sizeof(dshader_t));
    if (materialCount <= 0) {
        Com_Error(1, "\x15" "Map with no shaders");
    }

    uint32_t materialBytes =
        (uint32_t)materialCount *
        (uint32_t)sizeof(dshader_t);
    cm_materials = Hunk_Alloc(
        (size_t)(materialBytes +
                 (uint32_t)sizeof(dshader_t)));
    ++cm_materials;
    cm_numMaterials = materialCount;
    Com_Memcpy(cm_materials, materialData, (size_t)materialBytes);

    if (CM_LittleLong(1) != 1) {
        for (int32_t materialIndex = 0; materialIndex < materialCount;
             ++materialIndex) {
            cm_materials[materialIndex].contentFlags =
                (int32_t)CM_LittleLong(
                    (uint32_t)cm_materials[materialIndex].contentFlags);
            cm_materials[materialIndex].surfaceFlags =
                (int32_t)CM_LittleLong(
                    (uint32_t)cm_materials[materialIndex].surfaceFlags);
        }
    }
}

void CM_LoadPlanes(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const float *diskPlane = (const float *)coduomp_cm_lump_data(lump);

    if ((fileLength & 0x0fU) != 0U) {
        Com_Error(1, "\x15" "MOD_LoadBmodel: funny lump size");
    }

    int32_t numPlanes = (int32_t)(fileLength >> 4);
    if (numPlanes <= 0) {
        Com_Error(1, "\x15" "Map with no planes");
    }

    cm_planes = Hunk_Alloc((size_t)numPlanes * sizeof(cm_planes[0]));
    cm_numPlanes = numPlanes;
    cplane_t *planes = cm_planes;

    for (int32_t planeIndex = 0; planeIndex < numPlanes; ++planeIndex) {
        uint8_t signbits = 0;
        cplane_t *plane = &planes[planeIndex];

        for (int32_t axis = 0; axis < 3; ++axis) {
            plane->normal[axis] = (float)CM_LittleFloat(diskPlane[axis]);
            if (plane->normal[axis] < 0.0f) {
                signbits |= (uint8_t)(1U << axis);
            }
        }

        plane->dist = (float)CM_LittleFloat(diskPlane[3]);
        if (plane->normal[0] == 1.0f) {
            plane->type = 0;
        } else if (plane->normal[1] == 1.0f) {
            plane->type = 1;
        } else if (plane->normal[2] == 1.0f) {
            plane->type = 2;
        } else {
            plane->type = 3;
        }
        plane->signbits = signbits;

        diskPlane += 4;
    }
}

void CM_LoadBrushes(const lump_t *brushLump,
                    const lump_t *brushSideLump)
{
    uint32_t brushLength = (uint32_t)brushLump->filelen;
    uint32_t brushSideLength = (uint32_t)brushSideLump->filelen;
    const dbrush_t *diskBrush =
        (const dbrush_t *)coduomp_cm_lump_data(brushLump);

    if ((brushLength & 3U) != 0U) {
        Com_Error(1, "\x15" "CMod_LoadBrushes: funny lump size");
    }
    int32_t numBrushes = (int32_t)(brushLength >> 2);
    const dbrushside_t *diskSide =
        (const dbrushside_t *)coduomp_cm_lump_data(brushSideLump);

    if ((brushSideLength & 7U) != 0U) {
        Com_Error(1, "\x15" "CMod_LoadBrushes: funny lump size");
    }

    uint32_t brushSideCountRaw =
        (brushSideLength >> 3) -
        (uint32_t)numBrushes * CM_BSP_BRUSH_AXIAL_SIDE_COUNT;
    int32_t numBrushSides = (int32_t)brushSideCountRaw;
    if (numBrushSides < 0) {
        Com_Error(1, "\x15" "CMod_LoadBrushes: bad side count");
    }

    cm_brushSides =
        numBrushSides == 0
            ? NULL
            : Hunk_Alloc((size_t)((uint32_t)numBrushSides *
                                  (uint32_t)sizeof(cm_brushSides[0])));
    cm_numBrushSides = numBrushSides;
    coduo_collision_brush_side_t *runtimeSide = cm_brushSides;

    uint32_t brushBytes =
        (uint32_t)numBrushes * (uint32_t)sizeof(cm_brushes[0]);
    cm_brushes = Hunk_Alloc(
        (size_t)(brushBytes + (uint32_t)sizeof(cm_brushes[0])));
    cm_numBrushes = numBrushes;

    for (int32_t brushIndex = 0; brushIndex < numBrushes; ++brushIndex) {
        coduo_collision_brush_t *brush = &cm_brushes[brushIndex];
        int32_t sideCount = CM_LittleShort(diskBrush->numSides) -
                            CM_BSP_BRUSH_AXIAL_SIDE_COUNT;
        if (sideCount < 0) {
            Com_Error(1,
                      "\x15" "CMod_LoadBrushes: brush has less than 6 sides");
        }

        brush->numSides = sideCount;
        brush->sides = sideCount == 0 ? NULL : runtimeSide;

        for (int32_t axis = 0; axis < 3; ++axis) {
            for (int32_t side = 0; side < 2; ++side) {
                float distance = (float)CM_LittleFloat(diskSide->plane.dist);
                if (side == 0) {
                    brush->mins[axis] = distance;
                } else {
                    brush->maxs[axis] = distance;
                }
                int32_t materialIndex =
                    (int32_t)CM_LittleLong((uint32_t)diskSide->shaderNum);
                if (materialIndex < 0 || cm_numMaterials <= materialIndex) {
                    Com_Error(1, "\x15" "CMod_LoadBrushes: bad shaderNum: %i",
                              materialIndex);
                }
                brush->axialMaterialIndices[side * 3 + axis] =
                    (int16_t)materialIndex;
                if (brush->axialMaterialIndices[side * 3 + axis] !=
                    materialIndex) {
                    Com_Error(1,
                              "\x15" "CMod_LoadBrushes: axialShaderNum exceeded");
                }
                diskSide++;
            }
        }

        for (int32_t sideIndex = 0; sideIndex < sideCount; ++sideIndex) {
            int32_t planeIndex =
                (int32_t)CM_LittleLong((uint32_t)diskSide->plane.planeNum);
            int32_t materialIndex =
                (int32_t)CM_LittleLong((uint32_t)diskSide->shaderNum);
            /*
             * ORIGINAL_BINARY_BUG: stock does not range-check the authored
             * plane index or each brush's independent side span. See
             * original-bugs/coduomp-collision-bsp-cross-reference-bounds.md.
             */
            runtimeSide->plane = &cm_planes[planeIndex];
            runtimeSide->materialIndex = materialIndex;
            if (materialIndex < 0 || cm_numMaterials <= materialIndex) {
                Com_Error(1, "\x15" "CMod_LoadBrushes: bad shaderNum: %i",
                          materialIndex);
            }
            diskSide++;
            runtimeSide++;
        }

        int32_t materialIndex = CM_LittleShort(diskBrush->shaderNum);
        if (materialIndex < 0 || cm_numMaterials <= materialIndex) {
            Com_Error(1, "\x15" "CMod_LoadBrushes: bad shaderNum: %i",
                      materialIndex);
        }
        brush->contents =
            cm_materials[materialIndex].contentFlags &
            CM_BSP_BRUSH_CONTENTS_MASK;
        diskBrush++;
    }
}

void CM_LoadNodes(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const dnode_t *diskNode =
        (const dnode_t *)coduomp_cm_lump_data(lump);

    if ((fileLength % (uint32_t)sizeof(dnode_t)) != 0U) {
        Com_Error(1, "\x15" "MOD_LoadBmodel: funny lump size");
    }

    int32_t nodeCount =
        (int32_t)(fileLength / (uint32_t)sizeof(dnode_t));
    if (nodeCount <= 0) {
        Com_Error(1, "\x15" "Map has no nodes");
    }

    cm_nodes = Hunk_Alloc((size_t)nodeCount * sizeof(cm_nodes[0]));
    cm_numNodes = nodeCount;

    for (int32_t nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) {
        int32_t planeIndex =
            (int32_t)CM_LittleLong((uint32_t)diskNode->planeNum);
        /*
         * ORIGINAL_BINARY_BUG: stock converts the plane and child references
         * without proving they select loaded planes, nodes, or leafs. See
         * original-bugs/coduomp-collision-bsp-cross-reference-bounds.md.
         */
        cm_nodes[nodeIndex].plane = &cm_planes[planeIndex];

        for (int32_t childIndex = 0; childIndex < 2; ++childIndex) {
            int32_t child =
                (int32_t)CM_LittleLong((uint32_t)diskNode->children[childIndex]);
            cm_nodes[nodeIndex].children[childIndex] = (int16_t)child;
            if (cm_nodes[nodeIndex].children[childIndex] != child) {
                Com_Error(1, "\x15" "CMod_LoadNodes: children exceeded");
            }
        }

        diskNode++;
    }
}

void CM_LoadLeafs(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const dleaf_t *diskLeaf =
        (const dleaf_t *)coduomp_cm_lump_data(lump);

    if ((fileLength % (uint32_t)sizeof(dleaf_t)) != 0U) {
        Com_Error(1, "\x15" "CMod_LoadLeafs: funny lump size");
    }

    int32_t leafCount =
        (int32_t)(fileLength / (uint32_t)sizeof(dleaf_t));
    if (leafCount == 0) {
        Com_Error(1, "\x15" "Map with no leafs");
    }

    cm_leafs = Hunk_Alloc((size_t)leafCount * sizeof(cm_leafs[0]));
    cm_numLeafs = leafCount;

    for (int32_t leafIndex = 0; leafIndex < leafCount; ++leafIndex) {
        coduo_collision_leaf_t *leaf = &cm_leafs[leafIndex];
        int32_t cluster =
            (int32_t)CM_LittleLong((uint32_t)diskLeaf->cluster);
        int32_t area = (int32_t)CM_LittleLong((uint32_t)diskLeaf->area);
        uint32_t leafBrushCount =
            CM_LittleLong((uint32_t)diskLeaf->numLeafBrushes);
        uint32_t firstLeafTerrain =
            CM_LittleLong((uint32_t)diskLeaf->firstLeafTerrainPatch);
        uint32_t leafTerrainCount =
            CM_LittleLong((uint32_t)diskLeaf->numLeafTerrainPatches);
        int32_t cellnum = diskLeaf->cellNum;

        leaf->cellnum = (int16_t)cellnum;
        if (leaf->cellnum != cellnum) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: cellnum exceeded");
        }
        leaf->cluster = (int16_t)cluster;
        if (leaf->cluster != cluster) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: cluster exceeded");
        }
        leaf->area = (int16_t)area;
        if (leaf->area != area) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: area exceeded");
        }
        /*
         * ORIGINAL_BINARY_BUG: the narrowed counts are checked for width,
         * but their authored first/count spans are not checked against the
         * leaf-brush or terrain-patch arrays. See
         * original-bugs/coduomp-collision-bsp-cross-reference-bounds.md.
         */
        leaf->firstLeafBrush =
            (int32_t)CM_LittleLong((uint32_t)diskLeaf->firstLeafBrush);
        leaf->numLeafBrushes = (uint16_t)leafBrushCount;
        if (leaf->numLeafBrushes != leafBrushCount) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: numLeafBrushes exceeded");
        }
        leaf->firstLeafTerrainPatch = (uint16_t)firstLeafTerrain;
        if (leaf->firstLeafTerrainPatch != firstLeafTerrain) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: firstLeafSurface exceeded");
        }
        leaf->numLeafTerrainPatches = (uint16_t)leafTerrainCount;
        if (leaf->numLeafTerrainPatches != leafTerrainCount) {
            Com_Error(1, "\x15" "CMod_LoadLeafs: numLeafSurfaces exceeded");
        }

        if (cm_numClusters <= cluster) {
            cm_numClusters = cluster + 1;
        }
        if (cm_numAreas <= area) {
            cm_numAreas = area + 1;
        }

        diskLeaf++;
    }

    uint32_t areaBytes =
        (uint32_t)cm_numAreas * (uint32_t)sizeof(cm_areas[0]);
    cm_areas = Hunk_Alloc((size_t)areaBytes);
    /*
     * ORIGINAL_BINARY_BUG: for the representable maximum authored area,
     * stock's dword numAreas-squared byte count wraps to zero; the immediate
     * flood still traverses the conceptual matrix. See
     * original-bugs/coduomp-collision-area-portal-allocation-overflow.md.
     */
    uint32_t areaPortalBytes =
        (uint32_t)cm_numAreas * (uint32_t)cm_numAreas *
        (uint32_t)sizeof(cm_areaPortals[0]);
    cm_areaPortals = Hunk_Alloc((size_t)areaPortalBytes);
}

void CM_LoadLeafBrushes(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const int32_t *diskLeafBrush =
        (const int32_t *)coduomp_cm_lump_data(lump);

    if ((fileLength & 3U) != 0U) {
        Com_Error(1, "\x15" "MOD_LoadBmodel: funny lump size");
    }

    int32_t leafBrushCount = (int32_t)(fileLength >> 2);
    uint32_t leafBrushBytes =
        ((uint32_t)leafBrushCount + 1U) *
        (uint32_t)sizeof(cm_leafbrushes[0]);
    cm_leafbrushes = Hunk_Alloc((size_t)leafBrushBytes);
    cm_numLeafBrushes = leafBrushCount;

    int32_t *leafBrush = cm_leafbrushes;
    for (int32_t index = 0; index < leafBrushCount; ++index) {
        *leafBrush = (int32_t)CM_LittleLong((uint32_t)*diskLeafBrush);
        diskLeafBrush++;
        leafBrush++;
    }
}

void CM_LoadLeafSurfaces(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    const int32_t *diskLeafSurface =
        (const int32_t *)coduomp_cm_lump_data(lump);

    if ((fileLength & 3U) != 0U) {
        Com_Error(1, "\x15" "CMod_LoadLeafSurfaces: funny lump size");
    }

    int32_t leafSurfaceCount = (int32_t)(fileLength >> 2);
    cm_leafsurfaces =
        Hunk_Alloc((size_t)leafSurfaceCount * sizeof(cm_leafsurfaces[0]));
    cm_numLeafSurfaces = leafSurfaceCount;

    int32_t *leafSurface = cm_leafsurfaces;
    for (int32_t index = 0; index < leafSurfaceCount; ++index) {
        *leafSurface = (int32_t)CM_LittleLong((uint32_t)*diskLeafSurface);
        diskLeafSurface++;
        leafSurface++;
    }
}

void CM_LoadTerrainPatches(const lump_t *patchLump,
                           const lump_t *vertexLump,
                           const lump_t *indexLump)
{
    uint32_t patchLength = (uint32_t)patchLump->filelen;
    uint32_t vertexLength = (uint32_t)vertexLump->filelen;
    uint32_t indexLength = (uint32_t)indexLump->filelen;

    if ((patchLength & 0x0fU) != 0U) {
        Com_Error(1,
                  "\x15" "CMod_LoadLeafpatchesAndTerrain: funny lump size");
    }
    if ((vertexLength % (uint32_t)sizeof(vec3_t)) != 0U) {
        Com_Error(1,
                  "\x15" "CMod_LoadLeafpatchesAndTerrain: funny lump size");
    }
    if ((indexLength & 1U) != 0U) {
        Com_Error(1,
                  "\x15" "CMod_LoadLeafpatchesAndTerrain: funny lump size");
    }

    cm_numTerrainPatches = (int32_t)(patchLength >> 4);
    cm_terrainPatches =
        Hunk_Alloc((size_t)cm_numTerrainPatches * sizeof(cm_terrainPatches[0]));

    const dterrainPatch_t *diskPatch =
        (const dterrainPatch_t *)coduomp_cm_lump_data(patchLump);
    const float *diskVertex =
        (const float *)coduomp_cm_lump_data(vertexLump);
    const int16_t *diskIndexBase =
        (const int16_t *)coduomp_cm_lump_data(indexLump);

    vec3_t vertices[CM_BSP_MAX_GRID_POINTS];
    int16_t indices[CM_BSP_MAX_TRIANGLE_INDICES];

    for (int32_t patchIndex = 0; patchIndex < cm_numTerrainPatches;
         ++patchIndex) {
        coduo_collision_terrain_patch_t *patch = &cm_terrainPatches[patchIndex];
        int16_t materialIndex =
            (int16_t)CM_LittleShort(diskPatch->shaderNum);

        /*
         * ORIGINAL_BINARY_BUG: stock trusts the authored material index and
         * the vertex/index first/count spans after only record-size and local
         * scratch-capacity checks. See
         * original-bugs/coduomp-collision-bsp-cross-reference-bounds.md.
         */
        patch->checkcount = 0;
        patch->materialIndex = materialIndex;
        patch->contents = cm_materials[materialIndex].contentFlags;

        if (diskPatch->collisionMode == 0) {
            int16_t width =
                (int16_t)CM_LittleShort(diskPatch->data.curve.width);
            int16_t height =
                (int16_t)CM_LittleShort(diskPatch->data.curve.height);
            uint32_t pointCount = (uint32_t)((int32_t)width * (int32_t)height);
            if (CM_BSP_MAX_GRID_POINTS < pointCount) {
                Com_Error(1,
                          "\x15" "CMod_LoadLeafpatchesAndTerrain: more than %i verts in curve\n",
                          CM_BSP_MAX_GRID_POINTS);
            }

            uint32_t vertexOffset =
                CM_LittleLong(diskPatch->data.curve.firstVert);
            const float *gridVertex = &diskVertex[vertexOffset * 3U];

            for (uint32_t pointIndex = 0; pointIndex < pointCount;
                 ++pointIndex) {
                vertices[pointIndex][0] = (float)CM_LittleFloat(*gridVertex++);
                vertices[pointIndex][1] = (float)CM_LittleFloat(*gridVertex++);
                vertices[pointIndex][2] = (float)CM_LittleFloat(*gridVertex++);
            }

            int32_t maxError =
                (int32_t)CM_LittleLong(
                    (uint32_t)diskPatch->data.curve.maxError);

            patch->terrainCollide = CM_GeneratePatchCollide(
                (uint32_t)width, (uint32_t)height, maxError,
                (const vec3_t *)vertices, &patch->mins);
            patch->patchCollide = NULL;
        } else {
            int16_t vertexCount =
                (int16_t)CM_LittleShort(diskPatch->data.terrain.numVerts);
            if (CM_BSP_MAX_GRID_POINTS < (uint32_t)vertexCount) {
                Com_Error(1,
                          "\x15" "CMod_LoadLeafpatchesAndTerrain: more than %i verts in terrain\n",
                          CM_BSP_MAX_GRID_POINTS);
            }

            uint32_t vertexOffset =
                CM_LittleLong(diskPatch->data.terrain.firstVert);
            const float *terrainVertex = &diskVertex[vertexOffset * 3U];
            for (int32_t vertexIndex = 0; vertexIndex < vertexCount;
                 ++vertexIndex) {
                vertices[vertexIndex][0] = (float)CM_LittleFloat(*terrainVertex++);
                vertices[vertexIndex][1] = (float)CM_LittleFloat(*terrainVertex++);
                vertices[vertexIndex][2] = (float)CM_LittleFloat(*terrainVertex++);
            }

            int16_t indexCount =
                (int16_t)CM_LittleShort(diskPatch->data.terrain.numIndexes);
            if (CM_BSP_MAX_TRIANGLE_INDICES < (uint32_t)indexCount) {
                Com_Error(1,
                          "\x15" "CMod_LoadLeafpatchesAndTerrain: more than %i indexes in terrain\n",
                          CM_BSP_MAX_TRIANGLE_INDICES);
            }

            uint32_t indexOffset =
                CM_LittleLong(diskPatch->data.terrain.firstIndex);
            const int16_t *terrainIndex = &diskIndexBase[indexOffset];
            for (int32_t index = 0; index < indexCount; ++index) {
                indices[index] = (int16_t)CM_LittleShort(*terrainIndex++);
            }

            patch->terrainCollide = NULL;
            patch->patchCollide =
                CM_GenerateTerrainCollide(indexCount, indices,
                                          (uint32_t)vertexCount,
                                          (const vec3_t *)vertices,
                                          &patch->mins);
        }

        if ((cm_numTerrainPatches & 3) == 3) {
            Sys_LoadingKeepAlive();
        }
        diskPatch++;
    }
}

void CM_LoadSubmodels(const lump_t *lump)
{
    uint32_t fileLength = (uint32_t)lump->filelen;
    int32_t modelCount;

    if ((fileLength % (uint32_t)sizeof(dmodel_t)) != 0U) {
        Com_Error(1, "\x15" "CMod_LoadSubmodels: funny lump size");
    }

    modelCount = (int32_t)(fileLength / (uint32_t)sizeof(dmodel_t));
    if (modelCount == 0) {
        Com_Error(1, "\x15" "Map with no models");
    }

    cm_cmodels = Hunk_Alloc((size_t)modelCount * sizeof(cm_cmodels[0]));
    cm_numSubModels = modelCount;
    if (CM_BSP_MAX_MAP_MODELS < modelCount) {
        Com_Error(1, "\x15" "MAX_SUBMODELS exceeded");
    }

    const dmodel_t *diskModel =
        (const dmodel_t *)coduomp_cm_lump_data(lump);

    for (int32_t modelIndex = 0; modelIndex < modelCount; ++modelIndex) {
        coduo_collision_model_t *model = &cm_cmodels[modelIndex];

        for (int32_t axis = 0; axis < 3; ++axis) {
            /* mins-1.0 / maxs+1.0 with a double 1.0, kept 80-bit, one float
             * store (bit-identical model bounds) -> shim. */
#if EMULATE_X87
            model->mins[axis] = x87f_store_f32(x87f_sub(
                x87f_load_f32(CM_LittleFloat(diskModel->mins[axis])),
                x87f_load_f64(1.0)));
            model->maxs[axis] = x87f_store_f32(x87f_add(
                x87f_load_f32(CM_LittleFloat(diskModel->maxs[axis])),
                x87f_load_f64(1.0)));
#else
            model->mins[axis] =
                (float)((long double)CM_LittleFloat(diskModel->mins[axis]) -
                        (long double)1.0);
            model->maxs[axis] =
                (float)((long double)CM_LittleFloat(diskModel->maxs[axis]) +
                        (long double)1.0);
#endif
        }

        if (modelIndex != CM_BSP_WORLD_MODEL_INDEX) {
            uint32_t leafBrushCount =
                CM_LittleLong((uint32_t)diskModel->numLeafBrushes);
            model->leaf.numLeafBrushes = (uint16_t)leafBrushCount;
            if (model->leaf.numLeafBrushes != leafBrushCount) {
                Com_Error(1,
                          "\x15" "CMod_LoadSubmodels: numLeafBrushes exceeded");
            }

            /*
             * ORIGINAL_BINARY_BUG: stock does not prove that the authored
             * brush or surface spans select their loaded index arrays. See
             * original-bugs/coduomp-collision-bsp-cross-reference-bounds.md.
            */
            uint32_t inlineLeafBrushBytes =
                leafBrushCount * (uint32_t)sizeof(cm_leafbrushes[0]);
            int32_t *inlineLeafBrushes =
                Hunk_Alloc((size_t)inlineLeafBrushBytes);
            model->leaf.firstLeafBrush =
                (int32_t)(inlineLeafBrushes - cm_leafbrushes);
            for (uint32_t brushIndex = 0; brushIndex < leafBrushCount;
                 ++brushIndex) {
                inlineLeafBrushes[brushIndex] =
                    (int32_t)CM_LittleLong(
                        (uint32_t)diskModel->firstLeafBrush) +
                    (int32_t)brushIndex;
            }

            uint32_t leafSurfaceCount =
                CM_LittleLong((uint32_t)diskModel->numLeafSurfaces);
            model->leaf.numLeafTerrainPatches = (uint16_t)leafSurfaceCount;
            if (model->leaf.numLeafTerrainPatches != leafSurfaceCount) {
                Com_Error(1,
                          "\x15" "CMod_LoadSubmodels: numLeafSurfaces exceeded");
            }

            uint32_t firstLeafSurface =
                CM_LittleLong((uint32_t)diskModel->firstLeafSurface);
            model->leaf.firstLeafTerrainPatch = (uint16_t)firstLeafSurface;
            if (model->leaf.firstLeafTerrainPatch != firstLeafSurface) {
                Com_Error(1,
                          "\x15" "CMod_LoadSubmodels: firstLeafSurface exceeded");
            }
        }

        diskModel++;
    }
}

void CM_LoadVisibility(const lump_t *lump)
{
    /*
     * ORIGINAL_BINARY_BUG: a nonempty stock lump need not contain the
     * eight-byte header, and neither header dimension is reconciled with the
     * payload. See
     * original-bugs/coduomp-collision-visibility-lump-validation.md.
     */
    if (lump->filelen == 0) {
        cm_clusterBytes =
            (cm_numClusters + CM_BSP_NO_VIS_ALIGN - 1) &
            ~(CM_BSP_NO_VIS_ALIGN - 1);
        cm_visibility =
            Hunk_Alloc((size_t)(uint32_t)cm_clusterBytes);
        Com_Memset(cm_visibility, 0xff,
                   (size_t)(uint32_t)cm_clusterBytes);
        return;
    }

    const dvis_t *diskVis =
        (const dvis_t *)coduomp_cm_lump_data(lump);
    uint32_t visibilityBytes = (uint32_t)lump->filelen;
    cm_visibilityLoaded = qtrue;
    cm_visibility = Hunk_Alloc((size_t)visibilityBytes);
    cm_numClusters = (int32_t)CM_LittleLong(
        (uint32_t)diskVis->numClusters);
    cm_clusterBytes =
        (int32_t)CM_LittleLong((uint32_t)diskVis->clusterBytes);
    Com_Memcpy(cm_visibility, diskVis->data,
               (size_t)(visibilityBytes - CM_BSP_VIS_HEADER_SIZE));
}

void CM_LoadEntityString(const lump_t *lump)
{
    /*
     * ORIGINAL_BINARY_BUG: stock allocates and copies exactly filelen bytes
     * without appending a parser terminator. See
     * original-bugs/coduomp-world-entity-lump-missing-nul-overflow.md.
     */
    uint32_t entityBytes = (uint32_t)lump->filelen;
    cm_entityString = Hunk_Alloc((size_t)entityBytes);
    cm_entityStringLength = lump->filelen;
    Com_Memcpy(cm_entityString, coduomp_cm_lump_data(lump),
               (size_t)entityBytes);
}

void CM_LoadMap(const char *name, qboolean clientload, int32_t *checksum)
{
    if (name == NULL || name[0] == '\0') {
        Com_Error(1, "\x15" "CM_LoadMap: NULL name");
    }

    char localName[CM_BSP_MAP_NAME_SIZE];
    Q_strncpyz(localName, name, sizeof(localName));

    cm_noCurves = Cvar_Get("cm_noCurves", "0", 0x200);
    cm_playerCurveClip = Cvar_Get("cm_playerCurveClip", "1", 0x201);
    Com_DPrintf("CM_LoadMap( %s, %i )\n", localName, clientload);

    if (clientload != qfalse && sv_running->integer != 0) {
        *checksum = cm_mapChecksum;
        return;
    }

#ifdef CODUO_COLLISION_DIGEST
    coduo_engine_collision_load_timing_begin();
#endif

    coduomp_cm_clear_map_state();

    void *fileBuffer;
    int32_t fileLength = FS_ReadFile(localName, &fileBuffer);
    if (fileBuffer == NULL) {
        Com_Error(1, va("EXE_ERR_COULDNT_LOAD\x15%s", localName));
    }

    uint32_t blockChecksum = Com_BlockChecksum(fileBuffer, fileLength);
    cm_mapChecksum = (int32_t)blockChecksum;
    *checksum = (int32_t)blockChecksum;

    dheader_t header;
    coduomp_cm_read_bsp_header((const int32_t *)fileBuffer, &header);
    if (header.version != CM_BSP_VERSION) {
        Com_Error(1, va("EXE_ERR_WRONG_MAP_VERSION_NUM\x15%s\x15(%i "
                        "\x14" "EXE_ERR_SHOULD_BE\x15 %i)",
                        localName, header.version, CM_BSP_VERSION));
    }

    cm_loadedBsp = fileBuffer;

    CM_LoadMaterials(&header.lumps[CM_BSP_LUMP_MATERIALS]);
    CM_LoadPlanes(&header.lumps[CM_BSP_LUMP_PLANES]);
    CM_LoadBrushes(&header.lumps[CM_BSP_LUMP_BRUSHES],
                   &header.lumps[CM_BSP_LUMP_BRUSHSIDES]);
    CM_LoadNodes(&header.lumps[CM_BSP_LUMP_NODES]);
    CM_LoadLeafs(&header.lumps[CM_BSP_LUMP_LEAFS]);
    CM_LoadLeafBrushes(&header.lumps[CM_BSP_LUMP_LEAFBRUSHES]);
    CM_LoadLeafSurfaces(&header.lumps[CM_BSP_LUMP_LEAFSURFACES]);
    CM_LoadTerrainPatches(&header.lumps[CM_BSP_LUMP_TERRAIN_PATCHES],
                          &header.lumps[CM_BSP_LUMP_TERRAIN_VERTS],
                          &header.lumps[CM_BSP_LUMP_TERRAIN_INDICES]);
    CM_LoadSubmodels(&header.lumps[CM_BSP_LUMP_MODELS]);
    CM_LoadVisibility(&header.lumps[CM_BSP_LUMP_VISIBILITY]);
    CM_LoadEntityString(&header.lumps[CM_BSP_LUMP_ENTITIES]);

    FS_FreeFile(fileBuffer);
    CM_InitBoxHull();
    FloodAreaConnections();
    CM_InitWorldSector();
    CM_LoadStaticModels();

    if (clientload == qfalse) {
        Q_strncpyz(cm_mapName, localName, sizeof(cm_mapName));
    }

#ifdef CODUO_COLLISION_DIGEST
    coduo_engine_collision_load_timing_end(localName);
    coduo_engine_emit_collision_digest(localName);
    coduo_engine_emit_collision_parity(localName);
    coduo_engine_emit_capsule_patch_digest(localName);
    coduo_engine_emit_core_math_digest();
#endif
}

void CM_SaveLump(int32_t lumpIndex, const void *replacementData,
                 int32_t replacementLength, int32_t *checksum)
{
    void *fileBuffer;
    FS_ReadFile(cm_mapName, &fileBuffer);
    if (fileBuffer == NULL) {
        Com_Error(1, va("EXE_ERR_COULDNT_LOAD\x15%s", cm_mapName));
    }

    dheader_t header;
    coduomp_cm_read_bsp_header((const int32_t *)fileBuffer, &header);

    int32_t newLength = CM_BSP_HEADER_SIZE;
    for (int32_t index = 0; index < CM_BSP_LUMP_COUNT; ++index) {
        if (index == lumpIndex) {
            header.lumps[index].filelen = replacementLength;
        }
        newLength += (header.lumps[index].filelen + 3) & ~3;
    }

    /*
     * ORIGINAL_BINARY_BUG: stock rebuilds from unvalidated authored source
     * ranges and uses wrapping dword length arithmetic. See
     * original-bugs/coduomp-world-bsp-lump-bounds-unvalidated.md.
     */
    uint8_t *newFile = Z_Malloc((size_t)(uint32_t)newLength);
    int32_t cursor = CM_BSP_HEADER_SIZE;
    for (int32_t index = 0; index < CM_BSP_LUMP_COUNT; ++index) {
        int32_t lumpLength = header.lumps[index].filelen;

        if (lumpLength != 0) {
            const void *source =
                index == lumpIndex
                    ? replacementData
                    : (const uint8_t *)fileBuffer + header.lumps[index].fileofs;
            Com_Memcpy(&newFile[cursor], source,
                       (size_t)(uint32_t)lumpLength);
        }
        header.lumps[index].fileofs = cursor;
        cursor += (lumpLength + 3) & ~3;
    }

    int32_t *diskHeader = (int32_t *)newFile;
    diskHeader[CM_BSP_HEADER_IDENT_WORD] =
        (int32_t)CM_LittleLong((uint32_t)header.ident);
    diskHeader[CM_BSP_HEADER_VERSION_WORD] =
        (int32_t)CM_LittleLong((uint32_t)header.version);
    for (int32_t index = 0; index < CM_BSP_LUMP_COUNT; ++index) {
        int32_t wordIndex =
            CM_BSP_LUMP_HEADER_WORDS +
            index * (int32_t)(sizeof(lump_t) / sizeof(int32_t));
        diskHeader[wordIndex + CM_BSP_LUMP_FILE_OFFSET_WORD] =
            (int32_t)CM_LittleLong((uint32_t)header.lumps[index].fileofs);
        diskHeader[wordIndex + CM_BSP_LUMP_FILE_LENGTH_WORD] =
            (int32_t)CM_LittleLong((uint32_t)header.lumps[index].filelen);
    }

    qboolean restoredPermissions = FS_MakeReadOnly(cm_mapName, 0);
    FS_WriteFile(cm_mapName, newFile, cursor);
    if (restoredPermissions != qfalse) {
        FS_MakeReadOnly(cm_mapName, 1);
    }
    if (checksum != NULL) {
        *checksum = (int32_t)Com_BlockChecksum(newFile, cursor);
    }

    Z_Free(newFile);
    FS_FreeFile(fileBuffer);
}

int32_t CM_LoadMapLump(int32_t lumpIndex, void **buffer)
{
    int32_t handle;
    int32_t length = FS_FOpenFileRead(cm_mapName, &handle, 0);
    if (handle == 0) {
        Com_Error(1, va("EXE_ERR_COULDNT_LOAD\x15%s", cm_mapName));
    }

    dheader_t diskHeader;
    dheader_t header;
    FS_Read(&diskHeader, CM_BSP_HEADER_SIZE, handle);
    coduomp_cm_read_bsp_header((const int32_t *)&diskHeader, &header);

    /*
     * ORIGINAL_BINARY_BUG: stock indexes this stack header with the unchecked
     * caller-supplied lump number and trusts the selected offset/length. See
     * original-bugs/coduomp-world-bsp-lump-bounds-unvalidated.md.
     */
    int32_t lumpLength = header.lumps[lumpIndex].filelen;
    if (lumpLength == 0) {
        FS_FCloseFile(handle);
        return 0;
    }

    int32_t seekOffset =
        header.lumps[lumpIndex].fileofs - CM_BSP_HEADER_SIZE;
    FS_Seek(handle, seekOffset, 0);
    cm_loadedMapLump = Hunk_AllocateTempMemory((size_t)(uint32_t)lumpLength);
    FS_Read(cm_loadedMapLump, lumpLength, handle);
    FS_FCloseFile(handle);
    *buffer = cm_loadedMapLump;
    (void)length;

    return lumpLength;
}

void CM_FreeMapLump(void *buffer)
{
    (void)buffer;
    Hunk_FreeTempMemory(cm_loadedMapLump);
}
