Skip to content

Commit c99f84d

Browse files
committed
Fix crash in RpMaterialDestroy
1 parent e8b48ca commit c99f84d

File tree

3 files changed

+168
-12
lines changed

3 files changed

+168
-12
lines changed

Client/game_sa/CRenderWareSA.TextureReplacing.cpp

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,100 @@
88

99
#include "StdInc.h"
1010
#include "CGameSA.h"
11+
#include "CRenderWareSA.h"
1112
#include "gamesa_renderware.h"
1213

14+
#include <map>
15+
#include <unordered_map>
16+
#include <unordered_set>
17+
#include <utility>
18+
1319
extern CGameSA* pGame;
1420

15-
//
16-
// Info about the current state of a model's txd textures
17-
//
18-
class CModelTexturesInfo
21+
struct CModelTexturesInfo
1922
{
20-
public:
21-
std::vector<RwTexture*> originalTextures;
23+
ushort usTxdId = 0;
24+
RwTexDictionary* pTxd = nullptr;
25+
std::vector<RwTexture*> originalTextures;
2226
std::vector<SReplacementTextures*> usedByReplacements;
23-
ushort usTxdId;
24-
RwTexDictionary* pTxd;
2527
};
2628

27-
std::map<ushort, CModelTexturesInfo> ms_ModelTexturesInfoMap;
29+
static std::map<ushort, CModelTexturesInfo> ms_ModelTexturesInfoMap;
30+
31+
namespace
32+
{
33+
using TextureSwapMap = std::unordered_map<RwTexture*, RwTexture*>;
34+
35+
void ReplaceTextureInGeometry(RpGeometry* pGeometry, const TextureSwapMap& swapMap)
36+
{
37+
if (!pGeometry || swapMap.empty())
38+
return;
39+
40+
RpMaterials& materials = pGeometry->materials;
41+
if (!materials.materials)
42+
return;
43+
44+
for (int idx = 0; idx < materials.entries; ++idx)
45+
{
46+
RpMaterial* pMaterial = materials.materials[idx];
47+
if (!pMaterial)
48+
continue;
49+
50+
auto it = swapMap.find(pMaterial->texture);
51+
if (it != swapMap.end())
52+
RpMaterialSetTexture(pMaterial, it->second);
53+
}
54+
}
55+
56+
bool ReplaceTextureInAtomicCB(RpAtomic* pAtomic, void* userData)
57+
{
58+
if (!pAtomic)
59+
return true;
60+
61+
auto* swapMap = static_cast<TextureSwapMap*>(userData);
62+
if (!swapMap)
63+
return true;
64+
65+
ReplaceTextureInGeometry(pAtomic->geometry, *swapMap);
66+
return true;
67+
}
68+
69+
void ReplaceTextureInModel(CModelInfoSA* pModelInfo, TextureSwapMap& swapMap)
70+
{
71+
if (!pModelInfo || swapMap.empty())
72+
return;
73+
74+
RwObject* pRwObject = pModelInfo->GetRwObject();
75+
if (!pRwObject)
76+
return;
77+
78+
switch (pModelInfo->GetModelType())
79+
{
80+
case eModelInfoType::ATOMIC:
81+
case eModelInfoType::TIME:
82+
case eModelInfoType::LOD_ATOMIC:
83+
{
84+
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(pRwObject);
85+
if (pAtomic)
86+
ReplaceTextureInGeometry(pAtomic->geometry, swapMap);
87+
break;
88+
}
89+
90+
case eModelInfoType::WEAPON:
91+
case eModelInfoType::CLUMP:
92+
case eModelInfoType::VEHICLE:
93+
case eModelInfoType::PED:
94+
case eModelInfoType::UNKNOWN:
95+
default:
96+
{
97+
RpClump* pClump = reinterpret_cast<RpClump*>(pRwObject);
98+
if (pClump)
99+
RpClumpForAllAtomics(pClump, ReplaceTextureInAtomicCB, &swapMap);
100+
break;
101+
}
102+
}
103+
}
104+
}
28105

29106
////////////////////////////////////////////////////////////////
30107
//
@@ -192,6 +269,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
192269
RwTexDictionaryRemoveTexture(pInfo->pTxd, pExistingTexture);
193270
}
194271

272+
perTxdInfo.replacedOriginals.push_back(pExistingTexture);
273+
195274
// Add the texture
196275
dassert(!RwTexDictionaryContainsTexture(pInfo->pTxd, pNewTexture));
197276
RwTexDictionaryAddTexture(pInfo->pTxd, pNewTexture);
@@ -226,6 +305,48 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
226305
dassert(MapFind(ms_ModelTexturesInfoMap, usTxdId));
227306
dassert(ListContains(pInfo->usedByReplacements, pReplacementTextures));
228307

