Skip to content

Commit

Permalink
Allow allocating new TXD's (#1802)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladislav Nikolaevich <[email protected]>
Co-authored-by: Pirulax <[email protected]>
  • Loading branch information
3 people authored Aug 30, 2023
1 parent 3e9a373 commit fdaa114
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 29 deletions.
4 changes: 4 additions & 0 deletions Client/game_sa/CGameSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ extern unsigned int OBJECTDYNAMICINFO_MAX; // default: 160
#define NUM_WeaponInfosOtherSkill 11
#define NUM_WeaponInfosTotal (NUM_WeaponInfosStdSkill + (3*NUM_WeaponInfosOtherSkill)) // std, (poor, pro, special)

#define MODELINFO_DFF_MAX 20000
#define MODELINFO_TXD_MAX 25000
#define MODELINFO_MAX 26000 // Actual max is 25755

#define VAR_FlyingCarsEnabled 0x969160
#define VAR_ExtraBunnyhopEnabled 0x969161
#define VAR_HoveringCarsEnabled 0x969152
Expand Down
3 changes: 3 additions & 0 deletions Client/game_sa/CModelInfoSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,9 @@ CBoundingBox* CModelInfoSA::GetBoundingBox()

bool CModelInfoSA::IsValid()
{
if (m_dwModelID >= MODELINFO_DFF_MAX && m_dwModelID < MODELINFO_TXD_MAX)
return !pGame->GetPools()->IsFreeTextureDictonarySlot(m_dwModelID - MODELINFO_DFF_MAX);

if (m_dwModelID >= pGame->GetBaseIDforTXD() && m_dwModelID < pGame->GetCountOfAllFileIDs())
return true;

Expand Down
39 changes: 39 additions & 0 deletions Client/game_sa/CPoolsSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "CTrailerSA.h"
#include "CTrainSA.h"
#include "CWorldSA.h"
#include "CKeyGenSA.h"

extern CGameSA* pGame;

Expand All @@ -31,6 +32,7 @@ CPoolsSA::CPoolsSA()
m_ppPedPoolInterface = (CPoolSAInterface<CPedSAInterface>**)0xB74490;
m_ppObjectPoolInterface = (CPoolSAInterface<CObjectSAInterface>**)0xB7449C;
m_ppVehiclePoolInterface = (CPoolSAInterface<CVehicleSAInterface>**)0xB74494;
m_ppTxdPoolInterface = (CPoolSAInterface<CTextureDictonarySAInterface>**)0xC8800C;

m_bGetVehicleEnabled = true;
}
Expand Down Expand Up @@ -1053,3 +1055,40 @@ void CPoolsSA::InvalidateLocalPlayerClientEntity()
{
m_pedPool.arrayOfClientEntities[0] = {m_pedPool.arrayOfClientEntities[0].pEntity, nullptr};
}

unsigned int CPoolsSA::AllocateTextureDictonarySlot(uint uiSlotId, std::string& strTxdName)
{
CTextureDictonarySAInterface* pTxd = (*m_ppTxdPoolInterface)->AllocateAt(uiSlotId);
if (!pTxd)
return -1;

strTxdName.resize(24);

pTxd->usUsagesCount = 0;
pTxd->hash = pGame->GetKeyGen()->GetUppercaseKey(strTxdName.c_str());
pTxd->rwTexDictonary = nullptr;
pTxd->usParentIndex = -1;

return (*m_ppTxdPoolInterface)->GetObjectIndex(pTxd);
}

void CPoolsSA::RemoveTextureDictonarySlot(uint uiTxdId)
{
if (!(*m_ppTxdPoolInterface)->IsContains(uiTxdId))
return;

typedef uint(__cdecl * Function_TxdReleaseSlot)(uint uiTxdId);
((Function_TxdReleaseSlot)(0x731E90))(uiTxdId);

(*m_ppTxdPoolInterface)->Release(uiTxdId);
}

