Skip to content

Commit c4fe546

Browse files
committed
Fix shutdown crash in CRenderWareSA.TextureReplacing.cpp (+ some other safe tweaks/modernisations)
1 parent 8d8bbff commit c4fe546

File tree

3 files changed

+84
-65
lines changed

3 files changed

+84
-65
lines changed

Client/game_sa/CRenderWareSA.TextureReplacing.cpp

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -112,50 +112,49 @@ namespace
112112
////////////////////////////////////////////////////////////////
113113
CModelTexturesInfo* CRenderWareSA::GetModelTexturesInfo(ushort usModelId)
114114
{
115-
CModelInfoSA* pModelInfo = dynamic_cast<CModelInfoSA*>(pGame->GetModelInfo(usModelId));
115+
auto* pModelInfo = dynamic_cast<CModelInfoSA*>(pGame->GetModelInfo(usModelId));
116116
if (!pModelInfo)
117-
return NULL;
117+
return nullptr;
118118

119-
ushort usTxdId = pModelInfo->GetTextureDictionaryID();
119+
const ushort usTxdId = pModelInfo->GetTextureDictionaryID();
120120

121-
CModelTexturesInfo* pInfo = MapFind(ms_ModelTexturesInfoMap, usTxdId);
122-
if (!pInfo)
123-
{
124-
// Get txd
125-
RwTexDictionary* pTxd = CTxdStore_GetTxd(usTxdId);
121+
if (auto it = ms_ModelTexturesInfoMap.find(usTxdId); it != ms_ModelTexturesInfoMap.end())
122+
return &it->second;
126123

127-
if (!pTxd)
124+
// Get txd
125+
RwTexDictionary* pTxd = CTxdStore_GetTxd(usTxdId);
126+
127+
if (!pTxd)
128+
{
129+
pModelInfo->Request(BLOCKING, "CRenderWareSA::GetModelTexturesInfo");
130+
CTxdStore_AddRef(usTxdId);
131+
((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(usModelId);
132+
pTxd = CTxdStore_GetTxd(usTxdId);
133+
}
134+
else
135+
{
136+
CTxdStore_AddRef(usTxdId);
137+
if (pModelInfo->GetModelType() == eModelInfoType::PED)
128138
{
129-
pModelInfo->Request(BLOCKING, "CRenderWareSA::GetModelTexturesInfo");
130-
CTxdStore_AddRef(usTxdId);
139+
// Mystery fix for #9336: (MTA sometimes fails at loading custom textures)
140+
// Possibly forces the ped model to be reloaded in some way
131141
((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(usModelId);
132-
pTxd = CTxdStore_GetTxd(usTxdId);
133142
}
134-
else
135-
{
136-
CTxdStore_AddRef(usTxdId);
137-
if (pModelInfo->GetModelType() == eModelInfoType::PED)
138-
{
139-
// Mystery fix for #9336: (MTA sometimes fails at loading custom textures)
140-
// Possibly forces the ped model to be reloaded in some way
141-
((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(usModelId);
142-
}
143-
}
144-
145-
if (!pTxd)
146-
return NULL;
143+
}
147144

148-
// Add new info
149-
MapSet(ms_ModelTexturesInfoMap, usTxdId, CModelTexturesInfo());
150-
pInfo = MapFind(ms_ModelTexturesInfoMap, usTxdId);
151-
pInfo->usTxdId = usTxdId;
152-
pInfo->pTxd = pTxd;
145+
if (!pTxd)
146+
return nullptr;
153147

154-
// Save original textures
155-
GetTxdTextures(pInfo->originalTextures, pInfo->pTxd);
156-
}
148+
// Add new info
149+
auto [it, inserted] = ms_ModelTexturesInfoMap.emplace(usTxdId, CModelTexturesInfo{});
150+
auto& newInfo = it->second;
151+
newInfo.usTxdId = usTxdId;
152+
newInfo.pTxd = pTxd;
157153

158-
return pInfo;
154+
// Save original textures
155+
GetTxdTextures(newInfo.originalTextures, newInfo.pTxd);
156+
157+
return &newInfo;
159158
}
160159

161160
////////////////////////////////////////////////////////////////
@@ -173,27 +172,29 @@ bool CRenderWareSA::ModelInfoTXDLoadTextures(SReplacementTextures* pReplacementT
173172
return false;
174173

175174
// Try to load it
176-
RwTexDictionary* pTxd = ReadTXD(strFilename, buffer);
177-
if (pTxd)
175+
if (auto* pTxd = ReadTXD(strFilename, buffer); pTxd)
178176
{
179177
// Get the list of textures into our own list
180178
GetTxdTextures(pReplacementTextures->textures, pTxd);
181179

182-
for (uint i = 0; i < pReplacementTextures->textures.size(); i++)
180+
for (RwTexture* pTexture : pReplacementTextures->textures)
183181
{
184-
pReplacementTextures->textures[i]->txd = NULL;
182+
if (!pTexture)
183+
continue;
184+
185+
pTexture->txd = nullptr;
185186
if (bFilteringEnabled)
186-
pReplacementTextures->textures[i]->flags = 0x1102; // Enable filtering (otherwise textures are pixely)
187+
pTexture->flags = 0x1102; // Enable filtering (otherwise textures are pixely)
187188
}
188189

189190
// Make the txd forget it has any textures and destroy it
190191
pTxd->textures.root.next = &pTxd->textures.root;
191192
pTxd->textures.root.prev = &pTxd->textures.root;
192193
RwTexDictionaryDestroy(pTxd);
193-
pTxd = NULL;
194+
pTxd = nullptr;
194195

195196
// We succeeded if we got any textures
196-
return pReplacementTextures->textures.size() > 0;
197+
return !pReplacementTextures->textures.empty();
197198
}
198199

199200
return false;
@@ -228,17 +229,14 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
228229
//
229230
// Add section for this txd
230231
//
231-
pReplacementTextures->perTxdList.push_back(SReplacementTextures::SPerTxd());
232-
SReplacementTextures::SPerTxd& perTxdInfo = pReplacementTextures->perTxdList.back();
232+
auto& perTxdInfo = pReplacementTextures->perTxdList.emplace_back();
233233

234234
perTxdInfo.usTxdId = pInfo->usTxdId;
235-
perTxdInfo.bTexturesAreCopies = (pReplacementTextures->usedInTxdIds.size() > 0);
235+
perTxdInfo.bTexturesAreCopies = !pReplacementTextures->usedInTxdIds.empty();
236236

237237
// Copy / clone textures
238-
for (std::vector<RwTexture*>::iterator iter = pReplacementTextures->textures.begin(); iter != pReplacementTextures->textures.end(); iter++)
238+
for (RwTexture* pNewTexture : pReplacementTextures->textures)
239239
{
240-
RwTexture* pNewTexture = *iter;
241-
242240
// Use a copy if not first txd
243241
if (perTxdInfo.bTexturesAreCopies)
244242
{
@@ -258,10 +256,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
258256
//
259257
// Add each texture to the target txd
260258
//
261-
for (std::vector<RwTexture*>::iterator iter = perTxdInfo.usingTextures.begin(); iter != perTxdInfo.usingTextures.end(); iter++)
259+
for (RwTexture* pNewTexture : perTxdInfo.usingTextures)
262260
{
263-
RwTexture* pNewTexture = *iter;
264-
265261
// If there is a name clash with an existing texture, replace it
266262
RwTexture* pExistingTexture = RwTexDictionaryFindNamedTexture(pInfo->pTxd, pNewTexture->name);
267263
if (pExistingTexture)
@@ -293,10 +289,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
293289
void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacementTextures)
294290
{
295291
// For each using txd
296-
for (uint i = 0; i < pReplacementTextures->perTxdList.size(); i++)
292+
for (auto& perTxdInfo : pReplacementTextures->perTxdList)
297293
{
298-
SReplacementTextures::SPerTxd& perTxdInfo = pReplacementTextures->perTxdList[i];
299-
300294
// Get textures info
301295
ushort usTxdId = perTxdInfo.usTxdId;
302296
CModelTexturesInfo* pInfo = MapFind(ms_ModelTexturesInfoMap, usTxdId);
@@ -308,7 +302,7 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
308302
TextureSwapMap swapMap;
309303
swapMap.reserve(perTxdInfo.usingTextures.size());
310304

311-
for (size_t idx = 0; idx < perTxdInfo.usingTextures.size(); ++idx)
305+
for (std::size_t idx = 0; idx < perTxdInfo.usingTextures.size(); ++idx)
312306
{
313307
RwTexture* pOldTexture = perTxdInfo.usingTextures[idx];
314308
if (!pOldTexture)
@@ -330,7 +324,7 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
330324

331325
for (ushort modelId : pReplacementTextures->usedInModelIds)
332326
{
333-
CModelInfoSA* pModelInfo = dynamic_cast<CModelInfoSA*>(pGame->GetModelInfo(modelId));
327+
auto* pModelInfo = dynamic_cast<CModelInfoSA*>(pGame->GetModelInfo(modelId));
334328
if (!pModelInfo)
335329
continue;
336330

@@ -348,9 +342,11 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
348342
}
349343

350344
// Remove replacement textures
351-
for (uint i = 0; i < perTxdInfo.usingTextures.size(); i++)
345+
for (RwTexture* pOldTexture : perTxdInfo.usingTextures)
352346
{
353-
RwTexture* pOldTexture = perTxdInfo.usingTextures[i];
347+
if (!pOldTexture)
348+
continue;
349+
354350
RwTexDictionaryRemoveTexture(pInfo->pTxd, pOldTexture);
355351
dassert(!RwTexDictionaryContainsTexture(pInfo->pTxd, pOldTexture));
356352
if (perTxdInfo.bTexturesAreCopies)
@@ -362,12 +358,23 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
362358
}
363359

364360
perTxdInfo.usingTextures.clear();
361+
362+
// Free replaced textures that aren't in originalTextures (prevents leak)
363+
for (RwTexture* pReplacedTexture : perTxdInfo.replacedOriginals)
364+
{
365+
if (pReplacedTexture && !ListContains(pInfo->originalTextures, pReplacedTexture))
366+
RwTextureDestroy(pReplacedTexture);
367+
}
368+
365369
perTxdInfo.replacedOriginals.clear();
366370

367371
// Ensure there are original named textures in the txd
368-
for (uint i = 0; i < pInfo->originalTextures.size(); i++)
372+
for (RwTexture* pOriginalTexture : pInfo->originalTextures)
369373
{
370-
RwTexture* pOriginalTexture = pInfo->originalTextures[i];
374+
// Skip null/invalid textures (can happen during shutdown)
375+
if (!pOriginalTexture)
376+
continue;
377+
371378
if (!RwTexDictionaryFindNamedTexture(pInfo->pTxd, pOriginalTexture->name))
372379
RwTexDictionaryAddTexture(pInfo->pTxd, pOriginalTexture);
373380
}
@@ -383,9 +390,10 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
383390
std::vector<RwTexture*> currentTextures;
384391
GetTxdTextures(currentTextures, pInfo->pTxd);
385392
assert(currentTextures.size() == pInfo->originalTextures.size());
386-
for (uint i = 0; i < pInfo->originalTextures.size(); i++)
393+
for (RwTexture* pOriginalTexture : pInfo->originalTextures)
387394
{
388-
RwTexture* pOriginalTexture = pInfo->originalTextures[i];
395+
if (!pOriginalTexture)
396+
continue;
389397
assert(ListContains(currentTextures, pOriginalTexture));
390398
ListRemove(currentTextures, pOriginalTexture);
391399
}
@@ -394,17 +402,21 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
394402
int32_t refsCount = CTxdStore_GetNumRefs(pInfo->usTxdId);
395403
assert(refsCount > 0 && "Should have at least one TXD reference here");
396404
#endif
405+
// Clear original textures to prevent dangling pointers after TXD ref removal
406+
// The textures themselves are owned by the TXD and will be cleaned up when ref count hits zero
407+
pInfo->originalTextures.clear();
408+
397409
// Remove info
398410
CTxdStore_RemoveRef(pInfo->usTxdId);
399411
MapRemove(ms_ModelTexturesInfoMap, usTxdId);
400412
}
401413
}
402414

403415
// Destroy replacement textures
404-
for (uint i = 0; i < pReplacementTextures->textures.size(); i++)
416+
for (RwTexture* pTexture : pReplacementTextures->textures)
405417
{
406-
RwTexture* pOldTexture = pReplacementTextures->textures[i];
407-
DestroyTexture(pOldTexture);
418+
if (pTexture)
419+
DestroyTexture(pTexture);
408420
}
409421
pReplacementTextures->textures.clear();
410422
}

Client/game_sa/CRenderWareSA.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,9 @@ void CRenderWareSA::DestroyTexture(RwTexture* pTex)
737737

738738
void CRenderWareSA::RwTexDictionaryRemoveTexture(RwTexDictionary* pTXD, RwTexture* pTex)
739739
{
740+
if (!pTex || !pTXD)
741+
return;
742+
740743
if (pTex->txd != pTXD)
741744
return;
742745

@@ -752,6 +755,10 @@ short CRenderWareSA::CTxdStore_GetTxdRefcount(unsigned short usTxdID)
752755

753756
bool CRenderWareSA::RwTexDictionaryContainsTexture(RwTexDictionary* pTXD, RwTexture* pTex)
754757
{
758+
// Avoid crashes with freed/invalid textures
759+
if (!pTex || !pTXD)
760+
return false;
761+
755762
return pTex->txd == pTXD;
756763
}
757764

Client/game_sa/CRenderWareSA.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "CRenderWareSA.ShaderSupport.h"
1717

1818
class CMatchChannelManager;
19-
class CModelTexturesInfo;
19+
struct CModelTexturesInfo;
2020
struct RpAtomic;
2121
struct SShaderReplacementStats;
2222
struct STexInfo;

0 commit comments

Comments
 (0)