308+
TextureSwapMap swapMap;
309+
swapMap.reserve(perTxdInfo.usingTextures.size());
310+
311+
for (size_t idx = 0; idx < perTxdInfo.usingTextures.size(); ++idx)
312+
{
313+
RwTexture* pOldTexture = perTxdInfo.usingTextures[idx];
314+
if (!pOldTexture)
315+
continue;
316+
317+
RwTexture* pOriginalTexture = (idx < perTxdInfo.replacedOriginals.size()) ? perTxdInfo.replacedOriginals[idx] : nullptr;
318+
swapMap[pOldTexture] = pOriginalTexture;
319+
320+
if (pOriginalTexture && !RwTexDictionaryContainsTexture(pInfo->pTxd, pOriginalTexture))
321+
RwTexDictionaryAddTexture(pInfo->pTxd, pOriginalTexture);
322+
}
323+
324+
if (!swapMap.empty())
325+
{
326+
std::vector<CModelInfoSA*> targetModels;
327+
targetModels.reserve(pReplacementTextures->usedInModelIds.size());
328+
std::unordered_set<CModelInfoSA*> seenModels;
329+
seenModels.reserve(pReplacementTextures->usedInModelIds.size());
330+
331+
for (ushort modelId : pReplacementTextures->usedInModelIds)
332+
{
333+
CModelInfoSA* pModelInfo = dynamic_cast<CModelInfoSA*>(pGame->GetModelInfo(modelId));
334+
if (!pModelInfo)
335+
continue;
336+
337+
if (pModelInfo->GetTextureDictionaryID() != perTxdInfo.usTxdId)
338+
continue;
339+
340+
if (seenModels.insert(pModelInfo).second)
341+
targetModels.push_back(pModelInfo);
342+
}
343+
344+
for (CModelInfoSA* pModelInfo : targetModels)
345+
{
346+
ReplaceTextureInModel(pModelInfo, swapMap);
347+
}
348+
}
349+
229350
// Remove replacement textures
230351
for (uint i = 0; i < perTxdInfo.usingTextures.size(); i++)
231352
{
@@ -235,11 +356,14 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
235356
if (perTxdInfo.bTexturesAreCopies)
236357
{
237358
// Destroy the copy (but not the raster as that was not copied)
238-
pOldTexture->raster = NULL;
359+
std::exchange(pOldTexture->raster, nullptr);
239360
RwTextureDestroy(pOldTexture);
240361
}
241362
}
242363

364+
perTxdInfo.usingTextures.clear();
365+
perTxdInfo.replacedOriginals.clear();
366+
243367
// Ensure there are original named textures in the txd
244368
for (uint i = 0; i < pInfo->originalTextures.size(); i++)
245369
{

Client/multiplayer_sa/CMultiplayerSA_RwResources.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
*****************************************************************************/
1111

1212
#include "StdInc.h"
13+
#include <cstddef>
1314

1415
namespace
1516
{
1617
SRwResourceStats ms_Stats;
18+
19+
constexpr std::size_t kTextureRefsReadableSize = offsetof(RwTexture, refs) + sizeof(int);
20+
constexpr std::size_t kGeometryRefsReadableSize = offsetof(RwGeometry, refs) + sizeof(short);
21+
1722
}
1823

1924
//////////////////////////////////////////////////////////////////////////////////////////
@@ -27,6 +32,9 @@ void OnMY_RwTextureCreate_Pre(DWORD calledFrom)
2732

2833
void OnMY_RwTextureCreate_Post(RwTexture* pTexture, DWORD calledFrom)
2934
{
35+
if (!pTexture)
36+
return;
37+
3038
ms_Stats.uiTextures++;
3139
}
3240

@@ -72,7 +80,17 @@ static void __declspec(naked) HOOK_RwTextureCreate()
7280
//////////////////////////////////////////////////////////////////////////////////////////
7381
void OnMY_RwTextureDestroy(RwTexture* pTexture, DWORD calledFrom)
7482
{
75-
if (pTexture->refs == 1)
83+
if (!pTexture)
84+
return;
85+
86+
if (SharedUtil::IsReadablePointer(pTexture, kTextureRefsReadableSize))
87+
{
88+
if (pTexture->refs == 1 && ms_Stats.uiTextures > 0)
89+
ms_Stats.uiTextures--;
90+
return;
91+
}
92+
93+
if (ms_Stats.uiTextures > 0)
7694
ms_Stats.uiTextures--;
7795
}
7896

@@ -174,6 +192,9 @@ void OnMY_RwGeometryCreate_Pre(DWORD calledFrom)
174192

175193
void OnMY_RwGeometryCreate_Post(RwGeometry* pGeometry, DWORD calledFrom)
176194
{
195+
if (!pGeometry)
196+
return;
197+
177198
ms_Stats.uiGeometries++;
178199
}
179200

@@ -222,7 +243,17 @@ static void __declspec(naked) HOOK_RwGeometryCreate()
222243
//////////////////////////////////////////////////////////////////////////////////////////
223244
void OnMY_RwGeometryDestroy(DWORD calledFrom, RwGeometry* pGeometry)
224245
{
225-
if (pGeometry->refs == 1)
246+
if (!pGeometry)
247+
return;
248+
249+
if (SharedUtil::IsReadablePointer(pGeometry, kGeometryRefsReadableSize))
250+
{
251+
if (pGeometry->refs == 1 && ms_Stats.uiGeometries > 0)
252+
ms_Stats.uiGeometries--;
253+
return;
254+
}
255+
256+
if (ms_Stats.uiGeometries > 0)
226257
ms_Stats.uiGeometries--;
227258
}
228259

Client/sdk/game/CRenderWare.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct SReplacementTextures
3535
struct SPerTxd
3636
{
3737
std::vector<RwTexture*> usingTextures;
38+
std::vector<RwTexture*> replacedOriginals;
3839
ushort usTxdId;
3940
bool bTexturesAreCopies;
4041
};

0 commit comments

Comments
 (0)