bool CPoolsSA::IsFreeTextureDictonarySlot(uint uiTxdId)
{
return (*m_ppTxdPoolInterface)->IsEmpty(uiTxdId);
}

ushort CPoolsSA::GetFreeTextureDictonarySlot()
{
return (*m_ppTxdPoolInterface)->GetFreeSlot();
}
84 changes: 84 additions & 0 deletions Client/game_sa/CPoolsSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "CPedSA.h"
#include "CVehicleSA.h"
#include "CObjectSA.h"
#include "CTextureDictonarySA.h"

#define INVALID_POOL_ARRAY_ID 0xFFFFFFFF

Expand Down Expand Up @@ -54,8 +55,84 @@ class CPoolSAInterface
m_bOwnsAllocations = false;
}

uint GetFreeSlot()
{
bool bLooped = false;
uint index = m_nFirstFree + 1;

while (true)
{
if (index >= m_nSize)
{
if (bLooped)
return -1;

index = 0;
bLooped = true;
}

if (m_byteMap[index].bEmpty)
{
m_nFirstFree = index;
return index;
}
index++;
}

return -1;
};

B* Allocate()
{
m_nFirstFree++; // Continue after the last allocated slot
const auto sz = m_nSize; // Storing size to avoid reloads from memory - should help out the optimizer
for (auto i = 0u; i < sz; i++) {
const auto slot = (m_nFirstFree + i) % sz;
const auto e = &m_byteMap[slot];
if (!e->bEmpty) {
continue;
}
m_nFirstFree = slot;
e->bEmpty = false;
e->nId++;
return &m_pObjects[slot];
}
return nullptr;
}

B* AllocateAt(uint uiSlot)
{
m_pObjects[uiSlot] = B();
m_byteMap[uiSlot].bEmpty = false;
m_byteMap[uiSlot].nId ^= uiSlot ^ (uiSlot + 1);

return &m_pObjects[uiSlot];
}

void Release(uint index)
{
m_byteMap[index].bEmpty = true;
m_byteMap[index].nId = 0;
if (index == m_nFirstFree)
--m_nFirstFree;
}

void Delete(uint index)
{
Release(index);
}

bool IsEmpty(std::int32_t objectIndex) { return m_byteMap[objectIndex].bEmpty; }
bool IsContains(uint index)
{
if (m_nSize <= index)
return false;
return !IsEmpty(index);
}

B* GetObject(std::int32_t objectIndex) { return &m_pObjects[objectIndex]; }

uint GetObjectIndex(B* pObject) { return ((DWORD)pObject - (DWORD)m_pObjects) / sizeof(B); }
};

class CPoolsSA : public CPools
Expand Down Expand Up @@ -127,6 +204,12 @@ class CPoolsSA : public CPools
void ResetPedPoolCount() { m_pedPool.ulCount = 0; }
void InvalidateLocalPlayerClientEntity();

uint AllocateTextureDictonarySlot(uint uiSlotID, std::string& strTxdName);
void RemoveTextureDictonarySlot(uint uiTxdId);
bool IsFreeTextureDictonarySlot(uint uiTxdId);

ushort GetFreeTextureDictonarySlot();

private:
// Generic container for pools
template <class T, class I, unsigned long MAX>
Expand Down Expand Up @@ -157,6 +240,7 @@ class CPoolsSA : public CPools
CPoolSAInterface<CPedSAInterface>** m_ppPedPoolInterface;
CPoolSAInterface<CObjectSAInterface>** m_ppObjectPoolInterface;
CPoolSAInterface<CVehicleSAInterface>** m_ppVehiclePoolInterface;
CPoolSAInterface<CTextureDictonarySAInterface>** m_ppTxdPoolInterface;

bool m_bGetVehicleEnabled;
};
Expand Down
23 changes: 23 additions & 0 deletions Client/game_sa/CTextureDictonarySA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CTextureDictonarySA.h
* PURPOSE: Header file for game texture dictonary class
*
* Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

#include <game/RenderWare.h>

