ericw-tools/common/bspfile.cc

4010 lines
146 KiB
C++

/* Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#include <common/cmdlib.hh>
#include <common/mathlib.hh>
#include <common/bspfile.hh>
#include <cstdint>
#include <fmt/format.h>
struct gamedef_generic_t : public gamedef_t
{
gamedef_generic_t() :
gamedef_t("")
{
id = GAME_UNKNOWN;
}
bool surf_is_lightmapped(const surfflags_t &) const { throw std::bad_cast(); }
bool surf_is_subdivided(const surfflags_t &) const { throw std::bad_cast(); }
contentflags_t cluster_contents(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); }
int32_t get_content_type(const contentflags_t &) const { throw std::bad_cast(); }
int32_t contents_priority(const contentflags_t &) const { throw std::bad_cast(); }
contentflags_t create_empty_contents(const int32_t &) const { throw std::bad_cast(); }
contentflags_t create_solid_contents(const int32_t &) const { throw std::bad_cast(); }
contentflags_t create_sky_contents(const int32_t &) const { throw std::bad_cast(); }
contentflags_t create_liquid_contents(const int32_t &, const int32_t &) const { throw std::bad_cast(); }
bool contents_are_liquid(const contentflags_t &) const { throw std::bad_cast(); }
bool contents_are_valid(const contentflags_t &, bool) const { throw std::bad_cast(); }
bool portal_can_see_through(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); }
std::string get_contents_display(const contentflags_t &contents) const { throw std::bad_cast(); }
const std::initializer_list<qboundsd> &get_hull_sizes() const { throw std::bad_cast(); }
};
template<gameid_t id>
struct gamedef_q1_like_t : public gamedef_t
{
gamedef_q1_like_t(const char *base_dir = "ID1") :
gamedef_t(base_dir)
{
this->id = id;
has_rgb_lightmap = false;
}
bool surf_is_lightmapped(const surfflags_t &flags) const { return !(flags.native & TEX_SPECIAL); }
bool surf_is_subdivided(const surfflags_t &flags) const { return !(flags.native & TEX_SPECIAL); }
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const
{
if (contents0 == contents1)
return contents0;
const int32_t merged_flags = contents0.extended | contents1.extended;
/*
* Clusters may be partially solid but still be seen into
* ?? - Should we do something more explicit with mixed liquid contents?
*/
if (contents0.native == CONTENTS_EMPTY || contents1.native == CONTENTS_EMPTY)
return create_empty_contents(merged_flags);
if (contents0.native >= CONTENTS_LAVA && contents0.native <= CONTENTS_WATER)
return create_liquid_contents(contents0.native, merged_flags);
if (contents1.native >= CONTENTS_LAVA && contents1.native <= CONTENTS_WATER)
return create_liquid_contents(contents1.native, merged_flags);
if (contents0.native == CONTENTS_SKY || contents1.native == CONTENTS_SKY)
return create_sky_contents(merged_flags);
return create_solid_contents(merged_flags);
}
int32_t get_content_type(const contentflags_t &contents) const { return contents.native; }
int32_t contents_priority(const contentflags_t &contents) const
{
if (contents.extended & CFLAGS_DETAIL) {
return 5;
} else if (contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
return 3;
} else if (contents.extended & CFLAGS_DETAIL_FENCE) {
return 4;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else
switch (contents.native) {
case CONTENTS_SOLID: return 7;
case CONTENTS_SKY: return 6;
case CONTENTS_WATER: return 2;
case CONTENTS_SLIME: return 2;
case CONTENTS_LAVA: return 2;
case CONTENTS_EMPTY: return 1;
case 0: return 0;
default: FError("Bad contents in face"); return 0;
}
}
contentflags_t create_empty_contents(const int32_t &cflags) const { return {CONTENTS_EMPTY, cflags}; }
contentflags_t create_solid_contents(const int32_t &cflags) const { return {CONTENTS_SOLID, cflags}; }
contentflags_t create_sky_contents(const int32_t &cflags) const { return {CONTENTS_SKY, cflags}; }
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags) const
{
return {liquid_type, cflags};
}
bool contents_are_liquid(const contentflags_t &contents) const
{
return contents.native <= CONTENTS_WATER && contents.native >= CONTENTS_LAVA;
}
bool contents_are_valid(const contentflags_t &contents, bool strict) const { return contents.native <= 0; }
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const
{
/* If contents values are the same and not solid, can see through */
return !(contents0.is_structural_solid(this) || contents1.is_structural_solid(this)) && contents0 == contents1;
}
std::string get_contents_display(const contentflags_t &contents) const
{
switch (contents.native) {
case 0: return "UNSET";
case CONTENTS_EMPTY: return "EMPTY";
case CONTENTS_SOLID: return "SOLID";
case CONTENTS_SKY: return "SKY";
case CONTENTS_WATER: return "WATER";
case CONTENTS_SLIME: return "SLIME";
case CONTENTS_LAVA: return "LAVA";
default: return fmt::to_string(contents.native);
}
}
const std::initializer_list<qboundsd> &get_hull_sizes() const
{
static constexpr std::initializer_list<qboundsd> hulls = {
{{0, 0, 0}, {0, 0, 0}},
{{-16, -16, -32}, {16, 16, 24}},
{{-32, -32, -64}, {32, 32, 24}}
};
return hulls;
}
};
struct gamedef_h2_t : public gamedef_q1_like_t<GAME_HEXEN_II>
{
gamedef_h2_t() : gamedef_q1_like_t("DATA1") { }
const std::initializer_list<qboundsd> &get_hull_sizes() const
{
static constexpr std::initializer_list<qboundsd> hulls = {
{{0, 0, 0}, {0, 0, 0}},
{{-16, -16, -32}, {16, 16, 24}},
{{-24, -24, -20}, {24, 24, 20}},
{{-16, -16, -12}, {16, 16, 16}},
{{-8, -8, -8}, {8, 8, 8}}, // {{-40, -40, -42}, {40, 40, 42}} = original game
{{-48, -48, -50}, {48, 48, 50}}
};
return hulls;
}
};
struct gamedef_hl_t : public gamedef_q1_like_t<GAME_HALF_LIFE>
{
gamedef_hl_t() :
gamedef_q1_like_t("VALVE")
{
has_rgb_lightmap = true;
}
const std::initializer_list<qboundsd> &get_hull_sizes() const
{
static constexpr std::initializer_list<qboundsd> hulls = {
{{0, 0, 0}, {0, 0, 0}},
{{-16, -16, -36}, {16, 16, 36}},
{{-32, -32, -32}, {32, 32, 32}},
{{-16, -16, -18}, {16, 16, 18}}
};
return hulls;
}
};
struct gamedef_q2_t : public gamedef_t
{
gamedef_q2_t() :
gamedef_t("BASEQ2")
{
this->id = GAME_QUAKE_II;
has_rgb_lightmap = true;
}
bool surf_is_lightmapped(const surfflags_t &flags) const
{
return !(flags.native & (Q2_SURF_WARP | Q2_SURF_SKY | Q2_SURF_NODRAW)); // mxd. +Q2_SURF_NODRAW
}
bool surf_is_subdivided(const surfflags_t &flags) const { return !(flags.native & (Q2_SURF_WARP | Q2_SURF_SKY)); }
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const
{
contentflags_t c = {contents0.native | contents1.native, contents0.extended | contents1.extended};
// a cluster may include some solid detail areas, but
// still be seen into
if (!(contents0.native & Q2_CONTENTS_SOLID) || !(contents1.native & Q2_CONTENTS_SOLID))
c.native &= ~Q2_CONTENTS_SOLID;
return c;
}
int32_t get_content_type(const contentflags_t &contents) const
{
return (contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1)) |
(Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP);
}
int32_t contents_priority(const contentflags_t &contents) const
{
if (contents.extended & CFLAGS_DETAIL) {
return 8;
} else if (contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
return 6;
} else if (contents.extended & CFLAGS_DETAIL_FENCE) {
return 7;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else
switch (contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1)) {
case Q2_CONTENTS_SOLID: return 10;
case Q2_CONTENTS_WINDOW: return 9;
case Q2_CONTENTS_AUX: return 5;
case Q2_CONTENTS_LAVA: return 4;
case Q2_CONTENTS_SLIME: return 3;
case Q2_CONTENTS_WATER: return 2;
case Q2_CONTENTS_MIST: return 1;
default: return 0;
}
}
contentflags_t create_empty_contents(const int32_t &cflags) const { return {0, cflags}; }
contentflags_t create_solid_contents(const int32_t &cflags) const { return {Q2_CONTENTS_SOLID, cflags}; }
contentflags_t create_sky_contents(const int32_t &cflags) const { return create_solid_contents(cflags); }
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags) const
{
switch (liquid_type) {
case CONTENTS_WATER: return {Q2_CONTENTS_WATER, cflags};
case CONTENTS_SLIME: return {Q2_CONTENTS_SLIME, cflags};
case CONTENTS_LAVA: return {Q2_CONTENTS_LAVA, cflags};
default: FError("bad contents");
}
}
bool contents_are_empty(const contentflags_t &contents) const
{
return !(contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1));
}
bool contents_are_solid(const contentflags_t &contents) const { return contents.native & Q2_CONTENTS_SOLID; }
bool contents_are_sky(const contentflags_t &contents) const { return false; }
bool contents_are_liquid(const contentflags_t &contents) const { return contents.native & Q2_CONTENTS_LIQUID; }
bool contents_are_valid(const contentflags_t &contents, bool strict) const
{
// check that we don't have more than one visible contents type
const int32_t x = (contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1));
if ((x & (x - 1)) != 0) {
return false;
}
// TODO: check other invalid mixes
if (!x && strict) {
return false;
}
return true;
}
constexpr int32_t visible_contents(const int32_t &contents) const
{
for (int32_t i = 1; i <= Q2_LAST_VISIBLE_CONTENTS; i <<= 1)
if (contents & i)
return i;
return 0;
}
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const
{
int32_t c0 = contents0.native, c1 = contents1.native;
if (!visible_contents(c0 ^ c1))
return true;
if ((c0 & Q2_CONTENTS_TRANSLUCENT) || contents0.is_detail())
c0 = 0;
if ((c1 & Q2_CONTENTS_TRANSLUCENT) || contents1.is_detail())
c1 = 0;
// can't see through solid
if ((c0 | c1) & Q2_CONTENTS_SOLID)
return false;
// identical on both sides
if (!(c0 ^ c1))
return true;
return visible_contents(c0 ^ c1);
}
std::string get_contents_display(const contentflags_t &contents) const
{
constexpr const char *bitflag_names[] = {"SOLID", "WINDOW", "AUX", "LAVA", "SLIME", "WATER", "MIST", "128",
"256", "512", "1024", "2048", "4096", "8192", "16384", "AREAPORTAL", "PLAYERCLIP", "MONSTERCLIP",
"CURRENT_0", "CURRENT_90", "CURRENT_180", "CURRENT_270", "CURRENT_UP", "CURRENT_DOWN", "ORIGIN", "MONSTER",
"DEADMONSTER", "DETAIL", "TRANSLUCENT", "LADDER", "1073741824", "2147483648"};
std::string s;
for (int32_t i = 0; i < std::size(bitflag_names); i++) {
if (contents.native & (1 << i)) {
if (s.size()) {
s += " | " + std::string(bitflag_names[i]);
} else {
s += bitflag_names[i];
}
}
}
return s;
}
const std::initializer_list<qboundsd> &get_hull_sizes() const
{
static constexpr std::initializer_list<qboundsd> hulls = {};
return hulls;
}
};
static const gamedef_generic_t gamedef_generic;
const bspversion_t bspver_generic{NO_VERSION, NO_VERSION, "mbsp", "generic BSP", &gamedef_generic};
static const gamedef_q1_like_t<GAME_QUAKE> gamedef_q1;
const bspversion_t bspver_q1{BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", &gamedef_q1};
const bspversion_t bspver_bsp2{BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", &gamedef_q1};
const bspversion_t bspver_bsp2rmq{BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", &gamedef_q1};
/* Hexen II doesn't use a separate version, but we can still use a separate tag/name for it */
static const gamedef_h2_t gamedef_h2;
const bspversion_t bspver_h2{BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", &gamedef_h2};
const bspversion_t bspver_h2bsp2{BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", &gamedef_h2};
const bspversion_t bspver_h2bsp2rmq{BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", &gamedef_h2};
static const gamedef_hl_t gamedef_hl;
const bspversion_t bspver_hl{BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", &gamedef_hl};
static const gamedef_q2_t gamedef_q2;
const bspversion_t bspver_q2{Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", &gamedef_q2};
const bspversion_t bspver_qbism{Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", &gamedef_q2};
bool contentflags_t::types_equal(const contentflags_t &other, const gamedef_t *game) const
{
return (extended & CFLAGS_DETAIL_MASK) == (other.extended & CFLAGS_DETAIL_MASK) &&
game->get_content_type(*this) == game->get_content_type(other);
}
int32_t contentflags_t::priority(const gamedef_t *game) const
{
return game->contents_priority(*this);
}
bool contentflags_t::is_empty(const gamedef_t *game) const
{
return game->contents_are_empty(*this);
}
bool contentflags_t::is_solid(const gamedef_t *game) const
{
return game->contents_are_solid(*this);
}
bool contentflags_t::is_sky(const gamedef_t *game) const
{
return game->contents_are_sky(*this);
}
bool contentflags_t::is_liquid(const gamedef_t *game) const
{
return game->contents_are_liquid(*this);
}
bool contentflags_t::is_valid(const gamedef_t *game, bool strict) const
{
return game->contents_are_valid(*this, strict);
}
std::string contentflags_t::to_string(const gamedef_t *game) const
{
std::string s = game->get_contents_display(*this);
return s;
}
static const char *BSPVersionString(const bspversion_t *version)
{
if (version->name) {
return version->name;
}
static char buffers[2][20];
static int index;
char *buffer = buffers[1 & ++index];
if (version->version != NO_VERSION) {
snprintf(buffer, sizeof(buffers[0]), "%d:%d", version->version, version->ident);
} else {
snprintf(buffer, sizeof(buffers[0]), "%d", version->version);
}
return buffer;
}
static bool BSPVersionSupported(int32_t ident, int32_t version, const bspversion_t **out_version)
{
for (const bspversion_t *bspver : bspversions) {
if (bspver->ident == ident && bspver->version == version) {
*out_version = bspver;
return true;
}
}
return false;
}
/*
* =========================================================================
* BSP BYTE SWAPPING
* =========================================================================
*/
enum swaptype_t : bool
{
TO_DISK,
TO_CPU
};
static void SwapBSPVertexes(int numvertexes, dvertex_t *verticies)
{
dvertex_t *vertex = verticies;
int i, j;
for (i = 0; i < numvertexes; i++, vertex++)
for (j = 0; j < 3; j++)
vertex->point[j] = LittleFloat(vertex->point[j]);
}
static void SwapBSPPlanes(int numplanes, dplane_t *planes)
{
dplane_t *plane = planes;
int i, j;
for (i = 0; i < numplanes; i++, plane++) {
for (j = 0; j < 3; j++)
plane->normal[j] = LittleFloat(plane->normal[j]);
plane->dist = LittleFloat(plane->dist);
plane->type = LittleLong(plane->type);
}
}
static void SwapBSPTexinfo(int numtexinfo, texinfo_t *texinfos)
{
texinfo_t *texinfo = texinfos;
int i, j;
for (i = 0; i < numtexinfo; i++, texinfo++) {
for (j = 0; j < 4; j++) {
texinfo->vecs[0][j] = LittleFloat(texinfo->vecs[0][j]);
texinfo->vecs[1][j] = LittleFloat(texinfo->vecs[1][j]);
}
texinfo->miptex = LittleLong(texinfo->miptex);
texinfo->flags = LittleLong(texinfo->flags);
}
}
static void SwapBSP29Faces(int numfaces, bsp29_dface_t *faces)
{
bsp29_dface_t *face = faces;
int i;
for (i = 0; i < numfaces; i++, face++) {
face->texinfo = LittleShort(face->texinfo);
face->planenum = LittleShort(face->planenum);
face->side = LittleShort(face->side);
face->lightofs = LittleLong(face->lightofs);
face->firstedge = LittleLong(face->firstedge);
face->numedges = LittleShort(face->numedges);
}
}
static void SwapBSP2Faces(int numfaces, bsp2_dface_t *faces)
{
bsp2_dface_t *face = faces;
int i;
for (i = 0; i < numfaces; i++, face++) {
face->texinfo = LittleLong(face->texinfo);
face->planenum = LittleLong(face->planenum);
face->side = LittleLong(face->side);
face->lightofs = LittleLong(face->lightofs);
face->firstedge = LittleLong(face->firstedge);
face->numedges = LittleLong(face->numedges);
}
}
static void SwapBSP29Nodes(int numnodes, bsp29_dnode_t *nodes)
{
bsp29_dnode_t *node = nodes;
int i, j;
/* nodes */
for (i = 0; i < numnodes; i++, node++) {
node->planenum = LittleLong(node->planenum);
for (j = 0; j < 3; j++) {
node->mins[j] = LittleShort(node->mins[j]);
node->maxs[j] = LittleShort(node->maxs[j]);
}
node->children[0] = LittleShort(node->children[0]);
node->children[1] = LittleShort(node->children[1]);
node->firstface = LittleShort(node->firstface);
node->numfaces = LittleShort(node->numfaces);
}
}
static void SwapBSP2rmqNodes(int numnodes, bsp2rmq_dnode_t *nodes)
{
bsp2rmq_dnode_t *node = nodes;
int i, j;
/* nodes */
for (i = 0; i < numnodes; i++, node++) {
node->planenum = LittleLong(node->planenum);
for (j = 0; j < 3; j++) {
node->mins[j] = LittleShort(node->mins[j]);
node->maxs[j] = LittleShort(node->maxs[j]);
}
node->children[0] = LittleLong(node->children[0]);
node->children[1] = LittleLong(node->children[1]);
node->firstface = LittleLong(node->firstface);
node->numfaces = LittleLong(node->numfaces);
}
}
static void SwapBSP2Nodes(int numnodes, bsp2_dnode_t *nodes)
{
bsp2_dnode_t *node = nodes;
int i, j;
/* nodes */
for (i = 0; i < numnodes; i++, node++) {
node->planenum = LittleLong(node->planenum);
for (j = 0; j < 3; j++) {
node->mins[j] = LittleFloat(node->mins[j]);
node->maxs[j] = LittleFloat(node->maxs[j]);
}
node->children[0] = LittleLong(node->children[0]);
node->children[1] = LittleLong(node->children[1]);
node->firstface = LittleLong(node->firstface);
node->numfaces = LittleLong(node->numfaces);
}
}
static void SwapBSP29Leafs(int numleafs, bsp29_dleaf_t *leafs)
{
bsp29_dleaf_t *leaf = leafs;
int i, j;
for (i = 0; i < numleafs; i++, leaf++) {
leaf->contents = LittleLong(leaf->contents);
for (j = 0; j < 3; j++) {
leaf->mins[j] = LittleShort(leaf->mins[j]);
leaf->maxs[j] = LittleShort(leaf->maxs[j]);
}
leaf->firstmarksurface = LittleShort(leaf->firstmarksurface);
leaf->nummarksurfaces = LittleShort(leaf->nummarksurfaces);
leaf->visofs = LittleLong(leaf->visofs);
}
}
static void SwapBSP2rmqLeafs(int numleafs, bsp2rmq_dleaf_t *leafs)
{
bsp2rmq_dleaf_t *leaf = leafs;
int i, j;
for (i = 0; i < numleafs; i++, leaf++) {
leaf->contents = LittleLong(leaf->contents);
for (j = 0; j < 3; j++) {
leaf->mins[j] = LittleShort(leaf->mins[j]);
leaf->maxs[j] = LittleShort(leaf->maxs[j]);
}
leaf->firstmarksurface = LittleLong(leaf->firstmarksurface);
leaf->nummarksurfaces = LittleLong(leaf->nummarksurfaces);
leaf->visofs = LittleLong(leaf->visofs);
}
}
static void SwapBSP2Leafs(int numleafs, bsp2_dleaf_t *leafs)
{
bsp2_dleaf_t *leaf = leafs;
int i, j;
for (i = 0; i < numleafs; i++, leaf++) {
leaf->contents = LittleLong(leaf->contents);
for (j = 0; j < 3; j++) {
leaf->mins[j] = LittleFloat(leaf->mins[j]);
leaf->maxs[j] = LittleFloat(leaf->maxs[j]);
}
leaf->firstmarksurface = LittleLong(leaf->firstmarksurface);
leaf->nummarksurfaces = LittleLong(leaf->nummarksurfaces);
leaf->visofs = LittleLong(leaf->visofs);
}
}
static void SwapBSP29Clipnodes(int numclipnodes, bsp29_dclipnode_t *clipnodes)
{
bsp29_dclipnode_t *clipnode = clipnodes;
int i;
for (i = 0; i < numclipnodes; i++, clipnode++) {
clipnode->planenum = LittleLong(clipnode->planenum);
clipnode->children[0] = LittleShort(clipnode->children[0]);
clipnode->children[1] = LittleShort(clipnode->children[1]);
}
}
static void SwapBSP2Clipnodes(int numclipnodes, bsp2_dclipnode_t *clipnodes)
{
bsp2_dclipnode_t *clipnode = clipnodes;
int i;
for (i = 0; i < numclipnodes; i++, clipnode++) {
clipnode->planenum = LittleLong(clipnode->planenum);
clipnode->children[0] = LittleLong(clipnode->children[0]);
clipnode->children[1] = LittleLong(clipnode->children[1]);
}
}
static void SwapBSP29Marksurfaces(int nummarksurfaces, uint16_t *dmarksurfaces)
{
uint16_t *marksurface = dmarksurfaces;
int i;
for (i = 0; i < nummarksurfaces; i++, marksurface++)
*marksurface = LittleShort(*marksurface);
}
static void SwapBSP2Marksurfaces(int nummarksurfaces, uint32_t *dmarksurfaces)
{
uint32_t *marksurface = dmarksurfaces;
int i;
for (i = 0; i < nummarksurfaces; i++, marksurface++)
*marksurface = LittleLong(*marksurface);
}
static void SwapBSPSurfedges(int numsurfedges, int32_t *dsurfedges)
{
int32_t *surfedge = dsurfedges;
int i;
for (i = 0; i < numsurfedges; i++, surfedge++)
*surfedge = LittleLong(*surfedge);
}
static void SwapBSP29Edges(int numedges, bsp29_dedge_t *dedges)
{
bsp29_dedge_t *edge = dedges;
int i;
for (i = 0; i < numedges; i++, edge++) {
edge->v[0] = LittleShort(edge->v[0]);
edge->v[1] = LittleShort(edge->v[1]);
}
}
static void SwapBSP2Edges(int numedges, bsp2_dedge_t *dedges)
{
bsp2_dedge_t *edge = dedges;
int i;
for (i = 0; i < numedges; i++, edge++) {
edge->v[0] = LittleLong(edge->v[0]);
edge->v[1] = LittleLong(edge->v[1]);
}
}
static void SwapBSPModels(int nummodels, dmodelh2_t *dmodels)
{
dmodelh2_t *dmodel = dmodels;
int i, j;
for (i = 0; i < nummodels; i++, dmodel++) {
for (j = 0; j < MAX_MAP_HULLS_H2; j++)
dmodel->headnode[j] = LittleLong(dmodel->headnode[j]);
dmodel->visleafs = LittleLong(dmodel->visleafs);
dmodel->firstface = LittleLong(dmodel->firstface);
dmodel->numfaces = LittleLong(dmodel->numfaces);
for (j = 0; j < 3; j++) {
dmodel->mins[j] = LittleFloat(dmodel->mins[j]);
dmodel->maxs[j] = LittleFloat(dmodel->maxs[j]);
dmodel->origin[j] = LittleFloat(dmodel->origin[j]);
}
}
}
static void SwapBSPModels(int nummodels, dmodelq1_t *dmodels)
{
dmodelq1_t *dmodel = dmodels;
int i, j;
for (i = 0; i < nummodels; i++, dmodel++) {
for (j = 0; j < MAX_MAP_HULLS_Q1; j++)
dmodel->headnode[j] = LittleLong(dmodel->headnode[j]);
dmodel->visleafs = LittleLong(dmodel->visleafs);
dmodel->firstface = LittleLong(dmodel->firstface);
dmodel->numfaces = LittleLong(dmodel->numfaces);
for (j = 0; j < 3; j++) {
dmodel->mins[j] = LittleFloat(dmodel->mins[j]);
dmodel->maxs[j] = LittleFloat(dmodel->maxs[j]);
dmodel->origin[j] = LittleFloat(dmodel->origin[j]);
}
}
}
static void SwapBSPMiptex(int texdatasize, dmiptexlump_t *header, const swaptype_t swap)
{
int i, count;
if (!texdatasize)
return;
count = header->nummiptex;
if (swap == TO_CPU)
count = LittleLong(count);
header->nummiptex = LittleLong(header->nummiptex);
for (i = 0; i < count; i++)
header->dataofs[i] = LittleLong(header->dataofs[i]);
}
/*
=============
Q2_SwapBSPFile
Byte swaps all data in a bsp file.
=============
*/
void Q2_SwapBSPFile(q2bsp_t &bsp, bool todisk)
{
int i, j;
q2_dmodel_t *d;
// models
for (i = 0; i < bsp.nummodels; i++) {
d = &bsp.dmodels[i];
d->firstface = LittleLong(d->firstface);
d->numfaces = LittleLong(d->numfaces);
d->headnode = LittleLong(d->headnode);
for (j = 0; j < 3; j++) {
d->mins[j] = LittleFloat(d->mins[j]);
d->maxs[j] = LittleFloat(d->maxs[j]);
d->origin[j] = LittleFloat(d->origin[j]);
}
}
//
// vertexes
//
for (i = 0; i < bsp.numvertexes; i++) {
for (j = 0; j < 3; j++)
bsp.dvertexes[i].point[j] = LittleFloat(bsp.dvertexes[i].point[j]);
}
//
// planes
//
for (i = 0; i < bsp.numplanes; i++) {
for (j = 0; j < 3; j++)
bsp.dplanes[i].normal[j] = LittleFloat(bsp.dplanes[i].normal[j]);
bsp.dplanes[i].dist = LittleFloat(bsp.dplanes[i].dist);
bsp.dplanes[i].type = LittleLong(bsp.dplanes[i].type);
}
//
// texinfos
//
for (i = 0; i < bsp.numtexinfo; i++) {
for (j = 0; j < 4; j++) {
bsp.texinfo[i].vecs[0][j] = LittleFloat(bsp.texinfo[i].vecs[0][j]);
bsp.texinfo[i].vecs[1][j] = LittleFloat(bsp.texinfo[i].vecs[1][j]);
}
bsp.texinfo[i].flags = LittleLong(bsp.texinfo[i].flags);
bsp.texinfo[i].value = LittleLong(bsp.texinfo[i].value);
bsp.texinfo[i].nexttexinfo = LittleLong(bsp.texinfo[i].nexttexinfo);
}
//
// faces
//
for (i = 0; i < bsp.numfaces; i++) {
bsp.dfaces[i].texinfo = LittleShort(bsp.dfaces[i].texinfo);
bsp.dfaces[i].planenum = LittleShort(bsp.dfaces[i].planenum);
bsp.dfaces[i].side = LittleShort(bsp.dfaces[i].side);
bsp.dfaces[i].lightofs = LittleLong(bsp.dfaces[i].lightofs);
bsp.dfaces[i].firstedge = LittleLong(bsp.dfaces[i].firstedge);
bsp.dfaces[i].numedges = LittleShort(bsp.dfaces[i].numedges);
}
//
// nodes
//
for (i = 0; i < bsp.numnodes; i++) {
bsp.dnodes[i].planenum = LittleLong(bsp.dnodes[i].planenum);
for (j = 0; j < 3; j++) {
bsp.dnodes[i].mins[j] = LittleShort(bsp.dnodes[i].mins[j]);
bsp.dnodes[i].maxs[j] = LittleShort(bsp.dnodes[i].maxs[j]);
}
bsp.dnodes[i].children[0] = LittleLong(bsp.dnodes[i].children[0]);
bsp.dnodes[i].children[1] = LittleLong(bsp.dnodes[i].children[1]);
bsp.dnodes[i].firstface = LittleShort(bsp.dnodes[i].firstface);
bsp.dnodes[i].numfaces = LittleShort(bsp.dnodes[i].numfaces);
}
//
// leafs
//
for (i = 0; i < bsp.numleafs; i++) {
bsp.dleafs[i].contents = LittleLong(bsp.dleafs[i].contents);
bsp.dleafs[i].cluster = LittleShort(bsp.dleafs[i].cluster);
bsp.dleafs[i].area = LittleShort(bsp.dleafs[i].area);
for (j = 0; j < 3; j++) {
bsp.dleafs[i].mins[j] = LittleShort(bsp.dleafs[i].mins[j]);
bsp.dleafs[i].maxs[j] = LittleShort(bsp.dleafs[i].maxs[j]);
}
bsp.dleafs[i].firstleafface = LittleShort(bsp.dleafs[i].firstleafface);
bsp.dleafs[i].numleaffaces = LittleShort(bsp.dleafs[i].numleaffaces);
bsp.dleafs[i].firstleafbrush = LittleShort(bsp.dleafs[i].firstleafbrush);
bsp.dleafs[i].numleafbrushes = LittleShort(bsp.dleafs[i].numleafbrushes);
}
//
// leaffaces
//
for (i = 0; i < bsp.numleaffaces; i++)
bsp.dleaffaces[i] = LittleShort(bsp.dleaffaces[i]);
//
// leafbrushes
//
for (i = 0; i < bsp.numleafbrushes; i++)
bsp.dleafbrushes[i] = LittleShort(bsp.dleafbrushes[i]);
//
// surfedges
//
for (i = 0; i < bsp.numsurfedges; i++)
bsp.dsurfedges[i] = LittleLong(bsp.dsurfedges[i]);
//
// edges
//
for (i = 0; i < bsp.numedges; i++) {
bsp.dedges[i].v[0] = LittleShort(bsp.dedges[i].v[0]);
bsp.dedges[i].v[1] = LittleShort(bsp.dedges[i].v[1]);
}
//
// brushes
//
for (i = 0; i < bsp.numbrushes; i++) {
bsp.dbrushes[i].firstside = LittleLong(bsp.dbrushes[i].firstside);
bsp.dbrushes[i].numsides = LittleLong(bsp.dbrushes[i].numsides);
bsp.dbrushes[i].contents = LittleLong(bsp.dbrushes[i].contents);
}
//
// areas
//
for (i = 0; i < bsp.numareas; i++) {
bsp.dareas[i].numareaportals = LittleLong(bsp.dareas[i].numareaportals);
bsp.dareas[i].firstareaportal = LittleLong(bsp.dareas[i].firstareaportal);
}
//
// areasportals
//
for (i = 0; i < bsp.numareaportals; i++) {
bsp.dareaportals[i].portalnum = LittleLong(bsp.dareaportals[i].portalnum);
bsp.dareaportals[i].otherarea = LittleLong(bsp.dareaportals[i].otherarea);
}
//
// brushsides
//
for (i = 0; i < bsp.numbrushsides; i++) {
bsp.dbrushsides[i].planenum = LittleShort(bsp.dbrushsides[i].planenum);
bsp.dbrushsides[i].texinfo = LittleShort(bsp.dbrushsides[i].texinfo);
}
//
// visibility
//
if (bsp.dvis) {
if (todisk)
j = bsp.dvis->numclusters;
else
j = LittleLong(bsp.dvis->numclusters);
bsp.dvis->numclusters = LittleLong(bsp.dvis->numclusters);
for (i = 0; i < j; i++) {
bsp.dvis->bitofs[i][0] = LittleLong(bsp.dvis->bitofs[i][0]);
bsp.dvis->bitofs[i][1] = LittleLong(bsp.dvis->bitofs[i][1]);
}
}
}
/*
=============
Q2_Qbism_SwapBSPFile
Byte swaps all data in a bsp file.
=============
*/
void Q2_Qbism_SwapBSPFile(q2bsp_qbism_t &bsp, bool todisk)
{
int i, j;
q2_dmodel_t *d;
// models
for (i = 0; i < bsp.nummodels; i++) {
d = &bsp.dmodels[i];
d->firstface = LittleLong(d->firstface);
d->numfaces = LittleLong(d->numfaces);
d->headnode = LittleLong(d->headnode);
for (j = 0; j < 3; j++) {
d->mins[j] = LittleFloat(d->mins[j]);
d->maxs[j] = LittleFloat(d->maxs[j]);
d->origin[j] = LittleFloat(d->origin[j]);
}
}
//
// vertexes
//
for (i = 0; i < bsp.numvertexes; i++) {
for (j = 0; j < 3; j++)
bsp.dvertexes[i].point[j] = LittleFloat(bsp.dvertexes[i].point[j]);
}
//
// planes
//
for (i = 0; i < bsp.numplanes; i++) {
for (j = 0; j < 3; j++)
bsp.dplanes[i].normal[j] = LittleFloat(bsp.dplanes[i].normal[j]);
bsp.dplanes[i].dist = LittleFloat(bsp.dplanes[i].dist);
bsp.dplanes[i].type = LittleLong(bsp.dplanes[i].type);
}
//
// texinfos
//
for (i = 0; i < bsp.numtexinfo; i++) {
for (j = 0; j < 4; j++) {
bsp.texinfo[i].vecs[0][j] = LittleFloat(bsp.texinfo[i].vecs[0][j]);
bsp.texinfo[i].vecs[1][j] = LittleFloat(bsp.texinfo[i].vecs[1][j]);
}
bsp.texinfo[i].flags = LittleLong(bsp.texinfo[i].flags);
bsp.texinfo[i].value = LittleLong(bsp.texinfo[i].value);
bsp.texinfo[i].nexttexinfo = LittleLong(bsp.texinfo[i].nexttexinfo);
}
//
// faces
//
for (i = 0; i < bsp.numfaces; i++) {
bsp.dfaces[i].texinfo = LittleLong(bsp.dfaces[i].texinfo);
bsp.dfaces[i].planenum = LittleLong(bsp.dfaces[i].planenum);
bsp.dfaces[i].side = LittleLong(bsp.dfaces[i].side);
bsp.dfaces[i].lightofs = LittleLong(bsp.dfaces[i].lightofs);
bsp.dfaces[i].firstedge = LittleLong(bsp.dfaces[i].firstedge);
bsp.dfaces[i].numedges = LittleLong(bsp.dfaces[i].numedges);
}
//
// nodes
//
for (i = 0; i < bsp.numnodes; i++) {
bsp.dnodes[i].planenum = LittleLong(bsp.dnodes[i].planenum);
for (j = 0; j < 3; j++) {
bsp.dnodes[i].mins[j] = LittleFloat(bsp.dnodes[i].mins[j]);
bsp.dnodes[i].maxs[j] = LittleFloat(bsp.dnodes[i].maxs[j]);
}
bsp.dnodes[i].children[0] = LittleLong(bsp.dnodes[i].children[0]);
bsp.dnodes[i].children[1] = LittleLong(bsp.dnodes[i].children[1]);
bsp.dnodes[i].firstface = LittleLong(bsp.dnodes[i].firstface);
bsp.dnodes[i].numfaces = LittleLong(bsp.dnodes[i].numfaces);
}
//
// leafs
//
for (i = 0; i < bsp.numleafs; i++) {
bsp.dleafs[i].contents = LittleLong(bsp.dleafs[i].contents);
bsp.dleafs[i].cluster = LittleLong(bsp.dleafs[i].cluster);
bsp.dleafs[i].area = LittleLong(bsp.dleafs[i].area);
for (j = 0; j < 3; j++) {
bsp.dleafs[i].mins[j] = LittleFloat(bsp.dleafs[i].mins[j]);
bsp.dleafs[i].maxs[j] = LittleFloat(bsp.dleafs[i].maxs[j]);
}
bsp.dleafs[i].firstleafface = LittleLong(bsp.dleafs[i].firstleafface);
bsp.dleafs[i].numleaffaces = LittleLong(bsp.dleafs[i].numleaffaces);
bsp.dleafs[i].firstleafbrush = LittleLong(bsp.dleafs[i].firstleafbrush);
bsp.dleafs[i].numleafbrushes = LittleLong(bsp.dleafs[i].numleafbrushes);
}
//
// leaffaces
//
for (i = 0; i < bsp.numleaffaces; i++)
bsp.dleaffaces[i] = LittleLong(bsp.dleaffaces[i]);
//
// leafbrushes
//
for (i = 0; i < bsp.numleafbrushes; i++)
bsp.dleafbrushes[i] = LittleLong(bsp.dleafbrushes[i]);
//
// surfedges
//
for (i = 0; i < bsp.numsurfedges; i++)
bsp.dsurfedges[i] = LittleLong(bsp.dsurfedges[i]);
//
// edges
//
for (i = 0; i < bsp.numedges; i++) {
bsp.dedges[i].v[0] = LittleLong(bsp.dedges[i].v[0]);
bsp.dedges[i].v[1] = LittleLong(bsp.dedges[i].v[1]);
}
//
// brushes
//
for (i = 0; i < bsp.numbrushes; i++) {
bsp.dbrushes[i].firstside = LittleLong(bsp.dbrushes[i].firstside);
bsp.dbrushes[i].numsides = LittleLong(bsp.dbrushes[i].numsides);
bsp.dbrushes[i].contents = LittleLong(bsp.dbrushes[i].contents);
}
//
// areas
//
for (i = 0; i < bsp.numareas; i++) {
bsp.dareas[i].numareaportals = LittleLong(bsp.dareas[i].numareaportals);
bsp.dareas[i].firstareaportal = LittleLong(bsp.dareas[i].firstareaportal);
}
//
// areasportals
//
for (i = 0; i < bsp.numareaportals; i++) {
bsp.dareaportals[i].portalnum = LittleLong(bsp.dareaportals[i].portalnum);
bsp.dareaportals[i].otherarea = LittleLong(bsp.dareaportals[i].otherarea);
}
//
// brushsides
//
for (i = 0; i < bsp.numbrushsides; i++) {
bsp.dbrushsides[i].planenum = LittleLong(bsp.dbrushsides[i].planenum);
bsp.dbrushsides[i].texinfo = LittleLong(bsp.dbrushsides[i].texinfo);
}
//
// visibility
//
if (bsp.dvis) {
if (todisk)
j = bsp.dvis->numclusters;
else
j = LittleLong(bsp.dvis->numclusters);
bsp.dvis->numclusters = LittleLong(bsp.dvis->numclusters);
for (i = 0; i < j; i++) {
bsp.dvis->bitofs[i][0] = LittleLong(bsp.dvis->bitofs[i][0]);
bsp.dvis->bitofs[i][1] = LittleLong(bsp.dvis->bitofs[i][1]);
}
}
}
/*
* =============
* SwapBSPFile
* Byte swaps all data in a bsp file.
* =============
*/
static void SwapBSPFile(bspdata_t *bspdata, swaptype_t swap)
{
if (bspdata->version == &bspver_q2) {
Q2_SwapBSPFile(std::get<q2bsp_t>(bspdata->bsp), swap == TO_DISK);
return;
} else if (bspdata->version == &bspver_qbism) {
Q2_Qbism_SwapBSPFile(std::get<q2bsp_qbism_t>(bspdata->bsp), swap == TO_DISK);
return;
}
if (bspdata->version == &bspver_q1 || bspdata->version == &bspver_h2 || bspdata->version == &bspver_hl) {
bsp29_t &bsp = std::get<bsp29_t>(bspdata->bsp);
SwapBSPVertexes(bsp.numvertexes, bsp.dvertexes);
SwapBSPPlanes(bsp.numplanes, bsp.dplanes);
SwapBSPTexinfo(bsp.numtexinfo, bsp.texinfo);
SwapBSP29Faces(bsp.numfaces, bsp.dfaces);
SwapBSP29Nodes(bsp.numnodes, bsp.dnodes);
SwapBSP29Leafs(bsp.numleafs, bsp.dleafs);
SwapBSP29Clipnodes(bsp.numclipnodes, bsp.dclipnodes);
SwapBSPMiptex(bsp.texdatasize, bsp.dtexdata, swap);
SwapBSP29Marksurfaces(bsp.nummarksurfaces, bsp.dmarksurfaces);
SwapBSPSurfedges(bsp.numsurfedges, bsp.dsurfedges);
SwapBSP29Edges(bsp.numedges, bsp.dedges);
if (bspdata->version == &bspver_h2) {
SwapBSPModels(bsp.nummodels, bsp.dmodels_h2);
} else {
SwapBSPModels(bsp.nummodels, bsp.dmodels_q);
}
return;
}
if (bspdata->version == &bspver_bsp2rmq || bspdata->version == &bspver_h2bsp2rmq) {
bsp2rmq_t &bsp = std::get<bsp2rmq_t>(bspdata->bsp);
SwapBSPVertexes(bsp.numvertexes, bsp.dvertexes);
SwapBSPPlanes(bsp.numplanes, bsp.dplanes);
SwapBSPTexinfo(bsp.numtexinfo, bsp.texinfo);
SwapBSP2Faces(bsp.numfaces, bsp.dfaces);
SwapBSP2rmqNodes(bsp.numnodes, bsp.dnodes);
SwapBSP2rmqLeafs(bsp.numleafs, bsp.dleafs);
SwapBSP2Clipnodes(bsp.numclipnodes, bsp.dclipnodes);
SwapBSPMiptex(bsp.texdatasize, bsp.dtexdata, swap);
SwapBSP2Marksurfaces(bsp.nummarksurfaces, bsp.dmarksurfaces);
SwapBSPSurfedges(bsp.numsurfedges, bsp.dsurfedges);
SwapBSP2Edges(bsp.numedges, bsp.dedges);
if (bspdata->version == &bspver_h2bsp2rmq) {
SwapBSPModels(bsp.nummodels, bsp.dmodels_h2);
} else {
SwapBSPModels(bsp.nummodels, bsp.dmodels_q);
}
return;
}
if (bspdata->version == &bspver_bsp2 || bspdata->version == &bspver_h2bsp2) {
bsp2_t &bsp = std::get<bsp2_t>(bspdata->bsp);
SwapBSPVertexes(bsp.numvertexes, bsp.dvertexes);
SwapBSPPlanes(bsp.numplanes, bsp.dplanes);
SwapBSPTexinfo(bsp.numtexinfo, bsp.texinfo);
SwapBSP2Faces(bsp.numfaces, bsp.dfaces);
SwapBSP2Nodes(bsp.numnodes, bsp.dnodes);
SwapBSP2Leafs(bsp.numleafs, bsp.dleafs);
SwapBSP2Clipnodes(bsp.numclipnodes, bsp.dclipnodes);
SwapBSPMiptex(bsp.texdatasize, bsp.dtexdata, swap);
SwapBSP2Marksurfaces(bsp.nummarksurfaces, bsp.dmarksurfaces);
SwapBSPSurfedges(bsp.numsurfedges, bsp.dsurfedges);
SwapBSP2Edges(bsp.numedges, bsp.dedges);
if (bspdata->version == &bspver_h2bsp2) {
SwapBSPModels(bsp.nummodels, bsp.dmodels_h2);
} else {
SwapBSPModels(bsp.nummodels, bsp.dmodels_q);
}
return;
}
FError("Unsupported BSP version: {}", BSPVersionString(bspdata->version));
}
/*
* =========================================================================
* BSP Format Conversion (ver. 29 <-> MBSP)
* =========================================================================
*/
static dmodelh2_t *BSPQ1toH2_Models(const dmodelq1_t *dmodelsq1, const int nummodels)
{
const dmodelq1_t *in = dmodelsq1;
dmodelh2_t *out = new dmodelh2_t[nummodels];
int i, j;
for (i = 0; i < nummodels; i++) {
for (j = 0; j < 3; j++) {
out[i].mins[j] = in[i].mins[j];
out[i].maxs[j] = in[i].maxs[j];
out[i].origin[j] = in[i].origin[j];
}
for (j = 0; j < MAX_MAP_HULLS_Q1; j++)
out[i].headnode[j] = in[i].headnode[j];
for (; j < MAX_MAP_HULLS_H2; j++)
out[i].headnode[j] = 0;
out[i].visleafs = in[i].visleafs;
out[i].firstface = in[i].firstface;
out[i].numfaces = in[i].numfaces;
}
return out;
}
static dmodelq1_t *BSPH2toQ1_Models(const dmodelh2_t *dmodelsh2, int nummodels)
{
const dmodelh2_t *in = dmodelsh2;
dmodelq1_t *out = new dmodelq1_t[nummodels];
int i, j;
for (i = 0; i < nummodels; i++) {
for (j = 0; j < 3; j++) {
out[i].mins[j] = in[i].mins[j];
out[i].maxs[j] = in[i].maxs[j];
out[i].origin[j] = in[i].origin[j];
}
for (j = 0; j < MAX_MAP_HULLS_Q1; j++)
out[i].headnode[j] = in[i].headnode[j];
out[i].visleafs = in[i].visleafs;
out[i].firstface = in[i].firstface;
out[i].numfaces = in[i].numfaces;
}
return out;
}
static mleaf_t *BSP29toM_Leafs(const bsp29_dleaf_t *dleafs29, int numleafs)
{
const bsp29_dleaf_t *dleaf29 = dleafs29;
mleaf_t *newdata, *mleaf;
int i, j;
newdata = mleaf = new mleaf_t[numleafs];
for (i = 0; i < numleafs; i++, dleaf29++, mleaf++) {
mleaf->contents = dleaf29->contents;
mleaf->visofs = dleaf29->visofs;
for (j = 0; j < 3; j++) {
mleaf->mins[j] = dleaf29->mins[j];
mleaf->maxs[j] = dleaf29->maxs[j];
}
mleaf->firstmarksurface = dleaf29->firstmarksurface;
mleaf->nummarksurfaces = dleaf29->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
mleaf->ambient_level[j] = dleaf29->ambient_level[j];
}
return newdata;
}
static bool OverflowsInt16(float input)
{
constexpr float minvalue = static_cast<float>(INT16_MIN);
constexpr float maxvalue = static_cast<float>(INT16_MAX);
if (input < minvalue) {
return true;
}
if (input > maxvalue) {
return true;
}
return false;
}
static bool OverflowsInt16(int32_t input)
{
if (input < INT16_MIN) {
return true;
}
if (input > INT16_MAX) {
return true;
}
return false;
}
static bool OverflowsUint16(uint32_t input)
{
if (input > INT16_MAX) {
return true;
}
return false;
}
static bool MBSPto29_Leafs_Validate(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
for (int i = 0; i < numleafs; i++, mleaf++) {
for (int j = 0; j < 3; j++) {
const float min_j = floor(mleaf->mins[j]);
const float max_j = ceil(mleaf->maxs[j]);
if (OverflowsInt16(min_j) || OverflowsInt16(max_j)) {
return false;
}
}
if (OverflowsUint16(mleaf->firstmarksurface) || OverflowsUint16(mleaf->nummarksurfaces)) {
return false;
}
}
return true;
}
static bsp29_dleaf_t *MBSPto29_Leafs(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
bsp29_dleaf_t *newdata, *dleaf29;
int i, j;
newdata = dleaf29 = new bsp29_dleaf_t[numleafs];
for (i = 0; i < numleafs; i++, mleaf++, dleaf29++) {
dleaf29->contents = mleaf->contents;
dleaf29->visofs = mleaf->visofs;
for (j = 0; j < 3; j++) {
dleaf29->mins[j] = floor(mleaf->mins[j]);
dleaf29->maxs[j] = ceil(mleaf->maxs[j]);
}
dleaf29->firstmarksurface = mleaf->firstmarksurface;
dleaf29->nummarksurfaces = mleaf->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
dleaf29->ambient_level[j] = mleaf->ambient_level[j];
}
return newdata;
}
static gtexinfo_t *BSP29toM_Texinfo(const texinfo_t *texinfos, int numtexinfo)
{
const texinfo_t *texinfo29 = texinfos;
gtexinfo_t *newdata, *mtexinfo;
int i, j, k;
newdata = mtexinfo = new gtexinfo_t[numtexinfo];
for (i = 0; i < numtexinfo; i++, texinfo29++, mtexinfo++) {
for (j = 0; j < 2; j++)
for (k = 0; k < 4; k++)
mtexinfo->vecs[j][k] = texinfo29->vecs[j][k];
mtexinfo->flags = {texinfo29->flags};
mtexinfo->miptex = texinfo29->miptex;
}
return newdata;
}
static texinfo_t *MBSPto29_Texinfo(const gtexinfo_t *mtexinfos, int numtexinfo)
{
const gtexinfo_t *mtexinfo = mtexinfos;
texinfo_t *newdata, *texinfo29;
int i, j, k;
newdata = texinfo29 = new texinfo_t[numtexinfo];
for (i = 0; i < numtexinfo; i++, texinfo29++, mtexinfo++) {
for (j = 0; j < 2; j++)
for (k = 0; k < 4; k++)
texinfo29->vecs[j][k] = mtexinfo->vecs[j][k];
texinfo29->flags = mtexinfo->flags.native;
texinfo29->miptex = mtexinfo->miptex;
}
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (ver. q2 <-> MBSP)
* =========================================================================
*/
static dmodelh2_t *Q2BSPtoM_Models(const q2_dmodel_t *dmodelsq2, int nummodels)
{
const q2_dmodel_t *dmodelq2 = dmodelsq2;
dmodelh2_t *newdata, *dmodelh2;
int i, j;
newdata = dmodelh2 = new dmodelh2_t[nummodels];
for (i = 0; i < nummodels; i++, dmodelq2++, dmodelh2++) {
for (j = 0; j < 3; j++) {
dmodelh2->mins[j] = dmodelq2->mins[j];
dmodelh2->maxs[j] = dmodelq2->maxs[j];
dmodelh2->origin[j] = dmodelq2->origin[j];
}
dmodelh2->headnode[0] = dmodelq2->headnode;
dmodelh2->visleafs = 0;
dmodelh2->firstface = dmodelq2->firstface;
dmodelh2->numfaces = dmodelq2->numfaces;
}
return newdata;
}
static uint8_t *Q2BSPtoM_CopyVisData(const dvis_t *dvisq2, int vissize, int *outvissize, mleaf_t *leafs, int numleafs)
{
if (!*outvissize) {
return nullptr;
}
// FIXME: assumes PHS always follows PVS.
int32_t phs_start = INT_MAX, pvs_start = INT_MAX;
size_t header_offset = sizeof(dvis_t) + (sizeof(int32_t) * dvisq2->numclusters * 2);
for (int32_t i = 0; i < dvisq2->numclusters; i++) {
pvs_start = std::min(pvs_start, (int32_t)(dvisq2->bitofs[i][DVIS_PVS]));
phs_start = std::min(phs_start, (int32_t)(dvisq2->bitofs[i][DVIS_PHS] - header_offset));
for (int32_t l = 0; l < numleafs; l++) {
if (leafs[l].cluster == i) {
leafs[l].visofs = dvisq2->bitofs[i][DVIS_PVS] - header_offset;
}
}
}
// cut off the PHS and header
*outvissize -= header_offset + ((*outvissize - header_offset) - phs_start);
uint8_t *vis = new uint8_t[*outvissize];
memcpy(vis, ((uint8_t *)dvisq2) + pvs_start, *outvissize);
return vis;
}
static q2_dmodel_t *MBSPtoQ2_Models(const dmodelh2_t *dmodelsh2, int nummodels)
{
const dmodelh2_t *dmodelh2 = dmodelsh2;
q2_dmodel_t *newdata, *dmodelq2;
int i, j;
newdata = dmodelq2 = new q2_dmodel_t[nummodels];
for (i = 0; i < nummodels; i++, dmodelh2++, dmodelq2++) {
for (j = 0; j < 3; j++) {
dmodelq2->mins[j] = dmodelh2->mins[j];
dmodelq2->maxs[j] = dmodelh2->maxs[j];
dmodelq2->origin[j] = dmodelh2->origin[j];
}
dmodelq2->headnode = dmodelh2->headnode[0];
dmodelq2->firstface = dmodelh2->firstface;
dmodelq2->numfaces = dmodelh2->numfaces;
}
return newdata;
}
/*
================
CalcPHS
Calculate the PHS (Potentially Hearable Set)
by ORing together all the PVS visible from a leaf
================
*/
static std::vector<uint8_t> CalcPHS(
int32_t portalclusters, const uint8_t *visdata, int *visdatasize, int32_t bitofs[][2])
{
const int32_t leafbytes = (portalclusters + 7) >> 3;
const int32_t leaflongs = leafbytes / sizeof(long);
std::vector<uint8_t> compressed_phs;
// FIXME: should this use alloca?
uint8_t *uncompressed = new uint8_t[leafbytes];
uint8_t *uncompressed_2 = new uint8_t[leafbytes];
uint8_t *compressed = new uint8_t[leafbytes * 2];
uint8_t *uncompressed_orig = new uint8_t[leafbytes];
printf("Building PHS...\n");
int32_t count = 0;
for (int32_t i = 0; i < portalclusters; i++) {
const uint8_t *scan = &visdata[bitofs[i][DVIS_PVS]];
DecompressRow(scan, leafbytes, uncompressed);
memset(uncompressed_orig, 0, leafbytes);
memcpy(uncompressed_orig, uncompressed, leafbytes);
scan = uncompressed_orig;
for (int32_t j = 0; j < leafbytes; j++) {
uint8_t bitbyte = scan[j];
if (!bitbyte)
continue;
for (int32_t k = 0; k < 8; k++) {
if (!(bitbyte & (1 << k)))
continue;
// OR this pvs row into the phs
int32_t index = ((j << 3) + k);
if (index >= portalclusters)
FError("Bad bit in PVS"); // pad bits should be 0
const uint8_t *src_compressed = &visdata[bitofs[index][DVIS_PVS]];
DecompressRow(src_compressed, leafbytes, uncompressed_2);
const long *src = (long *)uncompressed_2;
long *dest = (long *)uncompressed;
for (int32_t l = 0; l < leaflongs; l++)
((long *)uncompressed)[l] |= src[l];
}
}
for (int32_t j = 0; j < portalclusters; j++)
if (uncompressed[j >> 3] & (1 << (j & 7)))
count++;
//
// compress the bit string
//
int32_t j = CompressRow(uncompressed, leafbytes, compressed);
bitofs[i][DVIS_PHS] = compressed_phs.size();
compressed_phs.insert(compressed_phs.end(), compressed, compressed + j);
}
delete[] uncompressed;
delete[] uncompressed_2;
delete[] compressed;
delete[] uncompressed_orig;
fmt::print("Average clusters hearable: {}\n", count / portalclusters);
return compressed_phs;
}
static dvis_t *MBSPtoQ2_CopyVisData(const uint8_t *visdata, int *visdatasize, int numleafs, const mleaf_t *leafs)
{
if (!*visdatasize) {
return nullptr;
}
int32_t num_clusters = 0;
for (int32_t i = 0; i < numleafs; i++) {
num_clusters = std::max(num_clusters, leafs[i].cluster + 1);
}
size_t vis_offset = sizeof(dvis_t) + (sizeof(int32_t) * num_clusters * 2);
dvis_t *vis = (dvis_t *)calloc(1, vis_offset + *visdatasize);
vis->numclusters = num_clusters;
// the leaves are already using a per-cluster visofs, so just find one matching
// cluster and note it down under bitofs.
// we're also not worrying about PHS currently.
for (int32_t i = 0; i < num_clusters; i++) {
for (int32_t l = 0; l < numleafs; l++) {
if (leafs[l].cluster == i) {
// copy PVS visofs
vis->bitofs[i][DVIS_PVS] = leafs[l].visofs;
break;
}
}
}
std::vector<uint8_t> phs = CalcPHS(num_clusters, visdata, visdatasize, vis->bitofs);
vis = (dvis_t *)realloc(vis, vis_offset + *visdatasize + phs.size());
// offset the pvs/phs properly
for (int32_t i = 0; i < num_clusters; i++) {
vis->bitofs[i][DVIS_PVS] += vis_offset;
vis->bitofs[i][DVIS_PHS] += vis_offset + *visdatasize;
}
memcpy(((uint8_t *)vis) + vis_offset, visdata, *visdatasize);
*visdatasize += vis_offset;
memcpy(((uint8_t *)vis) + *visdatasize, phs.data(), phs.size());
*visdatasize += phs.size();
return vis;
}
static mleaf_t *Q2BSPtoM_Leafs(const q2_dleaf_t *dleafsq2, int numleafs)
{
const q2_dleaf_t *dleafq2 = dleafsq2;
mleaf_t *newdata, *mleaf;
int i, j;
newdata = mleaf = new mleaf_t[numleafs];
for (i = 0; i < numleafs; i++, dleafq2++, mleaf++) {
mleaf->contents = dleafq2->contents;
mleaf->cluster = dleafq2->cluster;
mleaf->area = dleafq2->area;
mleaf->visofs = 0;
for (j = 0; j < 3; j++) {
mleaf->mins[j] = dleafq2->mins[j];
mleaf->maxs[j] = dleafq2->maxs[j];
}
mleaf->firstmarksurface = dleafq2->firstleafface;
mleaf->nummarksurfaces = dleafq2->numleaffaces;
mleaf->firstleafbrush = dleafq2->firstleafbrush;
mleaf->numleafbrushes = dleafq2->numleafbrushes;
}
return newdata;
}
static mleaf_t *Q2BSP_QBSPtoM_Leafs(const q2_dleaf_qbism_t *dleafsq2, int numleafs)
{
const q2_dleaf_qbism_t *dleafq2 = dleafsq2;
mleaf_t *newdata, *mleaf;
int i, j;
newdata = mleaf = new mleaf_t[numleafs];
for (i = 0; i < numleafs; i++, dleafq2++, mleaf++) {
mleaf->contents = dleafq2->contents;
mleaf->cluster = dleafq2->cluster;
mleaf->area = dleafq2->area;
mleaf->visofs = 0;
for (j = 0; j < 3; j++) {
mleaf->mins[j] = dleafq2->mins[j];
mleaf->maxs[j] = dleafq2->maxs[j];
}
mleaf->firstmarksurface = dleafq2->firstleafface;
mleaf->nummarksurfaces = dleafq2->numleaffaces;
mleaf->firstleafbrush = dleafq2->firstleafbrush;
mleaf->numleafbrushes = dleafq2->numleafbrushes;
}
return newdata;
}
static q2_dleaf_t *MBSPtoQ2_Leafs(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
q2_dleaf_t *newdata, *dleafq2;
int i, j;
newdata = dleafq2 = new q2_dleaf_t[numleafs];
for (i = 0; i < numleafs; i++, mleaf++, dleafq2++) {
dleafq2->contents = mleaf->contents;
dleafq2->cluster = mleaf->cluster;
dleafq2->area = mleaf->area;
for (j = 0; j < 3; j++) {
dleafq2->mins[j] = mleaf->mins[j];
dleafq2->maxs[j] = mleaf->maxs[j];
}
dleafq2->firstleafface = mleaf->firstmarksurface;
dleafq2->numleaffaces = mleaf->nummarksurfaces;
dleafq2->firstleafbrush = mleaf->firstleafbrush;
dleafq2->numleafbrushes = mleaf->numleafbrushes;
}
return newdata;
}
static q2_dleaf_qbism_t *MBSPtoQ2_Qbism_Leafs(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
q2_dleaf_qbism_t *newdata, *dleafq2;
int i, j;
newdata = dleafq2 = new q2_dleaf_qbism_t[numleafs];
for (i = 0; i < numleafs; i++, mleaf++, dleafq2++) {
dleafq2->contents = mleaf->contents;
dleafq2->cluster = mleaf->cluster;
dleafq2->area = mleaf->area;
for (j = 0; j < 3; j++) {
dleafq2->mins[j] = mleaf->mins[j];
dleafq2->maxs[j] = mleaf->maxs[j];
}
dleafq2->firstleafface = mleaf->firstmarksurface;
dleafq2->numleaffaces = mleaf->nummarksurfaces;
dleafq2->firstleafbrush = mleaf->firstleafbrush;
dleafq2->numleafbrushes = mleaf->numleafbrushes;
}
return newdata;
}
static bsp2_dnode_t *Q2BSPto2_Nodes(const q2_dnode_t *dnodesq2, int numnodes)
{
const q2_dnode_t *dnodeq2 = dnodesq2;
bsp2_dnode_t *newdata, *dnode2;
int i, j;
newdata = dnode2 = new bsp2_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnodeq2++, dnode2++) {
dnode2->planenum = dnodeq2->planenum;
dnode2->children[0] = dnodeq2->children[0];
dnode2->children[1] = dnodeq2->children[1];
for (j = 0; j < 3; j++) {
dnode2->mins[j] = dnodeq2->mins[j];
dnode2->maxs[j] = dnodeq2->maxs[j];
}
dnode2->firstface = dnodeq2->firstface;
dnode2->numfaces = dnodeq2->numfaces;
}
return newdata;
}
static q2_dnode_t *BSP2toQ2_Nodes(const bsp2_dnode_t *dnodes2, int numnodes)
{
const bsp2_dnode_t *dnode2 = dnodes2;
q2_dnode_t *newdata, *dnodeq2;
int i, j;
newdata = dnodeq2 = new q2_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnode2++, dnodeq2++) {
dnodeq2->planenum = dnode2->planenum;
dnodeq2->children[0] = dnode2->children[0];
dnodeq2->children[1] = dnode2->children[1];
for (j = 0; j < 3; j++) {
dnodeq2->mins[j] = dnode2->mins[j];
dnodeq2->maxs[j] = dnode2->maxs[j];
}
dnodeq2->firstface = dnode2->firstface;
dnodeq2->numfaces = dnode2->numfaces;
}
return newdata;
}
static bsp2_dface_t *Q2BSPto2_Faces(const q2_dface_t *dfacesq2, int numfaces)
{
const q2_dface_t *dfaceq2 = dfacesq2;
bsp2_dface_t *newdata, *dface2;
int i, j;
newdata = dface2 = new bsp2_dface_t[numfaces];
for (i = 0; i < numfaces; i++, dfaceq2++, dface2++) {
dface2->planenum = dfaceq2->planenum;
dface2->side = dfaceq2->side;
dface2->firstedge = dfaceq2->firstedge;
dface2->numedges = dfaceq2->numedges;
dface2->texinfo = dfaceq2->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dface2->styles[j] = dfaceq2->styles[j];
dface2->lightofs = dfaceq2->lightofs;
}
return newdata;
}
static bsp2_dface_t *Q2BSP_QBSPto2_Faces(const q2_dface_qbism_t *dfacesq2, int numfaces)
{
const q2_dface_qbism_t *dfaceq2 = dfacesq2;
bsp2_dface_t *newdata, *dface2;
int i, j;
newdata = dface2 = new bsp2_dface_t[numfaces];
for (i = 0; i < numfaces; i++, dfaceq2++, dface2++) {
dface2->planenum = dfaceq2->planenum;
dface2->side = dfaceq2->side;
dface2->firstedge = dfaceq2->firstedge;
dface2->numedges = dfaceq2->numedges;
dface2->texinfo = dfaceq2->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dface2->styles[j] = dfaceq2->styles[j];
dface2->lightofs = dfaceq2->lightofs;
}
return newdata;
}
static q2_dface_t *BSP2toQ2_Faces(const bsp2_dface_t *dfaces2, int numfaces)
{
const bsp2_dface_t *dface2 = dfaces2;
q2_dface_t *newdata, *dfaceq2;
int i, j;
newdata = dfaceq2 = new q2_dface_t[numfaces];
for (i = 0; i < numfaces; i++, dface2++, dfaceq2++) {
dfaceq2->planenum = dface2->planenum;
dfaceq2->side = dface2->side;
dfaceq2->firstedge = dface2->firstedge;
dfaceq2->numedges = dface2->numedges;
dfaceq2->texinfo = dface2->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dfaceq2->styles[j] = dface2->styles[j];
dfaceq2->lightofs = dface2->lightofs;
}
return newdata;
}
static q2_dface_qbism_t *BSP2toQ2_Qbism_Faces(const bsp2_dface_t *dfaces2, int numfaces)
{
const bsp2_dface_t *dface2 = dfaces2;
q2_dface_qbism_t *newdata, *dfaceq2;
int i, j;
newdata = dfaceq2 = new q2_dface_qbism_t[numfaces];
for (i = 0; i < numfaces; i++, dface2++, dfaceq2++) {
dfaceq2->planenum = dface2->planenum;
dfaceq2->side = dface2->side;
dfaceq2->firstedge = dface2->firstedge;
dfaceq2->numedges = dface2->numedges;
dfaceq2->texinfo = dface2->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dfaceq2->styles[j] = dface2->styles[j];
dfaceq2->lightofs = dface2->lightofs;
}
return newdata;
}
static gtexinfo_t *Q2BSPtoM_Texinfo(const q2_texinfo_t *dtexinfosq2, int numtexinfos)
{
const q2_texinfo_t *dtexinfoq2 = dtexinfosq2;
gtexinfo_t *newdata, *dtexinfo2;
int i, j, k;
newdata = dtexinfo2 = new gtexinfo_t[numtexinfos];
for (i = 0; i < numtexinfos; i++, dtexinfoq2++, dtexinfo2++) {
for (j = 0; j < 2; j++)
for (k = 0; k < 4; k++)
dtexinfo2->vecs[j][k] = dtexinfoq2->vecs[j][k];
dtexinfo2->flags = {dtexinfoq2->flags};
memcpy(dtexinfo2->texture, dtexinfoq2->texture, sizeof(dtexinfo2->texture));
dtexinfo2->value = dtexinfoq2->value;
dtexinfo2->nexttexinfo = dtexinfoq2->nexttexinfo;
}
return newdata;
}
static q2_texinfo_t *MBSPtoQ2_Texinfo(const gtexinfo_t *dtexinfos2, int numtexinfos)
{
const gtexinfo_t *dtexinfo2 = dtexinfos2;
q2_texinfo_t *newdata, *dtexinfoq2;
int i, j, k;
newdata = dtexinfoq2 = new q2_texinfo_t[numtexinfos];
for (i = 0; i < numtexinfos; i++, dtexinfo2++, dtexinfoq2++) {
for (j = 0; j < 2; j++)
for (k = 0; k < 4; k++)
dtexinfoq2->vecs[j][k] = dtexinfo2->vecs[j][k];
dtexinfoq2->flags = dtexinfo2->flags.native;
memcpy(dtexinfoq2->texture, dtexinfo2->texture, sizeof(dtexinfo2->texture));
dtexinfoq2->value = dtexinfo2->value;
dtexinfoq2->nexttexinfo = dtexinfo2->nexttexinfo;
}
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (ver. 2rmq <-> MBSP)
* =========================================================================
*/
static mleaf_t *BSP2rmqtoM_Leafs(const bsp2rmq_dleaf_t *dleafs2rmq, int numleafs)
{
const bsp2rmq_dleaf_t *dleaf2rmq = dleafs2rmq;
mleaf_t *newdata, *mleaf;
int i, j;
newdata = mleaf = new mleaf_t[numleafs];
for (i = 0; i < numleafs; i++, dleaf2rmq++, mleaf++) {
mleaf->contents = dleaf2rmq->contents;
mleaf->visofs = dleaf2rmq->visofs;
for (j = 0; j < 3; j++) {
mleaf->mins[j] = dleaf2rmq->mins[j];
mleaf->maxs[j] = dleaf2rmq->maxs[j];
}
mleaf->firstmarksurface = dleaf2rmq->firstmarksurface;
mleaf->nummarksurfaces = dleaf2rmq->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
mleaf->ambient_level[j] = dleaf2rmq->ambient_level[j];
}
return newdata;
}
static bsp2rmq_dleaf_t *MBSPto2rmq_Leafs(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
bsp2rmq_dleaf_t *newdata, *dleaf2rmq;
int i, j;
newdata = dleaf2rmq = new bsp2rmq_dleaf_t[numleafs];
for (i = 0; i < numleafs; i++, mleaf++, dleaf2rmq++) {
dleaf2rmq->contents = mleaf->contents;
dleaf2rmq->visofs = mleaf->visofs;
for (j = 0; j < 3; j++) {
dleaf2rmq->mins[j] = mleaf->mins[j];
dleaf2rmq->maxs[j] = mleaf->maxs[j];
}
dleaf2rmq->firstmarksurface = mleaf->firstmarksurface;
dleaf2rmq->nummarksurfaces = mleaf->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
dleaf2rmq->ambient_level[j] = mleaf->ambient_level[j];
}
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (ver. 2 <-> MBSP)
* =========================================================================
*/
static mleaf_t *BSP2toM_Leafs(const bsp2_dleaf_t *dleafs2, int numleafs)
{
const bsp2_dleaf_t *dleaf2 = dleafs2;
mleaf_t *newdata, *mleaf;
int i, j;
newdata = mleaf = new mleaf_t[numleafs];
for (i = 0; i < numleafs; i++, dleaf2++, mleaf++) {
mleaf->contents = dleaf2->contents;
mleaf->visofs = dleaf2->visofs;
for (j = 0; j < 3; j++) {
mleaf->mins[j] = dleaf2->mins[j];
mleaf->maxs[j] = dleaf2->maxs[j];
}
mleaf->firstmarksurface = dleaf2->firstmarksurface;
mleaf->nummarksurfaces = dleaf2->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
mleaf->ambient_level[j] = dleaf2->ambient_level[j];
}
return newdata;
}
static bsp2_dleaf_t *MBSPto2_Leafs(const mleaf_t *mleafs, int numleafs)
{
const mleaf_t *mleaf = mleafs;
bsp2_dleaf_t *newdata, *dleaf2;
int i, j;
newdata = dleaf2 = new bsp2_dleaf_t[numleafs];
for (i = 0; i < numleafs; i++, mleaf++, dleaf2++) {
dleaf2->contents = mleaf->contents;
dleaf2->visofs = mleaf->visofs;
for (j = 0; j < 3; j++) {
dleaf2->mins[j] = mleaf->mins[j];
dleaf2->maxs[j] = mleaf->maxs[j];
}
dleaf2->firstmarksurface = mleaf->firstmarksurface;
dleaf2->nummarksurfaces = mleaf->nummarksurfaces;
for (j = 0; j < NUM_AMBIENTS; j++)
dleaf2->ambient_level[j] = mleaf->ambient_level[j];
}
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (ver. 29 <-> BSP2)
* =========================================================================
*/
static bsp2_dnode_t *BSP29to2_Nodes(const bsp29_dnode_t *dnodes29, int numnodes)
{
const bsp29_dnode_t *dnode29 = dnodes29;
bsp2_dnode_t *newdata, *dnode2;
int i, j;
newdata = dnode2 = new bsp2_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnode29++, dnode2++) {
dnode2->planenum = dnode29->planenum;
dnode2->children[0] = dnode29->children[0];
dnode2->children[1] = dnode29->children[1];
for (j = 0; j < 3; j++) {
dnode2->mins[j] = dnode29->mins[j];
dnode2->maxs[j] = dnode29->maxs[j];
}
dnode2->firstface = dnode29->firstface;
dnode2->numfaces = dnode29->numfaces;
}
return newdata;
}
static bool BSP2to29_Nodes_Validate(const bsp2_dnode_t *dnodes2, int numnodes)
{
const bsp2_dnode_t *dnode2 = dnodes2;
for (int i = 0; i < numnodes; i++, dnode2++) {
if (OverflowsInt16(dnode2->children[0]) || OverflowsInt16(dnode2->children[1])) {
return false;
}
for (int j = 0; j < 3; j++) {
const float min_j = floor(dnode2->mins[j]);
const float max_j = ceil(dnode2->maxs[j]);
if (OverflowsInt16(min_j) || OverflowsInt16(max_j)) {
return false;
}
}
if (OverflowsUint16(dnode2->firstface) || OverflowsUint16(dnode2->numfaces)) {
return false;
}
}
return true;
}
static bsp29_dnode_t *BSP2to29_Nodes(const bsp2_dnode_t *dnodes2, int numnodes)
{
const bsp2_dnode_t *dnode2 = dnodes2;
bsp29_dnode_t *newdata, *dnode29;
int i, j;
newdata = dnode29 = new bsp29_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnode2++, dnode29++) {
dnode29->planenum = dnode2->planenum;
dnode29->children[0] = dnode2->children[0];
dnode29->children[1] = dnode2->children[1];
for (j = 0; j < 3; j++) {
dnode29->mins[j] = floor(dnode2->mins[j]);
dnode29->maxs[j] = ceil(dnode2->maxs[j]);
}
dnode29->firstface = dnode2->firstface;
dnode29->numfaces = dnode2->numfaces;
}
return newdata;
}
static bsp2_dface_t *BSP29to2_Faces(const bsp29_dface_t *dfaces29, int numfaces)
{
const bsp29_dface_t *dface29 = dfaces29;
bsp2_dface_t *newdata, *dface2;
int i, j;
newdata = dface2 = new bsp2_dface_t[numfaces];
for (i = 0; i < numfaces; i++, dface29++, dface2++) {
dface2->planenum = dface29->planenum;
dface2->side = dface29->side;
dface2->firstedge = dface29->firstedge;
dface2->numedges = dface29->numedges;
dface2->texinfo = dface29->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dface2->styles[j] = dface29->styles[j];
dface2->lightofs = dface29->lightofs;
}
return newdata;
}
static bool BSP2to29_Faces_Validate(const bsp2_dface_t *dfaces2, int numfaces)
{
const bsp2_dface_t *dface2 = dfaces2;
for (int i = 0; i < numfaces; i++, dface2++) {
if (OverflowsInt16(dface2->planenum)) {
return false;
}
if (OverflowsInt16(dface2->side)) {
return false;
}
if (OverflowsInt16(dface2->numedges)) {
return false;
}
if (OverflowsInt16(dface2->texinfo)) {
return false;
}
}
return true;
}
static bsp29_dface_t *BSP2to29_Faces(const bsp2_dface_t *dfaces2, int numfaces)
{
const bsp2_dface_t *dface2 = dfaces2;
bsp29_dface_t *newdata, *dface29;
int i, j;
newdata = dface29 = new bsp29_dface_t[numfaces];
for (i = 0; i < numfaces; i++, dface2++, dface29++) {
dface29->planenum = dface2->planenum;
dface29->side = dface2->side;
dface29->firstedge = dface2->firstedge;
dface29->numedges = dface2->numedges;
dface29->texinfo = dface2->texinfo;
for (j = 0; j < MAXLIGHTMAPS; j++)
dface29->styles[j] = dface2->styles[j];
dface29->lightofs = dface2->lightofs;
}
return newdata;
}
static bsp2_dclipnode_t *BSP29to2_Clipnodes(const bsp29_dclipnode_t *dclipnodes29, int numclipnodes)
{
const bsp29_dclipnode_t *dclipnode29 = dclipnodes29;
bsp2_dclipnode_t *newdata, *dclipnode2;
int i, j;
newdata = dclipnode2 = new bsp2_dclipnode_t[numclipnodes];
for (i = 0; i < numclipnodes; i++, dclipnode29++, dclipnode2++) {
dclipnode2->planenum = dclipnode29->planenum;
for (j = 0; j < 2; j++) {
/* Slightly tricky since we support > 32k clipnodes */
int32_t child = (uint16_t)dclipnode29->children[j];
dclipnode2->children[j] = child > 0xfff0 ? child - 0x10000 : child;
}
}
return newdata;
}
static bool BSP2to29_Clipnodes_Validate(const bsp2_dclipnode_t *dclipnodes2, int numclipnodes)
{
const bsp2_dclipnode_t *dclipnode2 = dclipnodes2;
for (int i = 0; i < numclipnodes; i++, dclipnode2++) {
for (int j = 0; j < 2; j++) {
/* Slightly tricky since we support > 32k clipnodes */
int32_t child = dclipnode2->children[j];
if (child < -15 || child > 0xFFF0) {
return false;
}
}
}
return true;
}
static bsp29_dclipnode_t *BSP2to29_Clipnodes(const bsp2_dclipnode_t *dclipnodes2, int numclipnodes)
{
const bsp2_dclipnode_t *dclipnode2 = dclipnodes2;
bsp29_dclipnode_t *newdata, *dclipnode29;
int i, j;
newdata = dclipnode29 = new bsp29_dclipnode_t[numclipnodes];
for (i = 0; i < numclipnodes; i++, dclipnode2++, dclipnode29++) {
dclipnode29->planenum = dclipnode2->planenum;
for (j = 0; j < 2; j++) {
/* Slightly tricky since we support > 32k clipnodes */
int32_t child = dclipnode2->children[j];
dclipnode29->children[j] = child < 0 ? child + 0x10000 : child;
}
}
return newdata;
}
static bsp2_dedge_t *BSP29to2_Edges(const bsp29_dedge_t *dedges29, int numedges)
{
const bsp29_dedge_t *dedge29 = dedges29;
bsp2_dedge_t *newdata, *dedge2;
int i;
newdata = dedge2 = new bsp2_dedge_t[numedges];
for (i = 0; i < numedges; i++, dedge29++, dedge2++) {
dedge2->v[0] = dedge29->v[0];
dedge2->v[1] = dedge29->v[1];
}
return newdata;
}
static bool BSP2to29_Edges_Validate(const bsp2_dedge_t *dedges2, int numedges)
{
const bsp2_dedge_t *dedge2 = dedges2;
for (int i = 0; i < numedges; i++, dedge2++) {
if (OverflowsUint16(dedge2->v[0]) || OverflowsUint16(dedge2->v[1])) {
return false;
}
}
return true;
}
static bsp29_dedge_t *BSP2to29_Edges(const bsp2_dedge_t *dedges2, int numedges)
{
const bsp2_dedge_t *dedge2 = dedges2;
bsp29_dedge_t *newdata, *dedge29;
int i;
newdata = dedge29 = new bsp29_dedge_t[numedges];
for (i = 0; i < numedges; i++, dedge2++, dedge29++) {
dedge29->v[0] = dedge2->v[0];
dedge29->v[1] = dedge2->v[1];
}
return newdata;
}
static uint32_t *BSP29to2_Marksurfaces(const uint16_t *dmarksurfaces29, int nummarksurfaces)
{
const uint16_t *dmarksurface29 = dmarksurfaces29;
uint32_t *newdata, *dmarksurface2;
int i;
newdata = dmarksurface2 = new uint32_t[nummarksurfaces];
for (i = 0; i < nummarksurfaces; i++, dmarksurface29++, dmarksurface2++)
*dmarksurface2 = *dmarksurface29;
return newdata;
}
static bool BSP2to29_Marksurfaces_Validate(const uint32_t *dmarksurfaces2, int nummarksurfaces)
{
const uint32_t *dmarksurface2 = dmarksurfaces2;
for (int i = 0; i < nummarksurfaces; i++, dmarksurface2++) {
if (OverflowsUint16(*dmarksurface2)) {
return false;
}
}
return true;
}
static uint16_t *BSP2to29_Marksurfaces(const uint32_t *dmarksurfaces2, int nummarksurfaces)
{
const uint32_t *dmarksurface2 = dmarksurfaces2;
uint16_t *newdata, *dmarksurface29;
int i;
newdata = dmarksurface29 = new uint16_t[nummarksurfaces];
for (i = 0; i < nummarksurfaces; i++, dmarksurface2++, dmarksurface29++)
*dmarksurface29 = *dmarksurface2;
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (ver. BSP2rmq <-> BSP2)
* =========================================================================
*/
static bsp2_dnode_t *BSP2rmqto2_Nodes(const bsp2rmq_dnode_t *dnodes2rmq, int numnodes)
{
const bsp2rmq_dnode_t *dnode2rmq = dnodes2rmq;
bsp2_dnode_t *newdata, *dnode2;
int i, j;
newdata = dnode2 = new bsp2_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnode2rmq++, dnode2++) {
dnode2->planenum = dnode2rmq->planenum;
dnode2->children[0] = dnode2rmq->children[0];
dnode2->children[1] = dnode2rmq->children[1];
for (j = 0; j < 3; j++) {
dnode2->mins[j] = dnode2rmq->mins[j];
dnode2->maxs[j] = dnode2rmq->maxs[j];
}
dnode2->firstface = dnode2rmq->firstface;
dnode2->numfaces = dnode2rmq->numfaces;
}
return newdata;
}
static bsp2rmq_dnode_t *BSP2to2rmq_Nodes(const bsp2_dnode_t *dnodes2, int numnodes)
{
const bsp2_dnode_t *dnode2 = dnodes2;
bsp2rmq_dnode_t *newdata, *dnode2rmq;
int i, j;
newdata = dnode2rmq = new bsp2rmq_dnode_t[numnodes];
for (i = 0; i < numnodes; i++, dnode2++, dnode2rmq++) {
dnode2rmq->planenum = dnode2->planenum;
dnode2rmq->children[0] = dnode2->children[0];
dnode2rmq->children[1] = dnode2->children[1];
for (j = 0; j < 3; j++) {
dnode2rmq->mins[j] = dnode2->mins[j];
dnode2rmq->maxs[j] = dnode2->maxs[j];
}
dnode2rmq->firstface = dnode2->firstface;
dnode2rmq->numfaces = dnode2->numfaces;
}
return newdata;
}
/*
* =========================================================================
* BSP Format Conversion (no-ops)
* =========================================================================
*/
template<typename T>
inline T *CopyArray(const T *in, int numelems)
{
T *out = new T[numelems];
if constexpr(std::is_trivially_copyable_v<T>)
memcpy(out, in, sizeof(T) * numelems);
else
for (int i = 0; i < numelems; i++)
out[i] = in[i];
return out;
}
// TODO: if T and F are scalar values, overflow should be checked.
template<typename T, typename F, typename bool = std::is_convertible_v<T, F>>
inline T *CopyArray(const F *in, int numelems)
{
T *out = new T[numelems];
for (int i = 0; i < numelems; i++)
out[i] = static_cast<T>(in[i]);
return out;
}
static dmodelh2_t *H2_CopyModels(const dmodelh2_t *dmodels, int nummodels)
{
return CopyArray(dmodels, nummodels);
}
static uint8_t *BSP29_CopyVisData(const uint8_t *dvisdata, int visdatasize)
{
return CopyArray(dvisdata, visdatasize);
}
static uint8_t *BSP29_CopyLightData(const uint8_t *dlightdata, int lightdatasize)
{
return CopyArray(dlightdata, lightdatasize);
}
static dmiptexlump_t *BSP29_CopyTexData(const dmiptexlump_t *dtexdata, int texdatasize)
{
return CopyArray(dtexdata, texdatasize);
}
static char *BSP29_CopyEntData(const char *dentdata, int entdatasize)
{
return CopyArray(dentdata, entdatasize);
}
static dplane_t *BSP29_CopyPlanes(const dplane_t *dplanes, int numplanes)
{
return CopyArray(dplanes, numplanes);
}
static dvertex_t *BSP29_CopyVertexes(const dvertex_t *dvertexes, int numvertexes)
{
return CopyArray(dvertexes, numvertexes);
}
static texinfo_t *BSP29_CopyTexinfo(const texinfo_t *texinfo, int numtexinfo)
{
return CopyArray(texinfo, numtexinfo);
}
static int32_t *BSP29_CopySurfedges(const int32_t *surfedges, int numsurfedges)
{
return CopyArray(surfedges, numsurfedges);
}
static bsp2_dface_t *BSP2_CopyFaces(const bsp2_dface_t *dfaces, int numfaces)
{
return CopyArray(dfaces, numfaces);
}
static bsp2_dclipnode_t *BSP2_CopyClipnodes(const bsp2_dclipnode_t *dclipnodes, int numclipnodes)
{
return CopyArray(dclipnodes, numclipnodes);
}
static bsp2_dedge_t *BSP2_CopyEdges(const bsp2_dedge_t *dedges, int numedges)
{
return CopyArray(dedges, numedges);
}
static uint32_t *BSP2_CopyMarksurfaces(const uint32_t *marksurfaces, int nummarksurfaces)
{
return CopyArray(marksurfaces, nummarksurfaces);
}
static bsp2_dnode_t *BSP2_CopyNodes(const bsp2_dnode_t *dnodes, int numnodes)
{
return CopyArray(dnodes, numnodes);
}
static uint32_t *Q2BSPtoM_CopyLeafBrushes(const uint16_t *leafbrushes, int count)
{
return CopyArray<uint32_t>(leafbrushes, count);
}
static uint16_t *MBSPtoQ2_CopyLeafBrushes(const uint32_t *leafbrushes, int count)
{
return CopyArray<uint16_t>(leafbrushes, count);
}
static uint32_t *Q2BSP_Qbism_CopyLeafBrushes(const uint32_t *leafbrushes, int count)
{
return CopyArray(leafbrushes, count);
}
static darea_t *Q2BSP_CopyAreas(const darea_t *areas, int count)
{
return CopyArray(areas, count);
}
static dareaportal_t *Q2BSP_CopyAreaPortals(const dareaportal_t *areaportals, int count)
{
return CopyArray(areaportals, count);
}
static dbrush_t *Q2BSP_CopyBrushes(const dbrush_t *brushes, int count)
{
return CopyArray(brushes, count);
}
static q2_dbrushside_qbism_t *Q2BSPtoM_CopyBrushSides(const dbrushside_t *dbrushsides, int count)
{
const dbrushside_t *brushside = dbrushsides;
q2_dbrushside_qbism_t *newdata, *brushsides2;
int i;
newdata = brushsides2 = new q2_dbrushside_qbism_t[count];
for (i = 0; i < count; i++, brushside++, brushsides2++) {
brushsides2->planenum = brushside->planenum;
brushsides2->texinfo = brushside->texinfo;
}
return newdata;
}
static q2_dbrushside_qbism_t *Q2BSP_Qbism_CopyBrushSides(const q2_dbrushside_qbism_t *brushsides, int count)
{
return CopyArray(brushsides, count);
}
static dbrushside_t *MBSPtoQ2_CopyBrushSides(const q2_dbrushside_qbism_t *dbrushsides, int count)
{
const q2_dbrushside_qbism_t *brushside = dbrushsides;
dbrushside_t *newdata, *brushsides2;
int i;
newdata = brushsides2 = new dbrushside_t[count];
for (i = 0; i < count; i++, brushside++, brushsides2++) {
brushsides2->planenum = brushside->planenum;
brushsides2->texinfo = brushside->texinfo;
}
return newdata;
}
/*
* =========================================================================
* ConvertBSPFormat
* - BSP is assumed to be in CPU byte order already
* - No checks are done here (yet) for overflow of values when down-converting
* =========================================================================
*/
bool ConvertBSPFormat(bspdata_t *bspdata, const bspversion_t *to_version)
{
if (bspdata->version == to_version)
return true;
if (to_version == &bspver_generic) {
// Conversions to bspver_generic
// NOTE: these always succeed
mbsp_t mbsp { };
if (std::holds_alternative<bsp29_t>(bspdata->bsp)) {
// bspver_q1, bspver_h2, bspver_hl -> bspver_generic
const bsp29_t &bsp29 = std::get<bsp29_t>(bspdata->bsp);
// copy counts
mbsp.nummodels = bsp29.nummodels;
mbsp.visdatasize = bsp29.visdatasize;
mbsp.lightdatasize = bsp29.lightdatasize;
mbsp.texdatasize = bsp29.texdatasize;
mbsp.entdatasize = bsp29.entdatasize;
mbsp.numleafs = bsp29.numleafs;
mbsp.numplanes = bsp29.numplanes;
mbsp.numvertexes = bsp29.numvertexes;
mbsp.numnodes = bsp29.numnodes;
mbsp.numtexinfo = bsp29.numtexinfo;
mbsp.numfaces = bsp29.numfaces;
mbsp.numclipnodes = bsp29.numclipnodes;
mbsp.numedges = bsp29.numedges;
mbsp.numleaffaces = bsp29.nummarksurfaces;
mbsp.numsurfedges = bsp29.numsurfedges;
// copy or convert data
if (bspdata->version == &bspver_h2) {
mbsp.dmodels = H2_CopyModels(bsp29.dmodels_h2, bsp29.nummodels);
} else {
mbsp.dmodels = BSPQ1toH2_Models(bsp29.dmodels_q, bsp29.nummodels);
}
mbsp.dvisdata = BSP29_CopyVisData(bsp29.dvisdata, bsp29.visdatasize);
mbsp.dlightdata = BSP29_CopyLightData(bsp29.dlightdata, bsp29.lightdatasize);
mbsp.dtexdata = BSP29_CopyTexData(bsp29.dtexdata, bsp29.texdatasize);
mbsp.dentdata = BSP29_CopyEntData(bsp29.dentdata, bsp29.entdatasize);
mbsp.dleafs = BSP29toM_Leafs(bsp29.dleafs, bsp29.numleafs);
mbsp.dplanes = BSP29_CopyPlanes(bsp29.dplanes, bsp29.numplanes);
mbsp.dvertexes = BSP29_CopyVertexes(bsp29.dvertexes, bsp29.numvertexes);
mbsp.dnodes = BSP29to2_Nodes(bsp29.dnodes, bsp29.numnodes);
mbsp.texinfo = BSP29toM_Texinfo(bsp29.texinfo, bsp29.numtexinfo);
mbsp.dfaces = BSP29to2_Faces(bsp29.dfaces, bsp29.numfaces);
mbsp.dclipnodes = BSP29to2_Clipnodes(bsp29.dclipnodes, bsp29.numclipnodes);
mbsp.dedges = BSP29to2_Edges(bsp29.dedges, bsp29.numedges);
mbsp.dleaffaces = BSP29to2_Marksurfaces(bsp29.dmarksurfaces, bsp29.nummarksurfaces);
mbsp.dsurfedges = BSP29_CopySurfedges(bsp29.dsurfedges, bsp29.numsurfedges);
} else if (std::holds_alternative<q2bsp_t>(bspdata->bsp)) {
// bspver_q2 -> bspver_generic
const q2bsp_t &q2bsp = std::get<q2bsp_t>(bspdata->bsp);
// copy counts
mbsp.nummodels = q2bsp.nummodels;
mbsp.visdatasize = q2bsp.visdatasize;
mbsp.lightdatasize = q2bsp.lightdatasize;
mbsp.entdatasize = q2bsp.entdatasize;
mbsp.numleafs = q2bsp.numleafs;
mbsp.numplanes = q2bsp.numplanes;
mbsp.numvertexes = q2bsp.numvertexes;
mbsp.numnodes = q2bsp.numnodes;
mbsp.numtexinfo = q2bsp.numtexinfo;
mbsp.numfaces = q2bsp.numfaces;
mbsp.numedges = q2bsp.numedges;
mbsp.numleaffaces = q2bsp.numleaffaces;
mbsp.numleafbrushes = q2bsp.numleafbrushes;
mbsp.numsurfedges = q2bsp.numsurfedges;
mbsp.numareas = q2bsp.numareas;
mbsp.numareaportals = q2bsp.numareaportals;
mbsp.numbrushes = q2bsp.numbrushes;
mbsp.numbrushsides = q2bsp.numbrushsides;
// copy or convert data
mbsp.dmodels = Q2BSPtoM_Models(q2bsp.dmodels, q2bsp.nummodels);
mbsp.dlightdata = BSP29_CopyLightData(q2bsp.dlightdata, q2bsp.lightdatasize);
mbsp.dentdata = BSP29_CopyEntData(q2bsp.dentdata, q2bsp.entdatasize);
mbsp.dleafs = Q2BSPtoM_Leafs(q2bsp.dleafs, q2bsp.numleafs);
mbsp.dplanes = BSP29_CopyPlanes(q2bsp.dplanes, q2bsp.numplanes);
mbsp.dvertexes = BSP29_CopyVertexes(q2bsp.dvertexes, q2bsp.numvertexes);
mbsp.dnodes = Q2BSPto2_Nodes(q2bsp.dnodes, q2bsp.numnodes);
mbsp.texinfo = Q2BSPtoM_Texinfo(q2bsp.texinfo, q2bsp.numtexinfo);
mbsp.dfaces = Q2BSPto2_Faces(q2bsp.dfaces, q2bsp.numfaces);
mbsp.dedges = BSP29to2_Edges(q2bsp.dedges, q2bsp.numedges);
mbsp.dleaffaces = BSP29to2_Marksurfaces(q2bsp.dleaffaces, q2bsp.numleaffaces);
mbsp.dleafbrushes = Q2BSPtoM_CopyLeafBrushes(q2bsp.dleafbrushes, q2bsp.numleafbrushes);
mbsp.dsurfedges = BSP29_CopySurfedges(q2bsp.dsurfedges, q2bsp.numsurfedges);
mbsp.dvisdata =
Q2BSPtoM_CopyVisData(q2bsp.dvis, q2bsp.visdatasize, &mbsp.visdatasize, mbsp.dleafs, mbsp.numleafs);
mbsp.dareas = Q2BSP_CopyAreas(q2bsp.dareas, q2bsp.numareas);
mbsp.dareaportals = Q2BSP_CopyAreaPortals(q2bsp.dareaportals, q2bsp.numareaportals);
mbsp.dbrushes = Q2BSP_CopyBrushes(q2bsp.dbrushes, q2bsp.numbrushes);
mbsp.dbrushsides = Q2BSPtoM_CopyBrushSides(q2bsp.dbrushsides, q2bsp.numbrushsides);
} else if (std::holds_alternative<q2bsp_qbism_t>(bspdata->bsp)) {
// bspver_qbism -> bspver_generic
const q2bsp_qbism_t &q2bsp = std::get<q2bsp_qbism_t>(bspdata->bsp);
// copy counts
mbsp.nummodels = q2bsp.nummodels;
mbsp.visdatasize = q2bsp.visdatasize;
mbsp.lightdatasize = q2bsp.lightdatasize;
mbsp.entdatasize = q2bsp.entdatasize;
mbsp.numleafs = q2bsp.numleafs;
mbsp.numplanes = q2bsp.numplanes;
mbsp.numvertexes = q2bsp.numvertexes;
mbsp.numnodes = q2bsp.numnodes;
mbsp.numtexinfo = q2bsp.numtexinfo;
mbsp.numfaces = q2bsp.numfaces;
mbsp.numedges = q2bsp.numedges;
mbsp.numleaffaces = q2bsp.numleaffaces;
mbsp.numleafbrushes = q2bsp.numleafbrushes;
mbsp.numsurfedges = q2bsp.numsurfedges;
mbsp.numareas = q2bsp.numareas;
mbsp.numareaportals = q2bsp.numareaportals;
mbsp.numbrushes = q2bsp.numbrushes;
mbsp.numbrushsides = q2bsp.numbrushsides;
// copy or convert data
mbsp.dmodels = Q2BSPtoM_Models(q2bsp.dmodels, q2bsp.nummodels);
mbsp.dlightdata = BSP29_CopyLightData(q2bsp.dlightdata, q2bsp.lightdatasize);
mbsp.dentdata = BSP29_CopyEntData(q2bsp.dentdata, q2bsp.entdatasize);
mbsp.dleafs = Q2BSP_QBSPtoM_Leafs(q2bsp.dleafs, q2bsp.numleafs);
mbsp.dplanes = BSP29_CopyPlanes(q2bsp.dplanes, q2bsp.numplanes);
mbsp.dvertexes = BSP29_CopyVertexes(q2bsp.dvertexes, q2bsp.numvertexes);
mbsp.dnodes = BSP2_CopyNodes(q2bsp.dnodes, q2bsp.numnodes);
mbsp.texinfo = Q2BSPtoM_Texinfo(q2bsp.texinfo, q2bsp.numtexinfo);
mbsp.dfaces = Q2BSP_QBSPto2_Faces(q2bsp.dfaces, q2bsp.numfaces);
mbsp.dedges = BSP2_CopyEdges(q2bsp.dedges, q2bsp.numedges);
mbsp.dleaffaces = BSP2_CopyMarksurfaces(q2bsp.dleaffaces, q2bsp.numleaffaces);
mbsp.dleafbrushes = Q2BSP_Qbism_CopyLeafBrushes(q2bsp.dleafbrushes, q2bsp.numleafbrushes);
mbsp.dsurfedges = BSP29_CopySurfedges(q2bsp.dsurfedges, q2bsp.numsurfedges);
mbsp.dvisdata =
Q2BSPtoM_CopyVisData(q2bsp.dvis, q2bsp.visdatasize, &mbsp.visdatasize, mbsp.dleafs, mbsp.numleafs);
mbsp.dareas = Q2BSP_CopyAreas(q2bsp.dareas, q2bsp.numareas);
mbsp.dareaportals = Q2BSP_CopyAreaPortals(q2bsp.dareaportals, q2bsp.numareaportals);
mbsp.dbrushes = Q2BSP_CopyBrushes(q2bsp.dbrushes, q2bsp.numbrushes);
mbsp.dbrushsides = Q2BSP_Qbism_CopyBrushSides(q2bsp.dbrushsides, q2bsp.numbrushsides);
} else if (std::holds_alternative<bsp2rmq_t>(bspdata->bsp)) {
// bspver_bsp2rmq, bspver_h2bsp2rmq -> bspver_generic
const bsp2rmq_t &bsp2rmq = std::get<bsp2rmq_t>(bspdata->bsp);
// copy counts
mbsp.nummodels = bsp2rmq.nummodels;
mbsp.visdatasize = bsp2rmq.visdatasize;
mbsp.lightdatasize = bsp2rmq.lightdatasize;
mbsp.texdatasize = bsp2rmq.texdatasize;
mbsp.entdatasize = bsp2rmq.entdatasize;
mbsp.numleafs = bsp2rmq.numleafs;
mbsp.numplanes = bsp2rmq.numplanes;
mbsp.numvertexes = bsp2rmq.numvertexes;
mbsp.numnodes = bsp2rmq.numnodes;
mbsp.numtexinfo = bsp2rmq.numtexinfo;
mbsp.numfaces = bsp2rmq.numfaces;
mbsp.numclipnodes = bsp2rmq.numclipnodes;
mbsp.numedges = bsp2rmq.numedges;
mbsp.numleaffaces = bsp2rmq.nummarksurfaces;
mbsp.numsurfedges = bsp2rmq.numsurfedges;
// copy or convert data
if (bspdata->version == &bspver_h2bsp2rmq) {
mbsp.dmodels = H2_CopyModels(bsp2rmq.dmodels_h2, bsp2rmq.nummodels);
} else {
mbsp.dmodels = BSPQ1toH2_Models(bsp2rmq.dmodels_q, bsp2rmq.nummodels);
}
mbsp.dvisdata = BSP29_CopyVisData(bsp2rmq.dvisdata, bsp2rmq.visdatasize);
mbsp.dlightdata = BSP29_CopyLightData(bsp2rmq.dlightdata, bsp2rmq.lightdatasize);
mbsp.dtexdata = BSP29_CopyTexData(bsp2rmq.dtexdata, bsp2rmq.texdatasize);
mbsp.dentdata = BSP29_CopyEntData(bsp2rmq.dentdata, bsp2rmq.entdatasize);
mbsp.dleafs = BSP2rmqtoM_Leafs(bsp2rmq.dleafs, bsp2rmq.numleafs);
mbsp.dplanes = BSP29_CopyPlanes(bsp2rmq.dplanes, bsp2rmq.numplanes);
mbsp.dvertexes = BSP29_CopyVertexes(bsp2rmq.dvertexes, bsp2rmq.numvertexes);
mbsp.dnodes = BSP2rmqto2_Nodes(bsp2rmq.dnodes, bsp2rmq.numnodes);
mbsp.texinfo = BSP29toM_Texinfo(bsp2rmq.texinfo, bsp2rmq.numtexinfo);
mbsp.dfaces = BSP2_CopyFaces(bsp2rmq.dfaces, bsp2rmq.numfaces);
mbsp.dclipnodes = BSP2_CopyClipnodes(bsp2rmq.dclipnodes, bsp2rmq.numclipnodes);
mbsp.dedges = BSP2_CopyEdges(bsp2rmq.dedges, bsp2rmq.numedges);
mbsp.dleaffaces = BSP2_CopyMarksurfaces(bsp2rmq.dmarksurfaces, bsp2rmq.nummarksurfaces);
mbsp.dsurfedges = BSP29_CopySurfedges(bsp2rmq.dsurfedges, bsp2rmq.numsurfedges);
} else if (std::holds_alternative<bsp2_t>(bspdata->bsp)) {
// bspver_bsp2, bspver_h2bsp2 -> bspver_generic
const bsp2_t &bsp2 = std::get<bsp2_t>(bspdata->bsp);
// copy counts
mbsp.nummodels = bsp2.nummodels;
mbsp.visdatasize = bsp2.visdatasize;
mbsp.lightdatasize = bsp2.lightdatasize;
mbsp.texdatasize = bsp2.texdatasize;
mbsp.entdatasize = bsp2.entdatasize;
mbsp.numleafs = bsp2.numleafs;
mbsp.numplanes = bsp2.numplanes;
mbsp.numvertexes = bsp2.numvertexes;
mbsp.numnodes = bsp2.numnodes;
mbsp.numtexinfo = bsp2.numtexinfo;
mbsp.numfaces = bsp2.numfaces;
mbsp.numclipnodes = bsp2.numclipnodes;
mbsp.numedges = bsp2.numedges;
mbsp.numleaffaces = bsp2.nummarksurfaces;
mbsp.numsurfedges = bsp2.numsurfedges;
// copy or convert data
if (bspdata->version == &bspver_h2bsp2) {
mbsp.dmodels = H2_CopyModels(bsp2.dmodels_h2, bsp2.nummodels);
} else {
mbsp.dmodels = BSPQ1toH2_Models(bsp2.dmodels_q, bsp2.nummodels);
}
mbsp.dvisdata = BSP29_CopyVisData(bsp2.dvisdata, bsp2.visdatasize);
mbsp.dlightdata = BSP29_CopyLightData(bsp2.dlightdata, bsp2.lightdatasize);
mbsp.dtexdata = BSP29_CopyTexData(bsp2.dtexdata, bsp2.texdatasize);
mbsp.dentdata = BSP29_CopyEntData(bsp2.dentdata, bsp2.entdatasize);
mbsp.dleafs = BSP2toM_Leafs(bsp2.dleafs, bsp2.numleafs);
mbsp.dplanes = BSP29_CopyPlanes(bsp2.dplanes, bsp2.numplanes);
mbsp.dvertexes = BSP29_CopyVertexes(bsp2.dvertexes, bsp2.numvertexes);
mbsp.dnodes = BSP2_CopyNodes(bsp2.dnodes, bsp2.numnodes);
mbsp.texinfo = BSP29toM_Texinfo(bsp2.texinfo, bsp2.numtexinfo);
mbsp.dfaces = BSP2_CopyFaces(bsp2.dfaces, bsp2.numfaces);
mbsp.dclipnodes = BSP2_CopyClipnodes(bsp2.dclipnodes, bsp2.numclipnodes);
mbsp.dedges = BSP2_CopyEdges(bsp2.dedges, bsp2.numedges);
mbsp.dleaffaces = BSP2_CopyMarksurfaces(bsp2.dmarksurfaces, bsp2.nummarksurfaces);
mbsp.dsurfedges = BSP29_CopySurfedges(bsp2.dsurfedges, bsp2.numsurfedges);
} else {
return false;
}
bspdata->loadversion = mbsp.loadversion = bspdata->version;
bspdata->version = to_version;
bspdata->bsp = std::move(mbsp);
return true;
} else if (bspdata->version == &bspver_generic) {
// Conversions from bspver_generic
const mbsp_t &mbsp = std::get<mbsp_t>(bspdata->bsp);
if (to_version == &bspver_q1 || to_version == &bspver_h2 || to_version == &bspver_hl) {
// bspver_generic -> bspver_q1, bspver_h2, bspver_hl
bsp29_t bsp29 { };
// validate that the conversion is possible
if (!MBSPto29_Leafs_Validate(mbsp.dleafs, mbsp.numleafs)) {
return false;
}
if (!BSP2to29_Nodes_Validate(mbsp.dnodes, mbsp.numnodes)) {
return false;
}
if (!BSP2to29_Faces_Validate(mbsp.dfaces, mbsp.numfaces)) {
return false;
}
if (!BSP2to29_Clipnodes_Validate(mbsp.dclipnodes, mbsp.numclipnodes)) {
return false;
}
if (!BSP2to29_Edges_Validate(mbsp.dedges, mbsp.numedges)) {
return false;
}
if (!BSP2to29_Marksurfaces_Validate(mbsp.dleaffaces, mbsp.numleaffaces)) {
return false;
}
// copy counts
bsp29.nummodels = mbsp.nummodels;
bsp29.visdatasize = mbsp.visdatasize;
bsp29.lightdatasize = mbsp.lightdatasize;
bsp29.texdatasize = mbsp.texdatasize;
bsp29.entdatasize = mbsp.entdatasize;
bsp29.numleafs = mbsp.numleafs;
bsp29.numplanes = mbsp.numplanes;
bsp29.numvertexes = mbsp.numvertexes;
bsp29.numnodes = mbsp.numnodes;
bsp29.numtexinfo = mbsp.numtexinfo;
bsp29.numfaces = mbsp.numfaces;
bsp29.numclipnodes = mbsp.numclipnodes;
bsp29.numedges = mbsp.numedges;
bsp29.nummarksurfaces = mbsp.numleaffaces;
bsp29.numsurfedges = mbsp.numsurfedges;
// copy or convert data
if (to_version == &bspver_h2) {
bsp29.dmodels_h2 = H2_CopyModels(mbsp.dmodels, mbsp.nummodels);
} else {
bsp29.dmodels_q = BSPH2toQ1_Models(mbsp.dmodels, mbsp.nummodels);
}
bsp29.dvisdata = BSP29_CopyVisData(mbsp.dvisdata, mbsp.visdatasize);
bsp29.dlightdata = BSP29_CopyLightData(mbsp.dlightdata, mbsp.lightdatasize);
bsp29.dtexdata = BSP29_CopyTexData(mbsp.dtexdata, mbsp.texdatasize);
bsp29.dentdata = BSP29_CopyEntData(mbsp.dentdata, mbsp.entdatasize);
bsp29.dleafs = MBSPto29_Leafs(mbsp.dleafs, mbsp.numleafs);
bsp29.dplanes = BSP29_CopyPlanes(mbsp.dplanes, mbsp.numplanes);
bsp29.dvertexes = BSP29_CopyVertexes(mbsp.dvertexes, mbsp.numvertexes);
bsp29.dnodes = BSP2to29_Nodes(mbsp.dnodes, mbsp.numnodes);
bsp29.texinfo = MBSPto29_Texinfo(mbsp.texinfo, mbsp.numtexinfo);
bsp29.dfaces = BSP2to29_Faces(mbsp.dfaces, mbsp.numfaces);
bsp29.dclipnodes = BSP2to29_Clipnodes(mbsp.dclipnodes, mbsp.numclipnodes);
bsp29.dedges = BSP2to29_Edges(mbsp.dedges, mbsp.numedges);
bsp29.dmarksurfaces = BSP2to29_Marksurfaces(mbsp.dleaffaces, mbsp.numleaffaces);
bsp29.dsurfedges = BSP29_CopySurfedges(mbsp.dsurfedges, mbsp.numsurfedges);
/* Conversion complete! */
bspdata->version = to_version;
bspdata->bsp = std::move(bsp29);
return true;
} else if (to_version == &bspver_q2) {
// bspver_generic -> bspver_q2
q2bsp_t q2bsp { };
// copy counts
q2bsp.nummodels = mbsp.nummodels;
q2bsp.visdatasize = mbsp.visdatasize;
q2bsp.lightdatasize = mbsp.lightdatasize;
q2bsp.entdatasize = mbsp.entdatasize;
q2bsp.numleafs = mbsp.numleafs;
q2bsp.numplanes = mbsp.numplanes;
q2bsp.numvertexes = mbsp.numvertexes;
q2bsp.numnodes = mbsp.numnodes;
q2bsp.numtexinfo = mbsp.numtexinfo;
q2bsp.numfaces = mbsp.numfaces;
q2bsp.numedges = mbsp.numedges;
q2bsp.numleaffaces = mbsp.numleaffaces;
q2bsp.numleafbrushes = mbsp.numleafbrushes;
q2bsp.numsurfedges = mbsp.numsurfedges;
q2bsp.numareas = mbsp.numareas;
q2bsp.numareaportals = mbsp.numareaportals;
q2bsp.numbrushes = mbsp.numbrushes;
q2bsp.numbrushsides = mbsp.numbrushsides;
// copy or convert data
q2bsp.dmodels = MBSPtoQ2_Models(mbsp.dmodels, mbsp.nummodels);
q2bsp.dvis = MBSPtoQ2_CopyVisData(mbsp.dvisdata, &q2bsp.visdatasize, mbsp.numleafs, mbsp.dleafs);
q2bsp.dlightdata = BSP29_CopyLightData(mbsp.dlightdata, mbsp.lightdatasize);
q2bsp.dentdata = BSP29_CopyEntData(mbsp.dentdata, mbsp.entdatasize);
q2bsp.dleafs = MBSPtoQ2_Leafs(mbsp.dleafs, mbsp.numleafs);
q2bsp.dplanes = BSP29_CopyPlanes(mbsp.dplanes, mbsp.numplanes);
q2bsp.dvertexes = BSP29_CopyVertexes(mbsp.dvertexes, mbsp.numvertexes);
q2bsp.dnodes = BSP2toQ2_Nodes(mbsp.dnodes, mbsp.numnodes);
q2bsp.texinfo = MBSPtoQ2_Texinfo(mbsp.texinfo, mbsp.numtexinfo);
q2bsp.dfaces = BSP2toQ2_Faces(mbsp.dfaces, mbsp.numfaces);
q2bsp.dedges = BSP2to29_Edges(mbsp.dedges, mbsp.numedges);
q2bsp.dleaffaces = BSP2to29_Marksurfaces(mbsp.dleaffaces, mbsp.numleaffaces);
q2bsp.dleafbrushes = MBSPtoQ2_CopyLeafBrushes(mbsp.dleafbrushes, mbsp.numleafbrushes);
q2bsp.dsurfedges = BSP29_CopySurfedges(mbsp.dsurfedges, mbsp.numsurfedges);
q2bsp.dareas = Q2BSP_CopyAreas(mbsp.dareas, mbsp.numareas);
q2bsp.dareaportals = Q2BSP_CopyAreaPortals(mbsp.dareaportals, mbsp.numareaportals);
q2bsp.dbrushes = Q2BSP_CopyBrushes(mbsp.dbrushes, mbsp.numbrushes);
q2bsp.dbrushsides = MBSPtoQ2_CopyBrushSides(mbsp.dbrushsides, mbsp.numbrushsides);
/* Conversion complete! */
bspdata->version = to_version;
bspdata->bsp = std::move(q2bsp);
return true;
} else if (to_version == &bspver_qbism) {
// bspver_generic -> bspver_qbism
q2bsp_qbism_t q2bsp { };
// copy counts
q2bsp.nummodels = mbsp.nummodels;
q2bsp.visdatasize = mbsp.visdatasize;
q2bsp.lightdatasize = mbsp.lightdatasize;
q2bsp.entdatasize = mbsp.entdatasize;
q2bsp.numleafs = mbsp.numleafs;
q2bsp.numplanes = mbsp.numplanes;
q2bsp.numvertexes = mbsp.numvertexes;
q2bsp.numnodes = mbsp.numnodes;
q2bsp.numtexinfo = mbsp.numtexinfo;
q2bsp.numfaces = mbsp.numfaces;
q2bsp.numedges = mbsp.numedges;
q2bsp.numleaffaces = mbsp.numleaffaces;
q2bsp.numleafbrushes = mbsp.numleafbrushes;
q2bsp.numsurfedges = mbsp.numsurfedges;
q2bsp.numareas = mbsp.numareas;
q2bsp.numareaportals = mbsp.numareaportals;
q2bsp.numbrushes = mbsp.numbrushes;
q2bsp.numbrushsides = mbsp.numbrushsides;
// copy or convert data
q2bsp.dmodels = MBSPtoQ2_Models(mbsp.dmodels, mbsp.nummodels);
q2bsp.dvis = MBSPtoQ2_CopyVisData(mbsp.dvisdata, &q2bsp.visdatasize, mbsp.numleafs, mbsp.dleafs);
q2bsp.dlightdata = BSP29_CopyLightData(mbsp.dlightdata, mbsp.lightdatasize);
q2bsp.dentdata = BSP29_CopyEntData(mbsp.dentdata, mbsp.entdatasize);
q2bsp.dleafs = MBSPtoQ2_Qbism_Leafs(mbsp.dleafs, mbsp.numleafs);
q2bsp.dplanes = BSP29_CopyPlanes(mbsp.dplanes, mbsp.numplanes);
q2bsp.dvertexes = BSP29_CopyVertexes(mbsp.dvertexes, mbsp.numvertexes);
q2bsp.dnodes = BSP2_CopyNodes(mbsp.dnodes, mbsp.numnodes);
q2bsp.texinfo = MBSPtoQ2_Texinfo(mbsp.texinfo, mbsp.numtexinfo);
q2bsp.dfaces = BSP2toQ2_Qbism_Faces(mbsp.dfaces, mbsp.numfaces);
q2bsp.dedges = BSP2_CopyEdges(mbsp.dedges, mbsp.numedges);
q2bsp.dleaffaces = BSP2_CopyMarksurfaces(mbsp.dleaffaces, mbsp.numleaffaces);
q2bsp.dleafbrushes = Q2BSP_Qbism_CopyLeafBrushes(mbsp.dleafbrushes, mbsp.numleafbrushes);
q2bsp.dsurfedges = BSP29_CopySurfedges(mbsp.dsurfedges, mbsp.numsurfedges);
q2bsp.dareas = Q2BSP_CopyAreas(mbsp.dareas, mbsp.numareas);
q2bsp.dareaportals = Q2BSP_CopyAreaPortals(mbsp.dareaportals, mbsp.numareaportals);
q2bsp.dbrushes = Q2BSP_CopyBrushes(mbsp.dbrushes, mbsp.numbrushes);
q2bsp.dbrushsides = Q2BSP_Qbism_CopyBrushSides(mbsp.dbrushsides, mbsp.numbrushsides);
/* Conversion complete! */
bspdata->version = to_version;
bspdata->bsp = std::move(q2bsp);
return true;
} else if (to_version == &bspver_bsp2rmq || to_version == &bspver_h2bsp2rmq) {
// bspver_generic -> bspver_bsp2rmq, bspver_h2bsp2rmq
bsp2rmq_t bsp2rmq { };
// copy counts
bsp2rmq.nummodels = mbsp.nummodels;
bsp2rmq.visdatasize = mbsp.visdatasize;
bsp2rmq.lightdatasize = mbsp.lightdatasize;
bsp2rmq.texdatasize = mbsp.texdatasize;
bsp2rmq.entdatasize = mbsp.entdatasize;
bsp2rmq.numleafs = mbsp.numleafs;
bsp2rmq.numplanes = mbsp.numplanes;
bsp2rmq.numvertexes = mbsp.numvertexes;
bsp2rmq.numnodes = mbsp.numnodes;
bsp2rmq.numtexinfo = mbsp.numtexinfo;
bsp2rmq.numfaces = mbsp.numfaces;
bsp2rmq.numclipnodes = mbsp.numclipnodes;
bsp2rmq.numedges = mbsp.numedges;
bsp2rmq.nummarksurfaces = mbsp.numleaffaces;
bsp2rmq.numsurfedges = mbsp.numsurfedges;
// copy or convert data
if (to_version == &bspver_h2bsp2rmq) {
bsp2rmq.dmodels_h2 = H2_CopyModels(mbsp.dmodels, mbsp.nummodels);
} else {
bsp2rmq.dmodels_q = BSPH2toQ1_Models(mbsp.dmodels, mbsp.nummodels);
}
bsp2rmq.dvisdata = BSP29_CopyVisData(mbsp.dvisdata, mbsp.visdatasize);
bsp2rmq.dlightdata = BSP29_CopyLightData(mbsp.dlightdata, mbsp.lightdatasize);
bsp2rmq.dtexdata = BSP29_CopyTexData(mbsp.dtexdata, mbsp.texdatasize);
bsp2rmq.dentdata = BSP29_CopyEntData(mbsp.dentdata, mbsp.entdatasize);
bsp2rmq.dleafs = MBSPto2rmq_Leafs(mbsp.dleafs, mbsp.numleafs);
bsp2rmq.dplanes = BSP29_CopyPlanes(mbsp.dplanes, mbsp.numplanes);
bsp2rmq.dvertexes = BSP29_CopyVertexes(mbsp.dvertexes, mbsp.numvertexes);
bsp2rmq.dnodes = BSP2to2rmq_Nodes(mbsp.dnodes, mbsp.numnodes);
bsp2rmq.texinfo = MBSPto29_Texinfo(mbsp.texinfo, mbsp.numtexinfo);
bsp2rmq.dfaces = BSP2_CopyFaces(mbsp.dfaces, mbsp.numfaces);
bsp2rmq.dclipnodes = BSP2_CopyClipnodes(mbsp.dclipnodes, mbsp.numclipnodes);
bsp2rmq.dedges = BSP2_CopyEdges(mbsp.dedges, mbsp.numedges);
bsp2rmq.dmarksurfaces = BSP2_CopyMarksurfaces(mbsp.dleaffaces, mbsp.numleaffaces);
bsp2rmq.dsurfedges = BSP29_CopySurfedges(mbsp.dsurfedges, mbsp.numsurfedges);
/* Conversion complete! */
bspdata->version = to_version;
bspdata->bsp = std::move(bsp2rmq);
return true;
} else if (to_version == &bspver_bsp2 || to_version == &bspver_h2bsp2) {
// bspver_generic -> bspver_bsp2, bspver_h2bsp2
bsp2_t bsp2 { };
// copy counts
bsp2.nummodels = mbsp.nummodels;
bsp2.visdatasize = mbsp.visdatasize;
bsp2.lightdatasize = mbsp.lightdatasize;
bsp2.texdatasize = mbsp.texdatasize;
bsp2.entdatasize = mbsp.entdatasize;
bsp2.numleafs = mbsp.numleafs;
bsp2.numplanes = mbsp.numplanes;
bsp2.numvertexes = mbsp.numvertexes;
bsp2.numnodes = mbsp.numnodes;
bsp2.numtexinfo = mbsp.numtexinfo;
bsp2.numfaces = mbsp.numfaces;
bsp2.numclipnodes = mbsp.numclipnodes;
bsp2.numedges = mbsp.numedges;
bsp2.nummarksurfaces = mbsp.numleaffaces;
bsp2.numsurfedges = mbsp.numsurfedges;
// copy or convert data
if (to_version == &bspver_h2bsp2) {
bsp2.dmodels_h2 = H2_CopyModels(mbsp.dmodels, mbsp.nummodels);
} else {
bsp2.dmodels_q = BSPH2toQ1_Models(mbsp.dmodels, mbsp.nummodels);
}
bsp2.dvisdata = BSP29_CopyVisData(mbsp.dvisdata, mbsp.visdatasize);
bsp2.dlightdata = BSP29_CopyLightData(mbsp.dlightdata, mbsp.lightdatasize);
bsp2.dtexdata = BSP29_CopyTexData(mbsp.dtexdata, mbsp.texdatasize);
bsp2.dentdata = BSP29_CopyEntData(mbsp.dentdata, mbsp.entdatasize);
bsp2.dleafs = MBSPto2_Leafs(mbsp.dleafs, mbsp.numleafs);
bsp2.dplanes = BSP29_CopyPlanes(mbsp.dplanes, mbsp.numplanes);
bsp2.dvertexes = BSP29_CopyVertexes(mbsp.dvertexes, mbsp.numvertexes);
bsp2.dnodes = BSP2_CopyNodes(mbsp.dnodes, mbsp.numnodes);
bsp2.texinfo = MBSPto29_Texinfo(mbsp.texinfo, mbsp.numtexinfo);
bsp2.dfaces = BSP2_CopyFaces(mbsp.dfaces, mbsp.numfaces);
bsp2.dclipnodes = BSP2_CopyClipnodes(mbsp.dclipnodes, mbsp.numclipnodes);
bsp2.dedges = BSP2_CopyEdges(mbsp.dedges, mbsp.numedges);
bsp2.dmarksurfaces = BSP2_CopyMarksurfaces(mbsp.dleaffaces, mbsp.numleaffaces);
bsp2.dsurfedges = BSP29_CopySurfedges(mbsp.dsurfedges, mbsp.numsurfedges);
/* Conversion complete! */
bspdata->version = to_version;
bspdata->bsp = std::move(bsp2);
return true;
}
}
Error("Don't know how to convert BSP version {} to {}", BSPVersionString(bspdata->version),
BSPVersionString(to_version));
}
static int isHexen2(const dheader_t *header)
{
/*
the world should always have some face.
however, if the sizes are wrong then we're actually reading headnode[6]. hexen2 only used 5 hulls, so this
should be 0 in hexen2, and not in quake.
*/
const dmodelq1_t *modelsq1 = (const dmodelq1_t *)((const uint8_t *)header + header->lumps[LUMP_MODELS].fileofs);
return !modelsq1->numfaces;
}
/*
* =========================================================================
* ...
* =========================================================================
*/
const lumpspec_t lumpspec_bsp29[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp29_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp29_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp29_dclipnode_t)},
{"leafs", sizeof(bsp29_dleaf_t)},
{"marksurfaces", sizeof(uint16_t)},
{"edges", sizeof(bsp29_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelq1_t)},
};
const lumpspec_t lumpspec_bsp2rmq[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp2rmq_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp2_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp2_dclipnode_t)},
{"leafs", sizeof(bsp2rmq_dleaf_t)},
{"marksurfaces", sizeof(uint32_t)},
{"edges", sizeof(bsp2_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelq1_t)},
};
const lumpspec_t lumpspec_bsp2[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp2_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp2_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp2_dclipnode_t)},
{"leafs", sizeof(bsp2_dleaf_t)},
{"marksurfaces", sizeof(uint32_t)},
{"edges", sizeof(bsp2_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelq1_t)},
};
const lumpspec_t lumpspec_bsp29_h2[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp29_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp29_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp29_dclipnode_t)},
{"leafs", sizeof(bsp29_dleaf_t)},
{"marksurfaces", sizeof(uint16_t)},
{"edges", sizeof(bsp29_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelh2_t)},
};
const lumpspec_t lumpspec_bsp2rmq_h2[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp2rmq_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp2_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp2_dclipnode_t)},
{"leafs", sizeof(bsp2rmq_dleaf_t)},
{"marksurfaces", sizeof(uint32_t)},
{"edges", sizeof(bsp2_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelh2_t)},
};
const lumpspec_t lumpspec_bsp2_h2[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"texture", sizeof(uint8_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(bsp2_dnode_t)},
{"texinfos", sizeof(texinfo_t)},
{"faces", sizeof(bsp2_dface_t)},
{"lighting", sizeof(uint8_t)},
{"clipnodes", sizeof(bsp2_dclipnode_t)},
{"leafs", sizeof(bsp2_dleaf_t)},
{"marksurfaces", sizeof(uint32_t)},
{"edges", sizeof(bsp2_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(dmodelh2_t)},
};
const lumpspec_t lumpspec_q2bsp[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(q2_dnode_t)},
{"texinfos", sizeof(q2_texinfo_t)},
{"faces", sizeof(q2_dface_t)},
{"lighting", sizeof(uint8_t)},
{"leafs", sizeof(q2_dleaf_t)},
{"leaffaces", sizeof(uint16_t)},
{"leafbrushes", sizeof(uint16_t)},
{"edges", sizeof(bsp29_dedge_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(q2_dmodel_t)},
{"brushes", sizeof(dbrush_t)},
{"brushsides", sizeof(dbrushside_t)},
{"pop", sizeof(uint8_t)},
{"areas", sizeof(darea_t)},
{"areaportals", sizeof(dareaportal_t)},
};
const lumpspec_t lumpspec_qbism[] = {
{"entities", sizeof(char)},
{"planes", sizeof(dplane_t)},
{"vertexes", sizeof(dvertex_t)},
{"visibility", sizeof(uint8_t)},
{"nodes", sizeof(q2_dnode_qbism_t)},
{"texinfos", sizeof(q2_texinfo_t)},
{"faces", sizeof(q2_dface_qbism_t)},
{"lighting", sizeof(uint8_t)},
{"leafs", sizeof(q2_dleaf_qbism_t)},
{"leaffaces", sizeof(uint32_t)},
{"leafbrushes", sizeof(uint32_t)},
{"edges", sizeof(q2_dedge_qbism_t)},
{"surfedges", sizeof(int32_t)},
{"models", sizeof(q2_dmodel_t)},
{"brushes", sizeof(dbrush_t)},
{"brushsides", sizeof(q2_dbrushside_qbism_t)},
{"pop", sizeof(uint8_t)},
{"areas", sizeof(darea_t)},
{"areaportals", sizeof(dareaportal_t)},
};
static const lumpspec_t *LumpspecsForVersion(const bspversion_t *version)
{
const lumpspec_t *lumpspec;
if (version == &bspver_q1 || version == &bspver_hl) {
lumpspec = lumpspec_bsp29;
} else if (version == &bspver_bsp2rmq) {
lumpspec = lumpspec_bsp2rmq;
} else if (version == &bspver_bsp2) {
lumpspec = lumpspec_bsp2;
} else if (version == &bspver_h2) {
lumpspec = lumpspec_bsp29_h2;
} else if (version == &bspver_h2bsp2rmq) {
lumpspec = lumpspec_bsp2rmq_h2;
} else if (version == &bspver_h2bsp2) {
lumpspec = lumpspec_bsp2_h2;
} else if (version == &bspver_q2) {
lumpspec = lumpspec_q2bsp;
} else if (version == &bspver_qbism) {
lumpspec = lumpspec_qbism;
} else {
Error("Unsupported BSP version: {}", BSPVersionString(version));
}
return lumpspec;
}
template<typename T>
static int CopyLump(const void *header, const bspversion_t *version, const lump_t *lumps, int lumpnum, T **bufferptr)
{
const lumpspec_t *lumpspecs = LumpspecsForVersion(version);
const lumpspec_t *lumpspec = &lumpspecs[lumpnum];
int length = lumps[lumpnum].filelen;
int ofs = lumps[lumpnum].fileofs;
if (*bufferptr)
delete[] *bufferptr;
if (sizeof(T) != lumpspec->size || length % lumpspec->size)
FError("odd {} lump size", lumpspec->name);
T *buffer;
if constexpr(std::is_same_v<T, char>)
buffer = *bufferptr = new T[length + 1];
else
buffer = *bufferptr = new T[length];
if (!buffer)
FError("allocation of {} bytes failed.", length);
memcpy(buffer, (const uint8_t *)header + ofs, length);
if constexpr(std::is_same_v<T, char>)
buffer[length] = 0; /* In case of corrupt entity lump */
return length / lumpspec->size;
}
void BSPX_AddLump(bspdata_t *bspdata, const char *xname, const void *xdata, size_t xsize)
{
bspxentry_t *e;
bspxentry_t **link;
if (!xdata) {
for (link = &bspdata->bspxentries; *link;) {
e = *link;
if (!strcmp(e->lumpname, xname)) {
*link = e->next;
delete e;
break;
} else
link = &(*link)->next;
}
return;
}
for (e = bspdata->bspxentries; e; e = e->next) {
if (!strcmp(e->lumpname, xname))
break;
}
if (!e) {
e = new bspxentry_t { };
strncpy(e->lumpname, xname, sizeof(e->lumpname));
e->next = bspdata->bspxentries;
bspdata->bspxentries = e;
}
// ericw -- make a copy
uint8_t *xdata_copy = new uint8_t[xsize];
memcpy(xdata_copy, xdata, xsize);
e->lumpdata = xdata_copy;
e->lumpsize = xsize;
}
const void *BSPX_GetLump(bspdata_t *bspdata, const char *xname, size_t *xsize)
{
bspxentry_t *e;
for (e = bspdata->bspxentries; e; e = e->next) {
if (!strcmp(e->lumpname, xname))
break;
}
if (e) {
if (xsize)
*xsize = e->lumpsize;
return e->lumpdata;
} else {
if (xsize)
*xsize = 0;
return NULL;
}
}
/*
* =============
* LoadBSPFile
* =============
*/
void LoadBSPFile(std::filesystem::path &filename, bspdata_t *bspdata)
{
int i;
uint32_t bspxofs;
const bspx_header_t *bspx;
bspdata->bspxentries = NULL;
FLogPrint("'{}'\n", filename);
/* load the file header */
uint8_t *file_data;
uint32_t flen = LoadFilePak(filename, &file_data);
/* transfer the header data to these variables */
int numlumps;
lump_t *lumps;
/* check for IBSP */
bspversion_t temp_version{LittleLong(((int *)file_data)[0])};
if (temp_version.ident == Q2_BSPIDENT || temp_version.ident == Q2_QBISMIDENT) {
q2_dheader_t *q2header = (q2_dheader_t *)file_data;
q2header->version = LittleLong(q2header->version);
numlumps = Q2_HEADER_LUMPS;
temp_version.version = q2header->version;
lumps = q2header->lumps;
} else {
dheader_t *q1header = (dheader_t *)file_data;
q1header->version = LittleLong(q1header->version);
numlumps = BSP_LUMPS;
lumps = q1header->lumps;
// not useful for Q1BSP, but we'll initialize it to -1
temp_version.version = NO_VERSION;
}
/* check the file version */
if (!BSPVersionSupported(temp_version.ident, temp_version.version, &bspdata->version)) {
LogPrint("BSP is version {}\n", BSPVersionString(&temp_version));
Error("Sorry, this bsp version is not supported.");
} else {
// special case handling for Hexen II
if (isHexen2((dheader_t *)file_data)) {
if (bspdata->version == &bspver_q1) {
bspdata->version = &bspver_h2;
} else if (bspdata->version == &bspver_bsp2) {
bspdata->version = &bspver_h2bsp2;
} else if (bspdata->version == &bspver_bsp2rmq) {
bspdata->version = &bspver_h2bsp2rmq;
}
}
LogPrint("BSP is version {}\n", BSPVersionString(bspdata->version));
}
/* swap the lump headers */
for (i = 0; i < numlumps; i++) {
lumps[i].fileofs = LittleLong(lumps[i].fileofs);
lumps[i].filelen = LittleLong(lumps[i].filelen);
}
/* copy the data */
if (bspdata->version == &bspver_q2) {
q2_dheader_t *header = (q2_dheader_t *)file_data;
q2bsp_t bsp { };
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_MODELS, &bsp.dmodels);
bsp.numvertexes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_VERTEXES, &bsp.dvertexes);
bsp.numplanes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_PLANES, &bsp.dplanes);
bsp.numleafs = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFS, &bsp.dleafs);
bsp.numnodes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_NODES, &bsp.dnodes);
bsp.numtexinfo = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_TEXINFO, &bsp.texinfo);
bsp.numfaces = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_FACES, &bsp.dfaces);
bsp.numleaffaces = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFFACES, &bsp.dleaffaces);
bsp.numleafbrushes =
CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFBRUSHES, &bsp.dleafbrushes);
bsp.numsurfedges = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_SURFEDGES, &bsp.dsurfedges);
bsp.numedges = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_EDGES, &bsp.dedges);
bsp.numbrushes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_BRUSHES, &bsp.dbrushes);
bsp.numbrushsides = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_BRUSHSIDES, &bsp.dbrushsides);
bsp.numareas = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_AREAS, &bsp.dareas);
bsp.numareaportals =
CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_AREAPORTALS, &bsp.dareaportals);
bsp.visdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_VISIBILITY, &bsp.dvisdata);
bsp.lightdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LIGHTING, &bsp.dlightdata);
bsp.entdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_ENTITIES, &bsp.dentdata);
bspdata->bsp = std::move(bsp);
} else if (bspdata->version == &bspver_qbism) {
q2_dheader_t *header = (q2_dheader_t *)file_data;
q2bsp_qbism_t bsp { };
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_MODELS, &bsp.dmodels);
bsp.numvertexes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_VERTEXES, &bsp.dvertexes);
bsp.numplanes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_PLANES, &bsp.dplanes);
bsp.numleafs = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFS, &bsp.dleafs);
bsp.numnodes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_NODES, &bsp.dnodes);
bsp.numtexinfo = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_TEXINFO, &bsp.texinfo);
bsp.numfaces = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_FACES, &bsp.dfaces);
bsp.numleaffaces = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFFACES, &bsp.dleaffaces);
bsp.numleafbrushes =
CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LEAFBRUSHES, &bsp.dleafbrushes);
bsp.numsurfedges = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_SURFEDGES, &bsp.dsurfedges);
bsp.numedges = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_EDGES, &bsp.dedges);
bsp.numbrushes = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_BRUSHES, &bsp.dbrushes);
bsp.numbrushsides = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_BRUSHSIDES, &bsp.dbrushsides);
bsp.numareas = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_AREAS, &bsp.dareas);
bsp.numareaportals =
CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_AREAPORTALS, &bsp.dareaportals);
bsp.visdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_VISIBILITY, &bsp.dvisdata);
bsp.lightdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_LIGHTING, &bsp.dlightdata);
bsp.entdatasize = CopyLump(header, bspdata->version, header->lumps, Q2_LUMP_ENTITIES, &bsp.dentdata);
bspdata->bsp = std::move(bsp);
} else if (bspdata->version == &bspver_q1 || bspdata->version == &bspver_h2 || bspdata->version == &bspver_hl) {
dheader_t *header = (dheader_t *)file_data;
bsp29_t bsp { };
if (bspdata->version == &bspver_h2) {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_h2);
} else {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_q);
}
bsp.numvertexes = CopyLump(header, bspdata->version, header->lumps, LUMP_VERTEXES, &bsp.dvertexes);
bsp.numplanes = CopyLump(header, bspdata->version, header->lumps, LUMP_PLANES, &bsp.dplanes);
bsp.numleafs = CopyLump(header, bspdata->version, header->lumps, LUMP_LEAFS, &bsp.dleafs);
bsp.numnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_NODES, &bsp.dnodes);
bsp.numtexinfo = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXINFO, &bsp.texinfo);
bsp.numclipnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_CLIPNODES, &bsp.dclipnodes);
bsp.numfaces = CopyLump(header, bspdata->version, header->lumps, LUMP_FACES, &bsp.dfaces);
bsp.nummarksurfaces =
CopyLump(header, bspdata->version, header->lumps, LUMP_MARKSURFACES, &bsp.dmarksurfaces);
bsp.numsurfedges = CopyLump(header, bspdata->version, header->lumps, LUMP_SURFEDGES, &bsp.dsurfedges);
bsp.numedges = CopyLump(header, bspdata->version, header->lumps, LUMP_EDGES, &bsp.dedges);
bsp.texdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXTURES, &bsp.dtexdata);
bsp.visdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_VISIBILITY, &bsp.dvisdata);
bsp.lightdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_LIGHTING, &bsp.dlightdata);
bsp.entdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_ENTITIES, &bsp.dentdata);
bspdata->bsp = std::move(bsp);
} else if (bspdata->version == &bspver_bsp2rmq || bspdata->version == &bspver_h2bsp2rmq) {
dheader_t *header = (dheader_t *)file_data;
bsp2rmq_t bsp { };
if (bspdata->version == &bspver_h2bsp2rmq) {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_h2);
} else {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_q);
}
bsp.numvertexes = CopyLump(header, bspdata->version, header->lumps, LUMP_VERTEXES, &bsp.dvertexes);
bsp.numplanes = CopyLump(header, bspdata->version, header->lumps, LUMP_PLANES, &bsp.dplanes);
bsp.numleafs = CopyLump(header, bspdata->version, header->lumps, LUMP_LEAFS, &bsp.dleafs);
bsp.numnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_NODES, &bsp.dnodes);
bsp.numtexinfo = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXINFO, &bsp.texinfo);
bsp.numclipnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_CLIPNODES, &bsp.dclipnodes);
bsp.numfaces = CopyLump(header, bspdata->version, header->lumps, LUMP_FACES, &bsp.dfaces);
bsp.nummarksurfaces =
CopyLump(header, bspdata->version, header->lumps, LUMP_MARKSURFACES, &bsp.dmarksurfaces);
bsp.numsurfedges = CopyLump(header, bspdata->version, header->lumps, LUMP_SURFEDGES, &bsp.dsurfedges);
bsp.numedges = CopyLump(header, bspdata->version, header->lumps, LUMP_EDGES, &bsp.dedges);
bsp.texdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXTURES, &bsp.dtexdata);
bsp.visdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_VISIBILITY, &bsp.dvisdata);
bsp.lightdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_LIGHTING, &bsp.dlightdata);
bsp.entdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_ENTITIES, &bsp.dentdata);
bspdata->bsp = std::move(bsp);
} else if (bspdata->version == &bspver_bsp2 || bspdata->version == &bspver_h2bsp2) {
dheader_t *header = (dheader_t *)file_data;
bsp2_t bsp { };
if (bspdata->version == &bspver_h2bsp2) {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_h2);
} else {
bsp.nummodels = CopyLump(header, bspdata->version, header->lumps, LUMP_MODELS, &bsp.dmodels_q);
}
bsp.numvertexes = CopyLump(header, bspdata->version, header->lumps, LUMP_VERTEXES, &bsp.dvertexes);
bsp.numplanes = CopyLump(header, bspdata->version, header->lumps, LUMP_PLANES, &bsp.dplanes);
bsp.numleafs = CopyLump(header, bspdata->version, header->lumps, LUMP_LEAFS, &bsp.dleafs);
bsp.numnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_NODES, &bsp.dnodes);
bsp.numtexinfo = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXINFO, &bsp.texinfo);
bsp.numclipnodes = CopyLump(header, bspdata->version, header->lumps, LUMP_CLIPNODES, &bsp.dclipnodes);
bsp.numfaces = CopyLump(header, bspdata->version, header->lumps, LUMP_FACES, &bsp.dfaces);
bsp.nummarksurfaces =
CopyLump(header, bspdata->version, header->lumps, LUMP_MARKSURFACES, &bsp.dmarksurfaces);
bsp.numsurfedges = CopyLump(header, bspdata->version, header->lumps, LUMP_SURFEDGES, &bsp.dsurfedges);
bsp.numedges = CopyLump(header, bspdata->version, header->lumps, LUMP_EDGES, &bsp.dedges);
bsp.texdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_TEXTURES, &bsp.dtexdata);
bsp.visdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_VISIBILITY, &bsp.dvisdata);
bsp.lightdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_LIGHTING, &bsp.dlightdata);
bsp.entdatasize = CopyLump(header, bspdata->version, header->lumps, LUMP_ENTITIES, &bsp.dentdata);
bspdata->bsp = std::move(bsp);
} else {
FError("Unknown format");
}
// detect BSPX
dheader_t *header = (dheader_t *)file_data;
/*bspx header is positioned exactly+4align at the end of the last lump position (regardless of order)*/
for (i = 0, bspxofs = 0; i < BSP_LUMPS; i++) {
if (bspxofs < header->lumps[i].fileofs + header->lumps[i].filelen)
bspxofs = header->lumps[i].fileofs + header->lumps[i].filelen;
}
bspxofs = (bspxofs + 3) & ~3;
/*okay, so that's where it *should* be if it exists */
if (bspxofs + sizeof(*bspx) <= flen) {
int xlumps;
const bspx_lump_t *xlump;
bspx = (const bspx_header_t *)((const uint8_t *)header + bspxofs);
xlump = (const bspx_lump_t *)(bspx + 1);
xlumps = LittleLong(bspx->numlumps);
if (!memcmp(&bspx->id, "BSPX", 4) && xlumps >= 0 && bspxofs + sizeof(*bspx) + sizeof(*xlump) * xlumps <= flen) {
/*header seems valid so far. just add the lumps as we normally would if we were generating them, ensuring
* that they get written out anew*/
while (xlumps-- > 0) {
uint32_t ofs = LittleLong(xlump[xlumps].fileofs);
uint32_t len = LittleLong(xlump[xlumps].filelen);
void *lumpdata = new uint8_t[len];
memcpy(lumpdata, (const uint8_t *)header + ofs, len);
BSPX_AddLump(bspdata, xlump[xlumps].lumpname, lumpdata, len);
}
} else {
if (!memcmp(&bspx->id, "BSPX", 4))
printf("invalid bspx header\n");
}
}
/* everything has been copied out */
delete[] file_data;
/* swap everything */
SwapBSPFile(bspdata, TO_CPU);
}
/* ========================================================================= */
struct bspfile_t
{
const bspversion_t *version;
// which one is used depends on version
union
{
dheader_t q1header;
q2_dheader_t q2header;
};
qfile_t file { nullptr, nullptr };
};
static void AddLump(bspfile_t *bspfile, int lumpnum, const void *data, int count)
{
bool q2 = false;
size_t size;
const lumpspec_t *lumpspecs = LumpspecsForVersion(bspfile->version);
const lumpspec_t *lumpspec = &lumpspecs[lumpnum];
lump_t *lumps;
if (bspfile->version->version != NO_VERSION) {
lumps = bspfile->q2header.lumps;
} else {
lumps = bspfile->q1header.lumps;
}
size = lumpspec->size * count;
uint8_t pad[4] = {0};
lump_t *lump = &lumps[lumpnum];
lump->fileofs = LittleLong(SafeTell(bspfile->file));
lump->filelen = LittleLong(size);
SafeWrite(bspfile->file, data, size);
if (size % 4)
SafeWrite(bspfile->file, pad, 4 - (size % 4));
}
/*
* =============
* WriteBSPFile
* Swaps the bsp file in place, so it should not be referenced again
* =============
*/
void WriteBSPFile(const std::filesystem::path &filename, bspdata_t *bspdata)
{
bspfile_t bspfile { };
SwapBSPFile(bspdata, TO_DISK);
bspfile.version = bspdata->version;
// headers are union'd, so this sets both
bspfile.q2header.ident = LittleLong(bspfile.version->ident);
if (bspfile.version->version != NO_VERSION) {
bspfile.q2header.version = LittleLong(bspfile.version->version);
}
LogPrint("Writing {} as BSP version {}\n", filename, BSPVersionString(bspdata->version));
bspfile.file = SafeOpenWrite(filename);
/* Save header space, updated after adding the lumps */
if (bspfile.version->version != NO_VERSION) {
SafeWrite(bspfile.file, &bspfile.q2header, sizeof(bspfile.q2header));
} else {
SafeWrite(bspfile.file, &bspfile.q1header, sizeof(bspfile.q1header));
}
if (std::holds_alternative<bsp29_t>(bspdata->bsp)) {
const bsp29_t &bsp = std::get<bsp29_t>(bspdata->bsp);
AddLump(&bspfile, LUMP_PLANES, bsp.dplanes, bsp.numplanes);
AddLump(&bspfile, LUMP_LEAFS, bsp.dleafs, bsp.numleafs);
AddLump(&bspfile, LUMP_VERTEXES, bsp.dvertexes, bsp.numvertexes);
AddLump(&bspfile, LUMP_NODES, bsp.dnodes, bsp.numnodes);
AddLump(&bspfile, LUMP_TEXINFO, bsp.texinfo, bsp.numtexinfo);
AddLump(&bspfile, LUMP_FACES, bsp.dfaces, bsp.numfaces);
AddLump(&bspfile, LUMP_CLIPNODES, bsp.dclipnodes, bsp.numclipnodes);
AddLump(&bspfile, LUMP_MARKSURFACES, bsp.dmarksurfaces, bsp.nummarksurfaces);
AddLump(&bspfile, LUMP_SURFEDGES, bsp.dsurfedges, bsp.numsurfedges);
AddLump(&bspfile, LUMP_EDGES, bsp.dedges, bsp.numedges);
if (bspdata->version == &bspver_h2) {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_h2, bsp.nummodels);
} else {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_q, bsp.nummodels);
}
AddLump(&bspfile, LUMP_LIGHTING, bsp.dlightdata, bsp.lightdatasize);
AddLump(&bspfile, LUMP_VISIBILITY, bsp.dvisdata, bsp.visdatasize);
AddLump(&bspfile, LUMP_ENTITIES, bsp.dentdata, bsp.entdatasize);
AddLump(&bspfile, LUMP_TEXTURES, bsp.dtexdata, bsp.texdatasize);
} else if (std::holds_alternative<bsp2rmq_t>(bspdata->bsp)) {
const bsp2rmq_t &bsp = std::get<bsp2rmq_t>(bspdata->bsp);
AddLump(&bspfile, LUMP_PLANES, bsp.dplanes, bsp.numplanes);
AddLump(&bspfile, LUMP_LEAFS, bsp.dleafs, bsp.numleafs);
AddLump(&bspfile, LUMP_VERTEXES, bsp.dvertexes, bsp.numvertexes);
AddLump(&bspfile, LUMP_NODES, bsp.dnodes, bsp.numnodes);
AddLump(&bspfile, LUMP_TEXINFO, bsp.texinfo, bsp.numtexinfo);
AddLump(&bspfile, LUMP_FACES, bsp.dfaces, bsp.numfaces);
AddLump(&bspfile, LUMP_CLIPNODES, bsp.dclipnodes, bsp.numclipnodes);
AddLump(&bspfile, LUMP_MARKSURFACES, bsp.dmarksurfaces, bsp.nummarksurfaces);
AddLump(&bspfile, LUMP_SURFEDGES, bsp.dsurfedges, bsp.numsurfedges);
AddLump(&bspfile, LUMP_EDGES, bsp.dedges, bsp.numedges);
if (bspdata->version == &bspver_h2bsp2rmq) {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_h2, bsp.nummodels);
} else {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_q, bsp.nummodels);
}
AddLump(&bspfile, LUMP_LIGHTING, bsp.dlightdata, bsp.lightdatasize);
AddLump(&bspfile, LUMP_VISIBILITY, bsp.dvisdata, bsp.visdatasize);
AddLump(&bspfile, LUMP_ENTITIES, bsp.dentdata, bsp.entdatasize);
AddLump(&bspfile, LUMP_TEXTURES, bsp.dtexdata, bsp.texdatasize);
} else if (std::holds_alternative<bsp2_t>(bspdata->bsp)) {
const bsp2_t &bsp = std::get<bsp2_t>(bspdata->bsp);
AddLump(&bspfile, LUMP_PLANES, bsp.dplanes, bsp.numplanes);
AddLump(&bspfile, LUMP_LEAFS, bsp.dleafs, bsp.numleafs);
AddLump(&bspfile, LUMP_VERTEXES, bsp.dvertexes, bsp.numvertexes);
AddLump(&bspfile, LUMP_NODES, bsp.dnodes, bsp.numnodes);
AddLump(&bspfile, LUMP_TEXINFO, bsp.texinfo, bsp.numtexinfo);
AddLump(&bspfile, LUMP_FACES, bsp.dfaces, bsp.numfaces);
AddLump(&bspfile, LUMP_CLIPNODES, bsp.dclipnodes, bsp.numclipnodes);
AddLump(&bspfile, LUMP_MARKSURFACES, bsp.dmarksurfaces, bsp.nummarksurfaces);
AddLump(&bspfile, LUMP_SURFEDGES, bsp.dsurfedges, bsp.numsurfedges);
AddLump(&bspfile, LUMP_EDGES, bsp.dedges, bsp.numedges);
if (bspdata->version == &bspver_h2bsp2) {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_h2, bsp.nummodels);
} else {
AddLump(&bspfile, LUMP_MODELS, bsp.dmodels_q, bsp.nummodels);
}
AddLump(&bspfile, LUMP_LIGHTING, bsp.dlightdata, bsp.lightdatasize);
AddLump(&bspfile, LUMP_VISIBILITY, bsp.dvisdata, bsp.visdatasize);
AddLump(&bspfile, LUMP_ENTITIES, bsp.dentdata, bsp.entdatasize);
AddLump(&bspfile, LUMP_TEXTURES, bsp.dtexdata, bsp.texdatasize);
} else if (std::holds_alternative<q2bsp_t>(bspdata->bsp)) {
const q2bsp_t &bsp = std::get<q2bsp_t>(bspdata->bsp);
AddLump(&bspfile, Q2_LUMP_MODELS, bsp.dmodels, bsp.nummodels);
AddLump(&bspfile, Q2_LUMP_VERTEXES, bsp.dvertexes, bsp.numvertexes);
AddLump(&bspfile, Q2_LUMP_PLANES, bsp.dplanes, bsp.numplanes);
AddLump(&bspfile, Q2_LUMP_LEAFS, bsp.dleafs, bsp.numleafs);
AddLump(&bspfile, Q2_LUMP_NODES, bsp.dnodes, bsp.numnodes);
AddLump(&bspfile, Q2_LUMP_TEXINFO, bsp.texinfo, bsp.numtexinfo);
AddLump(&bspfile, Q2_LUMP_FACES, bsp.dfaces, bsp.numfaces);
AddLump(&bspfile, Q2_LUMP_LEAFFACES, bsp.dleaffaces, bsp.numleaffaces);
AddLump(&bspfile, Q2_LUMP_LEAFBRUSHES, bsp.dleafbrushes, bsp.numleafbrushes);
AddLump(&bspfile, Q2_LUMP_SURFEDGES, bsp.dsurfedges, bsp.numsurfedges);
AddLump(&bspfile, Q2_LUMP_EDGES, bsp.dedges, bsp.numedges);
AddLump(&bspfile, Q2_LUMP_BRUSHES, bsp.dbrushes, bsp.numbrushes);
AddLump(&bspfile, Q2_LUMP_BRUSHSIDES, bsp.dbrushsides, bsp.numbrushsides);
AddLump(&bspfile, Q2_LUMP_AREAS, bsp.dareas, bsp.numareas);
AddLump(&bspfile, Q2_LUMP_AREAPORTALS, bsp.dareaportals, bsp.numareaportals);
AddLump(&bspfile, Q2_LUMP_VISIBILITY, bsp.dvis, bsp.visdatasize);
AddLump(&bspfile, Q2_LUMP_LIGHTING, bsp.dlightdata, bsp.lightdatasize);
AddLump(&bspfile, Q2_LUMP_ENTITIES, bsp.dentdata, bsp.entdatasize);
AddLump(&bspfile, Q2_LUMP_POP, bsp.dpop, sizeof(bsp.dpop));
} else if (std::holds_alternative<q2bsp_qbism_t>(bspdata->bsp)) {
const q2bsp_qbism_t &bsp = std::get<q2bsp_qbism_t>(bspdata->bsp);
AddLump(&bspfile, Q2_LUMP_MODELS, bsp.dmodels, bsp.nummodels);
AddLump(&bspfile, Q2_LUMP_VERTEXES, bsp.dvertexes, bsp.numvertexes);
AddLump(&bspfile, Q2_LUMP_PLANES, bsp.dplanes, bsp.numplanes);
AddLump(&bspfile, Q2_LUMP_LEAFS, bsp.dleafs, bsp.numleafs);
AddLump(&bspfile, Q2_LUMP_NODES, bsp.dnodes, bsp.numnodes);
AddLump(&bspfile, Q2_LUMP_TEXINFO, bsp.texinfo, bsp.numtexinfo);
AddLump(&bspfile, Q2_LUMP_FACES, bsp.dfaces, bsp.numfaces);
AddLump(&bspfile, Q2_LUMP_LEAFFACES, bsp.dleaffaces, bsp.numleaffaces);
AddLump(&bspfile, Q2_LUMP_LEAFBRUSHES, bsp.dleafbrushes, bsp.numleafbrushes);
AddLump(&bspfile, Q2_LUMP_SURFEDGES, bsp.dsurfedges, bsp.numsurfedges);
AddLump(&bspfile, Q2_LUMP_EDGES, bsp.dedges, bsp.numedges);
AddLump(&bspfile, Q2_LUMP_BRUSHES, bsp.dbrushes, bsp.numbrushes);
AddLump(&bspfile, Q2_LUMP_BRUSHSIDES, bsp.dbrushsides, bsp.numbrushsides);
AddLump(&bspfile, Q2_LUMP_AREAS, bsp.dareas, bsp.numareas);
AddLump(&bspfile, Q2_LUMP_AREAPORTALS, bsp.dareaportals, bsp.numareaportals);
AddLump(&bspfile, Q2_LUMP_VISIBILITY, bsp.dvis, bsp.visdatasize);
AddLump(&bspfile, Q2_LUMP_LIGHTING, bsp.dlightdata, bsp.lightdatasize);
AddLump(&bspfile, Q2_LUMP_ENTITIES, bsp.dentdata, bsp.entdatasize);
AddLump(&bspfile, Q2_LUMP_POP, bsp.dpop, sizeof(bsp.dpop));
} else {
FError("Unknown format");
}
/*BSPX lumps are at a 4-byte alignment after the last of any official lump*/
if (bspdata->bspxentries) {
bspx_header_t xheader;
bspxentry_t *x;
bspx_lump_t xlumps[64];
uint32_t l;
long bspxheader = SafeTell(bspfile.file);
if (bspxheader & 3)
FError("BSPX header is misaligned");
xheader.id[0] = 'B';
xheader.id[1] = 'S';
xheader.id[2] = 'P';
xheader.id[3] = 'X';
xheader.numlumps = 0;
for (x = bspdata->bspxentries; x; x = x->next)
xheader.numlumps++;
if (xheader.numlumps > sizeof(xlumps) / sizeof(xlumps[0])) /*eep*/
xheader.numlumps = sizeof(xlumps) / sizeof(xlumps[0]);
SafeWrite(bspfile.file, &xheader, sizeof(xheader));
SafeWrite(bspfile.file, xlumps, xheader.numlumps * sizeof(xlumps[0]));
for (x = bspdata->bspxentries, l = 0; x && l < xheader.numlumps; x = x->next, l++) {
uint8_t pad[4] = {0};
xlumps[l].filelen = LittleLong(x->lumpsize);
xlumps[l].fileofs = LittleLong(SafeTell(bspfile.file));
strncpy(xlumps[l].lumpname, x->lumpname, sizeof(xlumps[l].lumpname));
SafeWrite(bspfile.file, x->lumpdata, x->lumpsize);
if (x->lumpsize % 4)
SafeWrite(bspfile.file, pad, 4 - (x->lumpsize % 4));
}
SafeSeek(bspfile.file, bspxheader, SEEK_SET);
SafeWrite(bspfile.file, &xheader, sizeof(xheader));
SafeWrite(bspfile.file, xlumps, xheader.numlumps * sizeof(xlumps[0]));
}
SafeSeek(bspfile.file, 0, SEEK_SET);
// write the real header
if (bspfile.version->version != NO_VERSION) {
SafeWrite(bspfile.file, &bspfile.q2header, sizeof(bspfile.q2header));
} else {
SafeWrite(bspfile.file, &bspfile.q1header, sizeof(bspfile.q1header));
}
}
/* ========================================================================= */
static void PrintLumpSize(const lumpspec_t *lumpspec, int lumptype, int count)
{
const lumpspec_t *lump = &lumpspec[lumptype];
LogPrint("{:7} {:<12} {:10}\n", count, lump->name, count * (int)lump->size);
}
/*
* =============
* PrintBSPFileSizes
* Dumps info about the bsp data
* =============
*/
void PrintBSPFileSizes(const bspdata_t *bspdata)
{
int numtextures = 0;
const lumpspec_t *lumpspec = LumpspecsForVersion(bspdata->version);
if (std::holds_alternative<q2bsp_t>(bspdata->bsp)) {
const q2bsp_t &bsp = std::get<q2bsp_t>(bspdata->bsp);
LogPrint("{:7} {:<12}\n", bsp.nummodels, "models");
PrintLumpSize(lumpspec, Q2_LUMP_PLANES, bsp.numplanes);
PrintLumpSize(lumpspec, Q2_LUMP_VERTEXES, bsp.numvertexes);
PrintLumpSize(lumpspec, Q2_LUMP_NODES, bsp.numnodes);
PrintLumpSize(lumpspec, Q2_LUMP_TEXINFO, bsp.numtexinfo);
PrintLumpSize(lumpspec, Q2_LUMP_FACES, bsp.numfaces);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFS, bsp.numleafs);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFFACES, bsp.numleaffaces);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFBRUSHES, bsp.numleafbrushes);
PrintLumpSize(lumpspec, Q2_LUMP_EDGES, bsp.numedges);
PrintLumpSize(lumpspec, Q2_LUMP_SURFEDGES, bsp.numsurfedges);
PrintLumpSize(lumpspec, Q2_LUMP_BRUSHES, bsp.numbrushes);
PrintLumpSize(lumpspec, Q2_LUMP_BRUSHSIDES, bsp.numbrushsides);
PrintLumpSize(lumpspec, Q2_LUMP_AREAS, bsp.numareas);
PrintLumpSize(lumpspec, Q2_LUMP_AREAPORTALS, bsp.numareaportals);
LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.lightdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.visdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "entdata", bsp.entdatasize);
} else if (std::holds_alternative<q2bsp_qbism_t>(bspdata->bsp)) {
const q2bsp_qbism_t &bsp = std::get<q2bsp_qbism_t>(bspdata->bsp);
LogPrint("{:7} {:<12}\n", bsp.nummodels, "models");
PrintLumpSize(lumpspec, Q2_LUMP_PLANES, bsp.numplanes);
PrintLumpSize(lumpspec, Q2_LUMP_VERTEXES, bsp.numvertexes);
PrintLumpSize(lumpspec, Q2_LUMP_NODES, bsp.numnodes);
PrintLumpSize(lumpspec, Q2_LUMP_TEXINFO, bsp.numtexinfo);
PrintLumpSize(lumpspec, Q2_LUMP_FACES, bsp.numfaces);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFS, bsp.numleafs);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFFACES, bsp.numleaffaces);
PrintLumpSize(lumpspec, Q2_LUMP_LEAFBRUSHES, bsp.numleafbrushes);
PrintLumpSize(lumpspec, Q2_LUMP_EDGES, bsp.numedges);
PrintLumpSize(lumpspec, Q2_LUMP_SURFEDGES, bsp.numsurfedges);
PrintLumpSize(lumpspec, Q2_LUMP_BRUSHES, bsp.numbrushes);
PrintLumpSize(lumpspec, Q2_LUMP_BRUSHSIDES, bsp.numbrushsides);
PrintLumpSize(lumpspec, Q2_LUMP_AREAS, bsp.numareas);
PrintLumpSize(lumpspec, Q2_LUMP_AREAPORTALS, bsp.numareaportals);
LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.lightdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.visdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "entdata", bsp.entdatasize);
} else if (std::holds_alternative<bsp29_t>(bspdata->bsp)) {
const bsp29_t &bsp = std::get<bsp29_t>(bspdata->bsp);
if (bsp.texdatasize)
numtextures = bsp.dtexdata->nummiptex;
LogPrint("{:7} {:<12}\n", bsp.nummodels, "models");
PrintLumpSize(lumpspec, LUMP_PLANES, bsp.numplanes);
PrintLumpSize(lumpspec, LUMP_VERTEXES, bsp.numvertexes);
PrintLumpSize(lumpspec, LUMP_NODES, bsp.numnodes);
PrintLumpSize(lumpspec, LUMP_TEXINFO, bsp.numtexinfo);
PrintLumpSize(lumpspec, LUMP_FACES, bsp.numfaces);
PrintLumpSize(lumpspec, LUMP_CLIPNODES, bsp.numclipnodes);
PrintLumpSize(lumpspec, LUMP_LEAFS, bsp.numleafs);
PrintLumpSize(lumpspec, LUMP_MARKSURFACES, bsp.nummarksurfaces);
PrintLumpSize(lumpspec, LUMP_EDGES, bsp.numedges);
PrintLumpSize(lumpspec, LUMP_SURFEDGES, bsp.numsurfedges);
LogPrint("{:7} {:<12} {:10}\n", numtextures, "textures", bsp.texdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.lightdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.visdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "entdata", bsp.entdatasize);
} else if (std::holds_alternative<bsp2rmq_t>(bspdata->bsp)) {
const bsp2rmq_t &bsp = std::get<bsp2rmq_t>(bspdata->bsp);
if (bsp.texdatasize)
numtextures = bsp.dtexdata->nummiptex;
LogPrint("{:7} {:<12}\n", bsp.nummodels, "models");
PrintLumpSize(lumpspec, LUMP_PLANES, bsp.numplanes);
PrintLumpSize(lumpspec, LUMP_VERTEXES, bsp.numvertexes);
PrintLumpSize(lumpspec, LUMP_NODES, bsp.numnodes);
PrintLumpSize(lumpspec, LUMP_TEXINFO, bsp.numtexinfo);
PrintLumpSize(lumpspec, LUMP_FACES, bsp.numfaces);
PrintLumpSize(lumpspec, LUMP_CLIPNODES, bsp.numclipnodes);
PrintLumpSize(lumpspec, LUMP_LEAFS, bsp.numleafs);
PrintLumpSize(lumpspec, LUMP_MARKSURFACES, bsp.nummarksurfaces);
PrintLumpSize(lumpspec, LUMP_EDGES, bsp.numedges);
PrintLumpSize(lumpspec, LUMP_SURFEDGES, bsp.numsurfedges);
LogPrint("{:7} {:<12} {:10}\n", numtextures, "textures", bsp.texdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.lightdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.visdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "entdata", bsp.entdatasize);
} else if (std::holds_alternative<bsp2_t>(bspdata->bsp)) {
const bsp2_t &bsp = std::get<bsp2_t>(bspdata->bsp);
if (bsp.texdatasize)
numtextures = bsp.dtexdata->nummiptex;
LogPrint("{:7} {:<12s\n", bsp.nummodels, "models");
PrintLumpSize(lumpspec, LUMP_PLANES, bsp.numplanes);
PrintLumpSize(lumpspec, LUMP_VERTEXES, bsp.numvertexes);
PrintLumpSize(lumpspec, LUMP_NODES, bsp.numnodes);
PrintLumpSize(lumpspec, LUMP_TEXINFO, bsp.numtexinfo);
PrintLumpSize(lumpspec, LUMP_FACES, bsp.numfaces);
PrintLumpSize(lumpspec, LUMP_CLIPNODES, bsp.numclipnodes);
PrintLumpSize(lumpspec, LUMP_LEAFS, bsp.numleafs);
PrintLumpSize(lumpspec, LUMP_MARKSURFACES, bsp.nummarksurfaces);
PrintLumpSize(lumpspec, LUMP_EDGES, bsp.numedges);
PrintLumpSize(lumpspec, LUMP_SURFEDGES, bsp.numsurfedges);
LogPrint("{:7} {:<12} {:10}\n", numtextures, "textures", bsp.texdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.lightdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.visdatasize);
LogPrint("{:7} {:<12} {:10}\n", "", "entdata", bsp.entdatasize);
} else {
Error("Unsupported BSP version: {}", BSPVersionString(bspdata->version));
}
if (bspdata->bspxentries) {
bspxentry_t *x;
for (x = bspdata->bspxentries; x; x = x->next) {
LogPrint("{:7} {:<12} {:10}\n", "BSPX", x->lumpname, (int)x->lumpsize);
}
}
}
/*
===============
CompressRow
===============
*/
int CompressRow(const uint8_t *vis, const int numbytes, uint8_t *out)
{
int i, rep;
uint8_t *dst;
dst = out;
for (i = 0; i < numbytes; i++) {
*dst++ = vis[i];
if (vis[i])
continue;
rep = 1;
for (i++; i < numbytes; i++)
if (vis[i] || rep == 255)
break;
else
rep++;
*dst++ = rep;
i--;
}
return dst - out;
}
/*
===================
DecompressRow
===================
*/
void DecompressRow(const uint8_t *in, const int numbytes, uint8_t *decompressed)
{
int c;
uint8_t *out;
int row;
row = numbytes;
out = decompressed;
do {
if (*in) {
*out++ = *in++;
continue;
}
c = in[1];
if (!c)
FError("0 repeat");
in += 2;
while (c) {
*out++ = 0;
c--;
}
} while (out - decompressed < row);
}