Skip to content

Commit

Permalink
fix clean/optimize crashing on maps with bad VIS data (fixes #78)
Browse files Browse the repository at this point in the history
also fix merge command not loading limits
  • Loading branch information
wootguy committed Apr 23, 2024
1 parent 933cee3 commit 89542e8
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 59 deletions.
21 changes: 20 additions & 1 deletion src/bsp/Bsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ int Bsp::remove_unused_visdata(STRUCTREMAP* remap, BSPLEAF* oldLeaves, int oldLe
int oldDecompressedVisSize = oldLeafCount * oldVisRowSize;
byte* oldDecompressedVis = new byte[oldDecompressedVisSize];
memset(oldDecompressedVis, 0, oldDecompressedVisSize);
decompress_vis_lump(oldLeaves, lumps[LUMP_VISIBILITY], oldDecompressedVis, oldVisLeafCount);
decompress_vis_lump(oldLeaves, lumps[LUMP_VISIBILITY], visDataLength, oldDecompressedVis, oldVisLeafCount);

int newDecompressedVisSize = newVisLeafCount * newVisRowSize;
byte* newDecompressedVis = new byte[oldDecompressedVisSize];
Expand Down Expand Up @@ -3428,6 +3428,21 @@ bool Bsp::isValid() {
&& ents.size() < g_limits.max_entities;
}

bool Bsp::validate_vis_data() {
// exclude solid leaf
int visLeafCount = leafCount - 1;

uint visRowSize = ((visLeafCount + 63) & ~63) >> 3;

int decompressedVisSize = visLeafCount * visRowSize;
byte* decompressedVis = new byte[decompressedVisSize];
memset(decompressedVis, 0, decompressedVisSize);
bool ret = decompress_vis_lump(leaves, lumps[LUMP_VISIBILITY], visDataLength, decompressedVis, visLeafCount);
delete[] decompressedVis;

return ret;
}

bool Bsp::validate() {
bool isValid = true;

Expand Down Expand Up @@ -3618,6 +3633,10 @@ bool Bsp::validate() {
isValid = false;
}

if (!validate_vis_data()) {
isValid = false;
}

return isValid;
}

Expand Down
2 changes: 2 additions & 0 deletions src/bsp/Bsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ class Bsp
// check for bad indexes
bool validate();

bool validate_vis_data();

// creates a solid cube
int create_solid(vec3 mins, vec3 maxs, int textureIdx);

Expand Down
4 changes: 2 additions & 2 deletions src/bsp/BspMerger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1608,12 +1608,12 @@ void BspMerger::merge_vis(Bsp& mapA, Bsp& mapB) {

// decompress this map's world leaves
// model leaves don't need to be decompressed because the game ignores VIS for them.
decompress_vis_lump(allLeaves, mapA.visdata, decompressedVis,
decompress_vis_lump(allLeaves, mapA.visdata, mapA.visDataLength, decompressedVis,
thisWorldLeafCount, thisVisLeaves, totalVisLeaves);

// decompress other map's world-leaf vis data (skip empty first leaf, which now only the first map should have)
byte* decompressedOtherVis = decompressedVis + thisWorldLeafCount * newVisRowSize;
decompress_vis_lump(allLeaves + thisWorldLeafCount, mapB.visdata, decompressedOtherVis,
decompress_vis_lump(allLeaves + thisWorldLeafCount, mapB.visdata, mapB.visDataLength, decompressedOtherVis,
otherWorldLeafCount, otherLeafCount, totalVisLeaves);

// shift mapB's world leaves after mapA's world leaves
Expand Down
44 changes: 0 additions & 44 deletions src/editor/AppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,50 +34,6 @@ void AppSettings::loadDefault()
settings_tab = 0;
engine = ENGINE_HALF_LIFE;

g_engine_limits[ENGINE_HALF_LIFE].max_surface_extents = 16;
g_engine_limits[ENGINE_HALF_LIFE].max_models = 512;
g_engine_limits[ENGINE_HALF_LIFE].max_planes = 32768;
g_engine_limits[ENGINE_HALF_LIFE].max_vertexes = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_nodes = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_faces = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_clipnodes = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_leaves = 32760;
g_engine_limits[ENGINE_HALF_LIFE].max_marksurfaces = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_surfedges = 512000;
g_engine_limits[ENGINE_HALF_LIFE].max_edges = 256000;
g_engine_limits[ENGINE_HALF_LIFE].max_textures = 512;
g_engine_limits[ENGINE_HALF_LIFE].max_lightdata = 48*1024*1024;
g_engine_limits[ENGINE_HALF_LIFE].max_visdata = 8*1024*1024;
g_engine_limits[ENGINE_HALF_LIFE].max_entdata = 2*1024*1024;
g_engine_limits[ENGINE_HALF_LIFE].max_entities = 8192;
g_engine_limits[ENGINE_HALF_LIFE].max_texinfos = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_allocblocks = 64;
g_engine_limits[ENGINE_HALF_LIFE].max_texturepixels = 262144;
g_engine_limits[ENGINE_HALF_LIFE].max_mapboundary = 4096;

g_engine_limits[ENGINE_SVEN_COOP].max_surface_extents = 64;
g_engine_limits[ENGINE_SVEN_COOP].max_models = 4096;
g_engine_limits[ENGINE_SVEN_COOP].max_planes = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_vertexes = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_nodes = 32768;
g_engine_limits[ENGINE_SVEN_COOP].max_faces = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_clipnodes = 32768;
g_engine_limits[ENGINE_SVEN_COOP].max_leaves = 65536;
g_engine_limits[ENGINE_SVEN_COOP].max_marksurfaces = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_surfedges = 512000;
g_engine_limits[ENGINE_SVEN_COOP].max_edges = 256000;
g_engine_limits[ENGINE_SVEN_COOP].max_textures = 4096;
g_engine_limits[ENGINE_SVEN_COOP].max_lightdata = 64 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_visdata = 64 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_entdata = 2 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_entities = 8192;
g_engine_limits[ENGINE_SVEN_COOP].max_texinfos = 32767;
g_engine_limits[ENGINE_SVEN_COOP].max_allocblocks = 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_texturepixels = 1048576;
g_engine_limits[ENGINE_SVEN_COOP].max_mapboundary = 32768;

g_limits = g_engine_limits[ENGINE_HALF_LIFE];

render_flags = g_render_flags = RENDER_TEXTURES | RENDER_LIGHTMAPS | RENDER_SPECIAL
| RENDER_ENTS | RENDER_SPECIAL_ENTS | RENDER_POINT_ENTS | RENDER_WIREFRAME | RENDER_ENT_CONNECTIONS
| RENDER_ENT_CLIPNODES;
Expand Down
48 changes: 48 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,52 @@ void print_help(string command) {
}
}

void init_limits() {
g_engine_limits[ENGINE_HALF_LIFE].max_surface_extents = 16;
g_engine_limits[ENGINE_HALF_LIFE].max_models = 512;
g_engine_limits[ENGINE_HALF_LIFE].max_planes = 32768;
g_engine_limits[ENGINE_HALF_LIFE].max_vertexes = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_nodes = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_faces = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_clipnodes = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_leaves = 32760;
g_engine_limits[ENGINE_HALF_LIFE].max_marksurfaces = 65535;
g_engine_limits[ENGINE_HALF_LIFE].max_surfedges = 512000;
g_engine_limits[ENGINE_HALF_LIFE].max_edges = 256000;
g_engine_limits[ENGINE_HALF_LIFE].max_textures = 512;
g_engine_limits[ENGINE_HALF_LIFE].max_lightdata = 48 * 1024 * 1024;
g_engine_limits[ENGINE_HALF_LIFE].max_visdata = 8 * 1024 * 1024;
g_engine_limits[ENGINE_HALF_LIFE].max_entdata = 2 * 1024 * 1024;
g_engine_limits[ENGINE_HALF_LIFE].max_entities = 8192;
g_engine_limits[ENGINE_HALF_LIFE].max_texinfos = 32767;
g_engine_limits[ENGINE_HALF_LIFE].max_allocblocks = 64;
g_engine_limits[ENGINE_HALF_LIFE].max_texturepixels = 262144;
g_engine_limits[ENGINE_HALF_LIFE].max_mapboundary = 4096;

g_engine_limits[ENGINE_SVEN_COOP].max_surface_extents = 64;
g_engine_limits[ENGINE_SVEN_COOP].max_models = 4096;
g_engine_limits[ENGINE_SVEN_COOP].max_planes = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_vertexes = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_nodes = 32768;
g_engine_limits[ENGINE_SVEN_COOP].max_faces = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_clipnodes = 32768;
g_engine_limits[ENGINE_SVEN_COOP].max_leaves = 65536;
g_engine_limits[ENGINE_SVEN_COOP].max_marksurfaces = 65535;
g_engine_limits[ENGINE_SVEN_COOP].max_surfedges = 512000;
g_engine_limits[ENGINE_SVEN_COOP].max_edges = 256000;
g_engine_limits[ENGINE_SVEN_COOP].max_textures = 4096;
g_engine_limits[ENGINE_SVEN_COOP].max_lightdata = 64 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_visdata = 64 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_entdata = 2 * 1024 * 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_entities = 8192;
g_engine_limits[ENGINE_SVEN_COOP].max_texinfos = 32767;
g_engine_limits[ENGINE_SVEN_COOP].max_allocblocks = 1024;
g_engine_limits[ENGINE_SVEN_COOP].max_texturepixels = 1048576;
g_engine_limits[ENGINE_SVEN_COOP].max_mapboundary = 32768;

g_limits = g_engine_limits[ENGINE_SVEN_COOP];
}

int main(int argc, char* argv[])
{
#ifdef WIN32
Expand All @@ -660,6 +706,8 @@ int main(int argc, char* argv[])

//return test();

init_limits();

CommandLine cli(argc, argv);

if (cli.askingForHelp) {
Expand Down
32 changes: 23 additions & 9 deletions src/qtools/vis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ bool shiftVis(byte* vis, int len, int offsetLeaf, int shift) {
return overflow;
}

void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output, int visDataLeafCount)
bool decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, int visLength, byte* output, int visDataLeafCount)
{
decompress_vis_lump(leafLump, visLump, output, visDataLeafCount, visDataLeafCount, visDataLeafCount);
return decompress_vis_lump(leafLump, visLump, visLength, output, visDataLeafCount, visDataLeafCount, visDataLeafCount);
}

// decompress this map's vis data into arrays of bits where each bit indicates if a leaf is visible or not
// iterationLeaves = number of leaves to decompress vis for
// visDataLeafCount = total leaves in this map (exluding the shared solid leaf 0)
// newNumLeaves = total leaves that will be in the map after merging is finished (again, excluding solid leaf 0)
void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output,
bool decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, int visLength, byte* output,
int iterationLeaves, int visDataLeafCount, int newNumLeaves)
{
byte* dest;
Expand All @@ -166,19 +166,25 @@ void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output,
lastChunkMask = lastChunkMask | (1 << k);
}

bool anyErrors = false;

for (int i = 0; i < iterationLeaves; i++)
{
g_progress.tick();
dest = output + i * newVisRowSize;
if (lastUsedIdx >= 0)
{
if (leafLump[i + 1].nVisOffset < 0) {
if (leafLump[i + 1].nVisOffset < 0 || leafLump[i + 1].nVisOffset >= visLength) {
memset(dest, 255, lastUsedIdx);
dest[lastUsedIdx] |= lastChunkMask;
continue;
}

DecompressVis((const byte*)(visLump + leafLump[i + 1].nVisOffset), dest, oldVisRowSize, visDataLeafCount);
if (!DecompressVis((const byte*)(visLump + leafLump[i + 1].nVisOffset), dest, oldVisRowSize,
visDataLeafCount, visLump, visLength)) {
logf("Failed to decompress VIS for leaf %d\n", i+1);
anyErrors = true;
}

// Leaf visibility row lengths are multiples of 64 leaves, so there are usually some unused bits at the end.
// Maps sometimes set those unused bits randomly (e.g. leaf index 100 is marked visible, but there are only 90 leaves...)
Expand All @@ -192,16 +198,19 @@ void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output,
}
else {
logf("Overflow decompressing VIS lump!");
return;
return false;
}
}

return anyErrors;
}

//
// BEGIN COPIED QVIS CODE
//

void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length, uint numLeaves)
bool DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length, uint numLeaves,
byte* visLump, int visLength)
{
unsigned int current_length = 0;
int c;
Expand All @@ -214,7 +223,10 @@ void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_le

do
{
//hlassume(src - g_dvisdata < g_visdatasize, assume_DECOMPRESSVIS_OVERFLOW);
if (src - visLump > visLength) {
//hlassume(src - visLump < visLength, assume_DECOMPRESSVIS_OVERFLOW);
return false;
}
if (*src)
{
current_length++;
Expand All @@ -240,10 +252,12 @@ void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_le

if (out - dest >= row)
{
return;
return true;
}
}
} while (out - dest < row);

return true;
}

int CompressVis(const byte* const src, const unsigned int src_length, byte* dest, unsigned int dest_length)
Expand Down
7 changes: 4 additions & 3 deletions src/qtools/vis.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ bool shiftVis(byte* vis, int len, int offsetLeaf, int shift);
// iterationLeaves = number of leaves to decompress vis for
// visDataLeafCount = total leaves in the map (exluding the shared solid leaf 0)
// newNumLeaves = total leaves that will be in the map after merging is finished (again, excluding solid leaf 0)
void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output,
bool decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, int visLength, byte* output,
int iterationLeaves, int visDataLeafCount, int newNumLeaves);

// visDataLeafCount should exclude the solid leaf 0
void decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, byte* output, int visDataLeafCount);
bool decompress_vis_lump(BSPLEAF* leafLump, byte* visLump, int visLength, byte* output, int visDataLeafCount);

void DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length, uint numLeaves);
bool DecompressVis(const byte* src, byte* const dest, const unsigned int dest_length, uint numLeaves,
byte* visLump, int visLength);

int CompressVis(const byte* const src, const unsigned int src_length, byte* dest, unsigned int dest_length);

Expand Down

0 comments on commit 89542e8

Please sign in to comment.