class CTextureDictonarySAInterface
{
public:
RwTexDictionary* rwTexDictonary;
unsigned short usUsagesCount;
unsigned short usParentIndex;
unsigned int hash;
};
51 changes: 47 additions & 4 deletions Client/mods/deathmatch/logic/CClientModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*****************************************************************************/

#include "StdInc.h"
#include "game/CStreaming.h"

CClientModel::CClientModel(CClientManager* pManager, int iModelID, eClientModelType eModelType)
{
Expand Down Expand Up @@ -77,19 +78,30 @@ bool CClientModel::Allocate(ushort usParentID)
return false;
}

bool CClientModel::Deallocate(void)
bool CClientModel::Deallocate()
{
if (!m_bAllocatedByUs)
return false;
CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true);
if (!pModelInfo || !pModelInfo->IsValid())
return false;
pModelInfo->DeallocateModel();

SetParentResource(nullptr);
return true;

switch (m_eModelType)
{
case eClientModelType::PED:
case eClientModelType::OBJECT:
case eClientModelType::VEHICLE:
return DeallocateDFF(pModelInfo);
case eClientModelType::TXD:
return DeallocateTXD(pModelInfo);
default:
return false;
}
}

void CClientModel::RestoreEntitiesUsingThisModel()
bool CClientModel::DeallocateDFF(CModelInfo* pModelInfo)
{
auto unloadModelsAndCallEvents = [&](auto iterBegin, auto iterEnd, unsigned short usParentID, auto setElementModelLambda) {
for (auto iter = iterBegin; iter != iterEnd; iter++)
Expand Down Expand Up @@ -152,4 +164,35 @@ void CClientModel::RestoreEntitiesUsingThisModel()

// Restore DFF/TXD
g_pClientGame->GetManager()->GetDFFManager()->RestoreModel(m_iModelID);

return true;
}

bool CClientModel::AllocateTXD(std::string &strTxdName)
{
uint uiSlotID = g_pGame->GetPools()->AllocateTextureDictonarySlot(m_iModelID - MAX_MODEL_DFF_ID, strTxdName);
if (uiSlotID != -1)
{
m_bAllocatedByUs = true;
return true;
}
return false;
}

bool CClientModel::DeallocateTXD(CModelInfo* pModelInfo)
{
uint uiTextureDictonarySlotID = pModelInfo->GetModel() - MAX_MODEL_DFF_ID;

for (uint uiModelID = 0; uiModelID < MAX_MODEL_DFF_ID; uiModelID++)
{
CModelInfo* pModelInfo = g_pGame->GetModelInfo(uiModelID, true);

if (pModelInfo->GetTextureDictionaryID() == uiTextureDictonarySlotID)
pModelInfo->SetTextureDictionaryID(0);
}

g_pGame->GetPools()->RemoveTextureDictonarySlot(uiTextureDictonarySlotID);
g_pGame->GetStreaming()->SetStreamingInfo(pModelInfo->GetModel(), 0, 0, 0, -1);

return true;
}
7 changes: 6 additions & 1 deletion Client/mods/deathmatch/logic/CClientModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum class eClientModelType
VEHICLE,
TIMED_OBJECT,
CLUMP,
TXD,
};

class CResource;
Expand All @@ -37,11 +38,15 @@ class CClientModel final
int GetModelID(void) const { return m_iModelID; };
eClientModelType GetModelType(void) const { return m_eModelType; };
bool Allocate(ushort usParentID);
bool AllocateTXD(std::string& strTxdName);
bool Deallocate(void);
void RestoreEntitiesUsingThisModel();
void SetParentResource(CResource* pResource) { m_pParentResource = pResource; }
CResource* GetParentResource(void) const { return m_pParentResource; }

private:
bool DeallocateDFF(CModelInfo* pModelInfo);
bool DeallocateTXD(CModelInfo* pModelInfo);

protected:
CClientManager* m_pManager;

Expand Down
Loading

0 comments on commit fdaa114

Please sign in to comment.