Skip to content

Commit ceaadd4

Browse files
committed
Texture replacing stability/debugging tweaks
1 parent 288bc65 commit ceaadd4

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>
@@ -309,9 +310,16 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
309310
continue;
310311

311312
RwTexture* pOriginalTexture = (idx < perTxdInfo.replacedOriginals.size()) ? perTxdInfo.replacedOriginals[idx] : nullptr;
313+
314+
if (pOriginalTexture && !SharedUtil::IsReadablePointer(pOriginalTexture, sizeof(RwTexture)))
315+
pOriginalTexture = nullptr;
316+
312317
swapMap[pOldTexture] = pOriginalTexture;
313318

314-
if (pOriginalTexture && !RwTexDictionaryContainsTexture(pInfo->pTxd, pOriginalTexture))
319+
// Only restore original textures early if this is the last replacement set for this TXD
320+
// Otherwise, other replacement sets might still be using the TXD
321+
bool isLastReplacement = (pInfo->usedByReplacements.size() == 1);
322+
if (pOriginalTexture && isLastReplacement && !RwTexDictionaryContainsTexture(pInfo->pTxd, pOriginalTexture))
315323
RwTexDictionaryAddTexture(pInfo->pTxd, pOriginalTexture);
316324
}
317325

@@ -346,6 +354,9 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
346354
{
347355
if (!pOldTexture)
348356
continue;
357+
358+
if (!SharedUtil::IsReadablePointer(pOldTexture, sizeof(RwTexture)))
359+
continue;
349360

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

402-
int32_t refsCount = CTxdStore_GetNumRefs(pInfo->usTxdId);
403-
assert(refsCount > 0 && "Should have at least one TXD reference here");
473+
const int32_t refsCount = CTxdStore_GetNumRefs(pInfo->usTxdId);
474+
if (refsCount <= 0)
475+
{
476+
std::ostringstream oss;
477+
oss << "TXD " << pInfo->usTxdId << " has invalid ref count "
478+
<< refsCount << " - should be > 0 before cleanup";
479+
const std::string assertMsg = oss.str();
480+
assert(false && assertMsg.c_str());
481+
}
404482
#endif
405483
// Clear original textures to prevent dangling pointers after TXD ref removal
406484
// 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)