Skip to content

Commit ecb27fc

Browse files
committed
Texture replacing stability/debugging tweaks
1 parent 03755c0 commit ecb27fc

File tree

2 files changed

+96
-11
lines changed

2 files changed

+96
-11
lines changed

Client/game_sa/CRenderWareSA.TextureReplacing.cpp

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "gamesa_renderware.h"
1313

1414
#include <map>
15+
#include <sstream>
1516
#include <unordered_map>
1617
#include <unordered_set>
1718
#include <utility>
@@ -312,9 +313,16 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
312313
continue;
313314

314315
RwTexture* pOriginalTexture = (idx < perTxdInfo.replacedOriginals.size()) ? perTxdInfo.replacedOriginals[idx] : nullptr;
316+
317+
if (pOriginalTexture && !SharedUtil::IsReadablePointer(pOriginalTexture, sizeof(RwTexture)))
318+
pOriginalTexture = nullptr;
319+
315320
swapMap[pOldTexture] = pOriginalTexture;
316321

317-
if (pOriginalTexture && !RwTexDictionaryContainsTexture(pInfo->pTxd, pOriginalTexture))
322+
// Only restore original textures early if this is the last replacement set for this TXD
323+
// Otherwise, other replacement sets might still be using the TXD
324+
bool isLastReplacement = (pInfo->usedByReplacements.size() == 1);
325+
if (pOriginalTexture && isLastReplacement && !RwTexDictionaryContainsTexture(pInfo->pTxd, pOriginalTexture))
318326
RwTexDictionaryAddTexture(pInfo->pTxd, pOriginalTexture);
319327
}
320328

@@ -349,6 +357,9 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
349357
{
350358
if (!pOldTexture)
351359
continue;
360+
361+
if (!SharedUtil::IsReadablePointer(pOldTexture, sizeof(RwTexture)))
362+
continue;
352363

353364
RwTexDictionaryRemoveTexture(pInfo->pTxd, pOldTexture);
354365
dassert(!RwTexDictionaryContainsTexture(pInfo->pTxd, pOldTexture));
@@ -392,18 +403,85 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
392403
#ifdef MTA_DEBUG
393404
std::vector<RwTexture*> currentTextures;
394405
GetTxdTextures(currentTextures, pInfo->pTxd);
395-
assert(currentTextures.size() == pInfo->originalTextures.size());
396-
for (RwTexture* pOriginalTexture : pInfo->originalTextures)
406+
407+
auto formatTextures = [](const std::vector<RwTexture*>& textures) -> std::string {
408+
std::ostringstream result;
409+
for (size_t i = 0; i < textures.size(); ++i)
410+
{
411+
const auto* pTex = textures[i];
412+
const bool isValid = pTex && SharedUtil::IsReadablePointer(pTex, sizeof(RwTexture));
413+
const bool isLast = (i == textures.size() - 1);
414+
415+
if (isValid)
416+
result << pTex->name << "[0x" << std::hex << pTex << std::dec << "]";
417+
else
418+
result << "INVALID[0x" << std::hex << pTex << std::dec << "]";
419+
420+
if (!isLast)
421+
result << ", ";
422+
}
423+
return result.str();
424+
};
425+
426+
// Allow size mismatch in case texture removal was skipped due to invalid pointers
427+
if (currentTextures.size() != pInfo->originalTextures.size())
397428
{
398-
if (!pOriginalTexture)
399-
continue;
400-
assert(ListContains(currentTextures, pOriginalTexture));
401-
ListRemove(currentTextures, pOriginalTexture);
429+
std::ostringstream debugMsg;
430+
debugMsg << "TXD " << pInfo->usTxdId << ": texture count mismatch (current="
431+
<< currentTextures.size() << ", expected=" << pInfo->originalTextures.size() << ")\n";
432+
debugMsg << " Current textures: " << formatTextures(currentTextures);
433+
debugMsg << "\n Expected textures: " << formatTextures(pInfo->originalTextures);
434+
debugMsg << "\n";
435+
OutputDebugString(debugMsg.str().c_str());
436+
}
437+
else
438+
{
439+
// First pass: validate all original textures are present
440+
for (const auto* pOriginalTexture : pInfo->originalTextures)
441+
{
442+
if (!pOriginalTexture)
443+
continue;
444+
if (!ListContains(currentTextures, pOriginalTexture))
445+
{
446+
const char* texName = SharedUtil::IsReadablePointer(pOriginalTexture, sizeof(RwTexture))
447+
? pOriginalTexture->name : "INVALID";
448+
std::ostringstream oss;
449+
oss << "Original texture not found in TXD " << pInfo->usTxdId
450+
<< " - texture '" << texName << "' [0x" << std::hex << pOriginalTexture << std::dec
451+
<< "] was removed or replaced unexpectedly";
452+
const std::string assertMsg = oss.str();
453+
assert(false && assertMsg.c_str());
454+
}
455+
}
456+
457+
// Second pass: remove original textures from current list to find extras
458+
for (auto* pOriginalTexture : pInfo->originalTextures)
459+
{
460+
if (pOriginalTexture)
461+
ListRemove(currentTextures, pOriginalTexture);
462+
}
463+
464+
// Check for leaked textures
465+
if (!currentTextures.empty())
466+
{
467+
std::ostringstream oss;
468+
oss << "Extra textures remain in TXD " << pInfo->usTxdId
469+
<< " after removing all originals - indicates texture leak. Remaining: "
470+
<< formatTextures(currentTextures);
471+
const std::string assertMsg = oss.str();
472+
assert(false && assertMsg.c_str());
473+
}
402474
}
403-
assert(currentTextures.empty());
404475

405-
int32_t refsCount = CTxdStore_GetNumRefs(pInfo->usTxdId);
406-
assert(refsCount > 0, "Should have at least one TXD reference here");
476+
const int32_t refsCount = CTxdStore_GetNumRefs(pInfo->usTxdId);
477+
if (refsCount <= 0)
478+
{
479+
std::ostringstream oss;
480+
oss << "TXD " << pInfo->usTxdId << " has invalid ref count "
481+
<< refsCount << " - should be > 0 before cleanup";
482+
const std::string assertMsg = oss.str();
483+
assert(false && assertMsg.c_str());
484+
}
407485
#endif
408486
// Clear original textures to prevent dangling pointers after TXD ref removal
409487
// The textures themselves are owned by the TXD and will be cleaned up when ref count hits zero

Client/game_sa/CRenderWareSA.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,9 +755,16 @@ short CRenderWareSA::CTxdStore_GetTxdRefcount(unsigned short usTxdID)
755755

756756
bool CRenderWareSA::RwTexDictionaryContainsTexture(RwTexDictionary* pTXD, RwTexture* pTex)
757757
{
758-
// Avoid crashes with freed/invalid textures
758+
// Avoid crashes with freed/invalid textures and TXDs
759759
if (!pTex || !pTXD)
760760
return false;
761+
762+
// Prevent crash when texture/TXD has been freed but pointer still exists
763+
if (!SharedUtil::IsReadablePointer(pTex, sizeof(RwTexture)))
764+
return false;
765+
766+
if (!SharedUtil::IsReadablePointer(pTXD, sizeof(RwTexDictionary)))
767+
return false;
761768

762769
return pTex->txd == pTXD;
763770
}

0 commit comments

Comments
 (0)