-
Notifications
You must be signed in to change notification settings - Fork 265
bugfix(worldbuilder): Handle null boundary lookup output #3383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -600,13 +600,26 @@ void WorldHeightMapEdit::saveToFile(DataChunkOutput &chunkWriter) | |
| chunkWriter.closeDataChunk(); | ||
|
|
||
| /***************BLEND TILE DATA ***************/ | ||
| chunkWriter.openDataChunk("BlendTileData", K_BLEND_TILE_VERSION_8); | ||
| #if RTS_GENERALS && RETAIL_COMPATIBLE_DATA | ||
| const DataChunkVersionType blendTileVersion = K_BLEND_TILE_VERSION_7; | ||
| #else | ||
| const DataChunkVersionType blendTileVersion = K_BLEND_TILE_VERSION_8; | ||
| #endif | ||
| chunkWriter.openDataChunk("BlendTileData", blendTileVersion); | ||
| chunkWriter.writeInt(m_dataSize); | ||
| chunkWriter.writeArrayOfBytes((char*)m_tileNdxes, m_dataSize*sizeof(Short)); | ||
| chunkWriter.writeArrayOfBytes((char*)m_blendTileNdxes, m_dataSize*sizeof(Short)); | ||
| chunkWriter.writeArrayOfBytes((char*)m_extraBlendTileNdxes, m_dataSize*sizeof(Short)); | ||
| chunkWriter.writeArrayOfBytes((char*)m_cliffInfoNdxes, m_dataSize*sizeof(Short)); | ||
| chunkWriter.writeArrayOfBytes((char*)m_cellCliffState, m_height*m_flipStateWidth); | ||
| if (blendTileVersion <= K_BLEND_TILE_VERSION_7) { | ||
| // Version 7 uses the legacy row width, even when the in-memory rows are wider. | ||
| Int byteWidth = (m_width+1)/8; | ||
| for (Int j=0; j<m_height; j++) { | ||
| chunkWriter.writeArrayOfBytes((char*)m_cellCliffState + j*m_flipStateWidth, byteWidth); | ||
|
Comment on lines
+614
to
+618
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: rg -n 'setWidth|setDimensions|m_width[[:space:]]*=|mapWidth|heightMapWidth|resize|newMap|createMap|RETAIL_COMPATIBLE_DATA' Core/Tools/WorldBuilder Core/GameEngineDevice/Source/W3DDevice Generals/Code/Tools/WorldBuilder
rg -n 'width.*(multiple|%|/|64|128)|m_width.*(8|16|32|64|128)' Core/Tools/WorldBuilder Core/GameEngineDevice/Source/W3DDevice/GameClientRepository: TheSuperHackers/GeneralsGameCode Length of output: 26448 🏁 Script executed: set -e
printf '%s\n' '--- WHeightMapEdit declarations and construction ---'
sed -n '80,235p' Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp
sed -n '110,175p' Core/Tools/WorldBuilder/include/WHeightMapEdit.h
printf '%s\n' '--- resize implementation ---'
sed -n '1760,1910p' Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp
printf '%s\n' '--- WorldBuilderDoc map creation/loading entrypoints ---'
sed -n '960,1070p' Core/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
rg -n -C 5 'new WorldHeightMapEdit|WorldHeightMapEdit\(|resize\(|initialHeight|xExtent|yExtent|borderWidth' Core/Tools/WorldBuilder/src Core/Tools/WorldBuilder/include
printf '%s\n' '--- dimension validation and format readers ---'
rg -n -C 6 'xSize|ySize|xExtent|yExtent|width.*8|width.*64|MAP_SIZE|map size|MapSize|newWidth|newHeight' Core/Tools/WorldBuilder/src Core/Tools/WorldBuilder/include Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cppRepository: TheSuperHackers/GeneralsGameCode Length of output: 11905 🏁 Script executed: set -o pipefail
printf '%s\n' '--- WorldBuilder document creation/loading and resize callers ---'
rg -n -C 8 'WorldHeightMapEdit|resize\(|xExtent|yExtent|initialHeight|borderWidth|NewHeightMap|load' Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp Generals/Code/Tools/WorldBuilder/src Generals/Code/Tools/WorldBuilder/include
printf '%s\n' '--- NewHeightMap declarations and implementation ---'
fd -i 'NewHeightMap' Generals/Code/Tools/WorldBuilder
printf '%s\n' '--- WorldHeightMap loading dimension handling ---'
sed -n '820,985p' Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp
sed -n '1025,1090p' Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp
printf '%s\n' '--- all dimension-related guards in relevant WorldBuilder files ---'
rg -n -C 4 'BAD_VALUE|<= *0|< *0|% *8|/ *8|multiple|extent|size' Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp Generals/Code/Tools/WorldBuilder/src Generals/Code/Tools/WorldBuilder/include Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp Core/Tools/WorldBuilder/include/WHeightMapEdit.hRepository: TheSuperHackers/GeneralsGameCode Length of output: 45686 🏁 Script executed: set -e
printf '%s\n' '--- NewHeightMap files ---'
fd -i 'NewHeightMap' Generals/Code/Tools/WorldBuilder Core/Tools/WorldBuilder
printf '%s\n' '--- NewHeightMap validation ---'
rg -n -C 10 'OnInitDialog|OnOK|DDX|xExtent|yExtent|borderWidth|forResize|SetLimitText|GetDlgItem|BAD_VALUE|<=|< *0|% *8|/ *8' Generals/Code/Tools/WorldBuilder/src/NewHeightMap.cpp Generals/Code/Tools/WorldBuilder/include/NewHeightMap.h Core/Tools/WorldBuilder/src/NewHeightMap.cpp Core/Tools/WorldBuilder/include/NewHeightMap.h 2>/dev/null || true
printf '%s\n' '--- WorldHeightMap constructors and readers ---'
rg -n -C 10 'WorldHeightMap::WorldHeightMap|readInt\(\)|K_BLEND_TILE_VERSION|numBytesX|byteWidth|m_width|m_height' Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.hRepository: TheSuperHackers/GeneralsGameCode Length of output: 41736 🏁 Script executed: set -e
printf '%s\n' '--- save function and changed hunk ---'
sed -n '560,635p' Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp
rg -n -C 10 'saveToFile\(|SaveMap|OnSaveDocument|OnFileSave|write.*height|heightMap.*save' Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp Generals/Code/Tools/WorldBuilder/include/SaveMap.h 2>/dev/null || true
printf '%s\n' '--- exact parsing and flag readers ---'
rg -n -C 12 'ParseHeightMapData|ParseSizeOnly|m_dataSize.*m_width|byteWidth|numBytesX' Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cppRepository: TheSuperHackers/GeneralsGameCode Length of output: 41363 🏁 Script executed: set -e
sed -n '250,320p' Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
sed -n '780,855p' Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
rg -n -C 6 'throw ERROR|ERROR_CORRUPT_FILE_FORMAT|height map file write failed|saveToFile' Core/Tools/WorldBuilder/src Generals/Code/Tools/WorldBuilder/srcRepository: TheSuperHackers/GeneralsGameCode Length of output: 12421 🏁 Script executed: set -e
rg -n -C 12 'setCliffState|m_cellCliffState\[|setCellCliffFlagFromHeights|initCliffFlagsFromHeights' Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h Core/Tools/WorldBuilder/src/WHeightMapEdit.cpp Core/Tools/WorldBuilder/include/WHeightMapEdit.hRepository: TheSuperHackers/GeneralsGameCode Length of output: 28808 Reject version-7 saves when the omitted cliff-state byte is non-zero.
Version 8 preserves the bits but violates the retail compatibility contract. Check the omitted byte before writing any chunks and reject the save when it contains data. Suggested fix void WorldHeightMapEdit::saveToFile(DataChunkOutput &chunkWriter)
{
+ #if RTS_GENERALS && RETAIL_COMPATIBLE_DATA
+ Int legacyByteWidth = (m_width+1)/8;
+ if (legacyByteWidth < m_flipStateWidth) {
+ for (Int j=0; j<m_height; j++) {
+ if (m_cellCliffState[j*m_flipStateWidth + legacyByteWidth] != 0) {
+ throw(ERROR_CORRUPT_FILE_FORMAT);
+ }
+ }
+ }
+ #endif
+
// This is the chunk writer stuff.
int i; |
||
| } | ||
| } else { | ||
| chunkWriter.writeArrayOfBytes((char*)m_cellCliffState, m_height*m_flipStateWidth); | ||
| } | ||
| chunkWriter.writeInt(m_numBitmapTiles); | ||
| chunkWriter.writeInt(m_numBlendedTiles); | ||
| chunkWriter.writeInt(m_numCliffInfo); | ||
|
|
@@ -3427,5 +3440,8 @@ void WorldHeightMapEdit::findBoundaryNear(Coord3D *pt, float okDistance, Int *ou | |
| } | ||
|
|
||
| (*outNdx) = -1; | ||
| (*outHandle) = -1; | ||
| // TheSuperHackers @bugfix Handle an omitted boundary handle on the no-match path. | ||
| if (outHandle) { | ||
| (*outHandle) = -1; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
ast-grep outline Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp \ --match findOrCreateBlendTile --view expanded ast-grep run --lang cpp \ --pattern 'Int WorldHeightMapEdit::findOrCreateBlendTile(TBlendTileInfo *$INFO) { $$$BODY }' \ Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cppRepository: TheSuperHackers/GeneralsGameCode
Length of output: 306
🏁 Script executed:
Repository: TheSuperHackers/GeneralsGameCode
Length of output: 41942
🏁 Script executed:
Repository: TheSuperHackers/GeneralsGameCode
Length of output: 8401
Handle failure before replacing the primary blend index.
findOrCreateBlendTilecan return-1when the blend table is full. The second allocation stores that value inm_blendTileNdxes[ndx]without a check.optimizeTilesand later blend operations can then use-1as an array index.Allocate the flipped description before committing the cell indices. If it fails, preserve the previous cell state.
Suggested fix
if (newNdx >= 0) { Int ndx = (yIndex*m_width)+xIndex; - m_tileNdxes[ndx] = curTileNdx; - if (TheGlobalData->m_use3WayTerrainBlends && m_blendTileNdxes[ndx] != 0) + Bool hasBaseBlend = TheGlobalData->m_use3WayTerrainBlends && + m_blendTileNdxes[ndx] != 0; + Short primaryNdx = hasBaseBlend ? m_blendTileNdxes[ndx] : newNdx; + if (hasBaseBlend) { //this tile already has a blend applied to it. So we put the new blend into the //secondary layer. - m_extraBlendTileNdxes[ndx]=newNdx; //force the primary layer to flip if the extra blend layer needs flip. //we only do this on vertical/horizontal base blends because they work in either flip cases. if (flipped && !baseIsDiagonal) { //Find a new tile so as not to affect other cells using the base one. TBlendTileInfo tempBlendTileInfo=m_blendedTiles[m_blendTileNdxes[ndx]]; tempBlendTileInfo.inverted |= FLIPPED_MASK; - Short newNdx = findOrCreateBlendTile(&tempBlendTileInfo); - m_blendTileNdxes[ndx] = newNdx; //remap this tile to use a new one. + Short flippedNdx = findOrCreateBlendTile(&tempBlendTileInfo); + if (flippedNdx < 0) + return; + primaryNdx = flippedNdx; } - } - else - m_blendTileNdxes[ndx] = newNdx; + } + m_tileNdxes[ndx] = curTileNdx; + if (hasBaseBlend) + m_extraBlendTileNdxes[ndx] = newNdx; + m_blendTileNdxes[ndx] = primaryNdx; }