diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h index 5e932acf52a..43462180fe2 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h @@ -237,7 +237,8 @@ class WorldHeightMap : public RefCountClass, public: // height map info. static Int getMinHeightValue() {return K_MIN_HEIGHT;} static Int getMaxHeightValue() {return K_MAX_HEIGHT;} - static Bool isValidMapSize(Int width, Int height, Int borderSize); ///< Within MAX_MAP_DIMENSION and MAX_MAP_AREA, width and height include the border + static Bool isValidTotalMapSize(Int width, Int height, Int borderSize); ///< Within MAX_MAP_DIMENSION and MAX_MAP_AREA, width and height include the border + static Bool isValidPlayableMapSize(Int width, Int height, Int borderSize); ///< Width and height exclude the border UnsignedByte *getDataPtr() {return m_data;} diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp index 8e99488d2a7..1f3f2dbb20b 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp @@ -30,6 +30,7 @@ #include "windows.h" #include "stdlib.h" +#include #include "Common/STLTypedefs.h" #include "Common/DataChunk.h" @@ -849,19 +850,39 @@ Bool WorldHeightMap::ParseObjectsDataChunk(DataChunkInput &file, DataChunkInfo * return (file.parse(userData)); } -Bool WorldHeightMap::isValidMapSize(Int width, Int height, Int borderSize) +Bool WorldHeightMap::isValidTotalMapSize(Int width, Int height, Int borderSize) { if (width <= 0 || width > MAX_MAP_DIMENSION) return false; if (height <= 0 || height > MAX_MAP_DIMENSION) return false; - if (width * height > MAX_MAP_AREA) + + if (borderSize < 0 || + borderSize > (width - 1) / 2 || + borderSize > (height - 1) / 2) return false; - if (borderSize < 0 || borderSize > (width - 1) / 2 || borderSize > (height - 1) / 2) + + if (width * height > MAX_MAP_AREA) return false; + return true; } +Bool WorldHeightMap::isValidPlayableMapSize(Int width, Int height, Int borderSize) +{ + if (width <= 0 || height <= 0 || borderSize < 0) + return false; + + if (borderSize > (INT_MAX - width) / 2 || + borderSize > (INT_MAX - height) / 2) + return false; + + return isValidTotalMapSize( + width + 2 * borderSize, + height + 2 * borderSize, + borderSize); +} + static Bool isValidHeightDataSize(DataChunkInput &file, Int dataSize, Int width, Int height) { return dataSize > 0 && dataSize == width * height && (UnsignedInt)dataSize <= file.getChunkDataSizeLeft(); @@ -897,7 +918,7 @@ Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *inf m_borderSize = 0; } - if (!isValidMapSize(m_width, m_height, m_borderSize)) { + if (!isValidTotalMapSize(m_width, m_height, m_borderSize)) { throw ERROR_CORRUPT_FILE_FORMAT; } @@ -976,7 +997,7 @@ Bool WorldHeightMap::ParseSizeOnly(DataChunkInput &file, DataChunkInfo *info, vo m_borderSize = 0; } - if (!isValidMapSize(m_width, m_height, m_borderSize)) { + if (!isValidTotalMapSize(m_width, m_height, m_borderSize)) { throw ERROR_CORRUPT_FILE_FORMAT; } diff --git a/Generals/Code/Tools/WorldBuilder/src/NewHeightMap.cpp b/Generals/Code/Tools/WorldBuilder/src/NewHeightMap.cpp index 43358bb9f39..944b758fa11 100644 --- a/Generals/Code/Tools/WorldBuilder/src/NewHeightMap.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/NewHeightMap.cpp @@ -22,6 +22,10 @@ #include "StdAfx.h" #include "WorldBuilder.h" #include "NewHeightMap.h" +#include "WHeightMapEdit.h" +#include "Common/MapReaderWriterInfo.h" + +static constexpr const Int LARGE_MAP_SIDE = 1024; ///< Memory warning above this squared ///////////////////////////////////////////////////////////////////////////// // CNewHeightMap dialog @@ -209,6 +213,23 @@ void CNewHeightMap::OnOK() edit->GetWindowText(val); mHeightInfo.borderWidth = atoi(val); } + + if (!WorldHeightMapEdit::isValidPlayableMapSize(mHeightInfo.xExtent, mHeightInfo.yExtent, mHeightInfo.borderWidth)) { + CString msg; + msg.Format("Map sides including the border must be 1 to %d, with an area of at most %d cells.", MAX_MAP_DIMENSION, MAX_MAP_AREA); + AfxMessageBox(msg); + return; + } + + const Int width = mHeightInfo.xExtent + 2*mHeightInfo.borderWidth; + const Int height = mHeightInfo.yExtent + 2*mHeightInfo.borderWidth; + if (width * height > sqr(LARGE_MAP_SIDE)) { + CString msg; + msg.Format("Maps with more than %d tiles including the border can run out of memory in WorldBuilder. Continue?", sqr(LARGE_MAP_SIDE)); + if (AfxMessageBox(msg, MB_YESNO) != IDYES) + return; + } + CDialog::OnOK(); } diff --git a/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp b/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp index bf0c3564763..a56e1cd83cd 100644 --- a/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp @@ -94,7 +94,7 @@ WorldHeightMapEdit::WorldHeightMapEdit(Int width, Int height, UnsignedByte initi m_sourceTiles[i]=nullptr; m_edgeTiles[i]=nullptr; } - if (width<0 || height < 0) { + if (!isValidPlayableMapSize(width, height, border)) { AfxMessageBox(IDS_BAD_VALUE); return; } @@ -1786,6 +1786,10 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n Bool anchorLeft, Bool anchorRight, Coord3D *pObjOffset) { if (newBorder<0) newBorder = 0; + if (!isValidPlayableMapSize(newXSize, newYSize, newBorder)) { + AfxMessageBox(IDS_BAD_VALUE); + return(false); + } newXSize += 2*newBorder; newYSize += 2*newBorder; Int newDataSize = newXSize*newYSize; @@ -1824,7 +1828,7 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n Short *tileNdxes = new Short[newDataSize]; Short *blendTileNdxes = new Short[newDataSize]; Short *extraBlendTileNdxes = new Short[newDataSize]; - UnsignedByte *data = new UnsignedByte[newDataSize]; + UnsignedByte *data = new UnsignedByte[newDataSize + newXSize+1]; Short *cliffInfoNdxes = new Short[newDataSize]; Int i, j; @@ -1867,11 +1871,11 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n } } - delete(m_tileNdxes); - delete(m_cliffInfoNdxes); - delete(m_blendTileNdxes); - delete(m_extraBlendTileNdxes); - delete(m_data); + delete[] m_tileNdxes; + delete[] m_cliffInfoNdxes; + delete[] m_blendTileNdxes; + delete[] m_extraBlendTileNdxes; + delete[] m_data; m_tileNdxes = tileNdxes; m_blendTileNdxes = blendTileNdxes; m_extraBlendTileNdxes = extraBlendTileNdxes; @@ -1881,13 +1885,15 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n m_height = newYSize; m_borderSize = newBorder; m_dataSize = newDataSize; - delete(m_cellCliffState); - delete(m_cellFlipState); + delete[] m_cellCliffState; + delete[] m_cellFlipState; Int numBytesX = (m_width+7)/8; //how many bytes to fit all bitflags m_flipStateWidth=numBytesX; m_cellFlipState = MSGNEW("WorldHeightMapEdit::resize") UnsignedByte[numBytesX*m_height]; m_cellCliffState = MSGNEW("WorldHeightMapEdit::resize") UnsignedByte[numBytesX*m_height]; + memset(m_cellFlipState,0,numBytesX*m_height); + memset(m_cellCliffState,0,numBytesX*m_height); // Verify index remapping for(i=0; iGetProfileInt("GameOptions", "Default Map X-size", 100); hi.yExtent = AfxGetApp()->GetProfileInt("GameOptions", "Default Map Y-size", 100); hi.borderWidth = AfxGetApp()->GetProfileInt("GameOptions", "Default Map Border", 30); + if (!WorldHeightMapEdit::isValidPlayableMapSize(hi.xExtent, hi.yExtent, hi.borderWidth)) { + hi.xExtent = 100; + hi.yExtent = 100; + hi.borderWidth = 30; + } hi.forResize = false; if (!firstTime) { CString label; diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp index 5a7172d7359..b7c01ef83cf 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp @@ -22,6 +22,10 @@ #include "StdAfx.h" #include "WorldBuilder.h" #include "NewHeightMap.h" +#include "WHeightMapEdit.h" +#include "Common/MapReaderWriterInfo.h" + +static constexpr const Int LARGE_MAP_SIDE = 1024; ///< Memory warning above this squared ///////////////////////////////////////////////////////////////////////////// // CNewHeightMap dialog @@ -209,6 +213,23 @@ void CNewHeightMap::OnOK() edit->GetWindowText(val); mHeightInfo.borderWidth = atoi(val); } + + if (!WorldHeightMapEdit::isValidPlayableMapSize(mHeightInfo.xExtent, mHeightInfo.yExtent, mHeightInfo.borderWidth)) { + CString msg; + msg.Format("Map sides including the border must be 1 to %d, with an area of at most %d cells.", MAX_MAP_DIMENSION, MAX_MAP_AREA); + AfxMessageBox(msg); + return; + } + + const Int width = mHeightInfo.xExtent + 2*mHeightInfo.borderWidth; + const Int height = mHeightInfo.yExtent + 2*mHeightInfo.borderWidth; + if (width * height > sqr(LARGE_MAP_SIDE)) { + CString msg; + msg.Format("Maps with more than %d tiles including the border can run out of memory in WorldBuilder. Continue?", sqr(LARGE_MAP_SIDE)); + if (AfxMessageBox(msg, MB_YESNO) != IDYES) + return; + } + CDialog::OnOK(); } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp index 9c95163661d..dca58ee8420 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp @@ -94,7 +94,7 @@ WorldHeightMapEdit::WorldHeightMapEdit(Int width, Int height, UnsignedByte initi m_sourceTiles[i]=nullptr; m_edgeTiles[i]=nullptr; } - if (width<0 || height < 0) { + if (!isValidPlayableMapSize(width, height, border)) { AfxMessageBox(IDS_BAD_VALUE); return; } @@ -1786,6 +1786,10 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n Bool anchorLeft, Bool anchorRight, Coord3D *pObjOffset) { if (newBorder<0) newBorder = 0; + if (!isValidPlayableMapSize(newXSize, newYSize, newBorder)) { + AfxMessageBox(IDS_BAD_VALUE); + return(false); + } newXSize += 2*newBorder; newYSize += 2*newBorder; Int newDataSize = newXSize*newYSize; @@ -1824,7 +1828,7 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n Short *tileNdxes = new Short[newDataSize]; Short *blendTileNdxes = new Short[newDataSize]; Short *extraBlendTileNdxes = new Short[newDataSize]; - UnsignedByte *data = new UnsignedByte[newDataSize]; + UnsignedByte *data = new UnsignedByte[newDataSize + newXSize+1]; Short *cliffInfoNdxes = new Short[newDataSize]; Int i, j; @@ -1867,11 +1871,11 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n } } - delete(m_tileNdxes); - delete(m_cliffInfoNdxes); - delete(m_blendTileNdxes); - delete(m_extraBlendTileNdxes); - delete(m_data); + delete[] m_tileNdxes; + delete[] m_cliffInfoNdxes; + delete[] m_blendTileNdxes; + delete[] m_extraBlendTileNdxes; + delete[] m_data; m_tileNdxes = tileNdxes; m_blendTileNdxes = blendTileNdxes; m_extraBlendTileNdxes = extraBlendTileNdxes; @@ -1881,13 +1885,15 @@ Bool WorldHeightMapEdit::resize(Int newXSize, Int newYSize, Int newHeight, Int n m_height = newYSize; m_borderSize = newBorder; m_dataSize = newDataSize; - delete(m_cellCliffState); - delete(m_cellFlipState); + delete[] m_cellCliffState; + delete[] m_cellFlipState; Int numBytesX = (m_width+7)/8; //how many bytes to fit all bitflags m_flipStateWidth=numBytesX; m_cellFlipState = MSGNEW("WorldHeightMapEdit::resize") UnsignedByte[numBytesX*m_height]; m_cellCliffState = MSGNEW("WorldHeightMapEdit::resize") UnsignedByte[numBytesX*m_height]; + memset(m_cellFlipState,0,numBytesX*m_height); + memset(m_cellCliffState,0,numBytesX*m_height); // Verify index remapping for(i=0; iGetProfileInt("GameOptions", "Default Map X-size", 100); hi.yExtent = AfxGetApp()->GetProfileInt("GameOptions", "Default Map Y-size", 100); hi.borderWidth = AfxGetApp()->GetProfileInt("GameOptions", "Default Map Border", 30); + if (!WorldHeightMapEdit::isValidPlayableMapSize(hi.xExtent, hi.yExtent, hi.borderWidth)) { + hi.xExtent = 100; + hi.yExtent = 100; + hi.borderWidth = 30; + } hi.forResize = false; if (!firstTime) { CString label;