diff --git a/src/bsp/Bsp.cpp b/src/bsp/Bsp.cpp index 7f83093d..4f56c746 100644 --- a/src/bsp/Bsp.cpp +++ b/src/bsp/Bsp.cpp @@ -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]; @@ -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; @@ -3618,6 +3633,10 @@ bool Bsp::validate() { isValid = false; } + if (!validate_vis_data()) { + isValid = false; + } + return isValid; } diff --git a/src/bsp/Bsp.h b/src/bsp/Bsp.h index 601e2769..e677c9a1 100644 --- a/src/bsp/Bsp.h +++ b/src/bsp/Bsp.h @@ -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); diff --git a/src/bsp/BspMerger.cpp b/src/bsp/BspMerger.cpp index 431da1d9..793f18d4 100644 --- a/src/bsp/BspMerger.cpp +++ b/src/bsp/BspMerger.cpp @@ -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 diff --git a/src/editor/AppSettings.cpp b/src/editor/AppSettings.cpp index 5113a338..b5d3ca95 100644 --- a/src/editor/AppSettings.cpp +++ b/src/editor/AppSettings.cpp @@ -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; diff --git a/src/main.cpp b/src/main.cpp index d7b395bd..e71ccd89 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 @@ -660,6 +706,8 @@ int main(int argc, char* argv[]) //return test(); + init_limits(); + CommandLine cli(argc, argv); if (cli.askingForHelp) { diff --git a/src/qtools/vis.cpp b/src/qtools/vis.cpp index 4ad483f4..e09b056a 100644 --- a/src/qtools/vis.cpp +++ b/src/qtools/vis.cpp @@ -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; @@ -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...) @@ -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; @@ -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++; @@ -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) diff --git a/src/qtools/vis.h b/src/qtools/vis.h index a27a9c72..fef173a1 100644 --- a/src/qtools/vis.h +++ b/src/qtools/vis.h @@ -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);