Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fcef9e4
Adds functions for adding new CJ clothes
W3lac3 Feb 22, 2025
904382c
Fix spelling error
W3lac3 Feb 22, 2025
c727575
Merge remote-tracking branch 'upstream/master' into feature/add-new-c…
W3lac3 Feb 28, 2025
7ad1e7e
Merge remote-tracking branch 'upstream/master' into feature/add-new-c…
W3lac3 Mar 8, 2025
17a19f5
Refactor clothing list and fix crash
W3lac3 Mar 8, 2025
c8e6319
Add new clothing directory size
W3lac3 Mar 17, 2025
5318993
Update CDirectorySA.cpp
W3lac3 Mar 17, 2025
43186ea
Update CDirectorySA
W3lac3 Mar 19, 2025
3f1cf67
Update CRenderWareSA.ClothesReplacing
W3lac3 Mar 19, 2025
43b922c
Update CMultiplayerSA_ClothesSpeedUp.cpp
W3lac3 Mar 19, 2025
88e0484
Merge remote-tracking branch 'upstream/master' into feature/add-new-c…
W3lac3 Mar 19, 2025
0886f1f
Removed clothing model functions from CStaticFunctionDefinitions
W3lac3 Mar 20, 2025
e4cc7d9
Update due to code revision
W3lac3 Mar 20, 2025
294bd7f
Fix crash when clothing has removed
W3lac3 Mar 20, 2025
99e5a65
Merge remote-tracking branch 'upstream/master' into feature/add-new-c…
W3lac3 Mar 29, 2025
fda03ce
Change AddClothingTexture/AddClothingModel to const std::string&
W3lac3 Mar 29, 2025
7952f16
Change g_playerImgEntries and g_playerImgSize to static
W3lac3 Mar 29, 2025
b14cfbf
Remove sz prefix from SPlayerClothing structure
W3lac3 Mar 31, 2025
5ec40bc
Add null terminator to entry.m_name in CRenderWareSA::ClothesAddFile
W3lac3 Mar 31, 2025
c9154b3
Add condition to check if m_numEntries is zero
W3lac3 Mar 31, 2025
912f284
Rename clothesReplacementChanged to remove Hungarian notation
W3lac3 Apr 4, 2025
74ebac3
Merge remote-tracking branch 'upstream/master' into feature/add-new-c…
W3lac3 Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Client/game_sa/CDirectorySA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,70 @@
#include "StdInc.h"
#include "CDirectorySA.h"

bool CDirectorySAInterface::AddEntry(DirectoryInfoSA& entry)
{
if (m_nNumEntries >= m_nCapacity)
return false;

if (GetModelEntry(entry.m_szName))
return false;

entry.m_nOffset = 0;

if (m_nNumEntries > 0)
{
DirectoryInfoSA* lastEntry = m_pEntries + m_nNumEntries - 1;
entry.m_nOffset = lastEntry->m_nOffset + lastEntry->m_nStreamingSize;

if (entry.m_nOffset % 2048)
entry.m_nOffset += 2048 - (entry.m_nOffset % 2048);
}

m_pEntries[m_nNumEntries++] = entry;

return true;
}

bool CDirectorySAInterface::RemoveEntry(const char* pFileName)
{
if (m_nNumEntries <= 0)
return false;

DirectoryInfoSA* entry = GetModelEntry(pFileName);

if (!entry)
return false;

std::uint32_t index = entry - m_pEntries;

if (index < m_nNumEntries - 1)
{
DirectoryInfoSA* lastEntry = m_pEntries + m_nNumEntries - 1;
entry->m_nOffset = lastEntry->m_nOffset + lastEntry->m_nSizeInArchive;
}

m_nNumEntries--;

if (index < m_nNumEntries)
memmove(entry, entry + 1, (m_nNumEntries - index) * sizeof(DirectoryInfoSA));

return true;
}

DirectoryInfoSA* CDirectorySAInterface::GetModelEntry(const char* pFileName)
{
if (m_nNumEntries <= 0)
return nullptr;

for (DirectoryInfoSA* it = m_pEntries; it != m_pEntries + m_nNumEntries; it++)
{
if (strcmp(it->m_szName, pFileName) == 0)
return it;
}

return nullptr;
}

DirectoryInfoSA* CDirectorySAInterface::GetModelEntry(std::uint16_t modelId)
{
if (m_nNumEntries <= 0)
Expand Down
4 changes: 4 additions & 0 deletions Client/game_sa/CDirectorySA.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ struct DirectoryInfoSA
class CDirectorySAInterface
{
public:
bool AddEntry(DirectoryInfoSA& entry);
bool RemoveEntry(const char* pFileName);

DirectoryInfoSA* GetModelEntry(const char* pFileName);
DirectoryInfoSA* GetModelEntry(std::uint16_t modelId);
bool SetModelStreamingSize(std::uint16_t modelId, std::uint16_t size);
std::uint16_t GetModelStreamingSize(std::uint16_t modelId);
Expand Down
109 changes: 100 additions & 9 deletions Client/game_sa/CRenderWareSA.ClothesReplacing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace

std::unordered_map<ushort, char*> ms_ReplacementClothesFileDataMap;
std::unordered_map<ushort, std::uint16_t> ms_OriginalStreamingSizesMap;
std::unordered_map<std::string, char*> ms_ClothesFileDataMap;

bool bClothesReplacementChanged = false;

Expand Down Expand Up @@ -128,6 +129,83 @@ bool CRenderWareSA::HasClothesReplacementChanged()
return bResult;
}

////////////////////////////////////////////////////////////////
//
// CRenderWareSA::ClothesAddFile
//
// Add a file to the clothes directory
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::ClothesAddFile(const char* pFileData, size_t fileSize, const char* pFileName)
{
if (!pFileData || !pFileName)
return false;

if (MapFind(ms_ClothesFileDataMap, pFileName))
return false;

DirectoryInfoSA entry{};
entry.m_nStreamingSize = GetSizeInBlocks(fileSize);
strncpy(entry.m_szName, pFileName, sizeof(entry.m_szName));

if (!g_clothesDirectory->AddEntry(entry))
return false;

MapSet(ms_ClothesFileDataMap, pFileName, (char*)pFileData);
bClothesReplacementChanged = true;

return true;
}

////////////////////////////////////////////////////////////////
//
// CRenderWareSA::ClothesRemoveFile
//
// Remove a file from the clothes directory
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::ClothesRemoveFile(char* pFileData)
{
if (!pFileData)
return false;

for (auto iter = ms_ClothesFileDataMap.begin(); iter != ms_ClothesFileDataMap.end();)
{
if (iter->second == pFileData)
{
if (!g_clothesDirectory->RemoveEntry(iter->first.c_str()))
return false;

iter = ms_ClothesFileDataMap.erase(iter);
bClothesReplacementChanged = true;
}
else
++iter;
}
}

////////////////////////////////////////////////////////////////
//
// CRenderWareSA::HasClothesFile
//
// Check if clothe file exits
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::HasClothesFile(const char* pFileName)
{
if (!pFileName)
{
return false;
}

if (!MapFind(ms_ClothesFileDataMap, pFileName))
{
return false;
}

return true;
}

////////////////////////////////////////////////////////////////
//
// CStreaming_RequestModel_Mid
Expand All @@ -143,34 +221,47 @@ __declspec(noinline) bool _cdecl OnCStreaming_RequestModel_Mid(int flags, SImgGT
return false;

// Early out if no clothes textures to replace with
if (ms_ReplacementClothesFileDataMap.empty())
if (ms_ReplacementClothesFileDataMap.empty() && ms_ClothesFileDataMap.empty())
return false;

// Initialze lookup map if needed
static std::map<uint, int> blockOffsetToFileIdMap;
static std::map<uint, std::string> blockOffsetToFileNameMap;
if (blockOffsetToFileIdMap.empty())
{
// Check is player.img dir has been loaded by GTA
SPlayerImgItemArray* pItemArray = (SPlayerImgItemArray*)0x00BC12C0;
if (!pItemArray->pItems || pItemArray->uiArraySize != 542)
std::uint32_t maxArraySize = 542 + ms_ClothesFileDataMap.size();

if (!pItemArray->pItems || pItemArray->uiArraySize != maxArraySize)
return false;

for (uint i = 0; i < pItemArray->uiArraySize; i++)
{
SPlayerImgItem* pImgItem = &pItemArray->pItems[i];
MapSet(blockOffsetToFileIdMap, pImgItem->uiBlockOffset, i);
MapSet(blockOffsetToFileNameMap, pImgItem->uiBlockOffset, pImgItem->szName);
}
}

// Get player.img fileId by comparing the supplied BlockOffset with entries in the player.img dir
int* piPlayerImgFileId = MapFind(blockOffsetToFileIdMap, pImgGTAInfo->iBlockOffset);
if (!piPlayerImgFileId)
return false;
char* pReplacementFileData = nullptr;
int* piPlayerImgFileId = MapFind(blockOffsetToFileIdMap, pImgGTAInfo->iBlockOffset);

if (piPlayerImgFileId)
{
pReplacementFileData = MapFindRef(ms_ReplacementClothesFileDataMap, *piPlayerImgFileId);
}

if (!pReplacementFileData)
{
std::string* pFileName = MapFind(blockOffsetToFileNameMap, pImgGTAInfo->iBlockOffset);

int iPlayerImgFileId = *piPlayerImgFileId;
if (pFileName)
{
pReplacementFileData = MapFindRef(ms_ClothesFileDataMap, *pFileName);
}
}

// Do we have a replacement for this clothes texture?
char* pReplacementFileData = MapFindRef(ms_ReplacementClothesFileDataMap, iPlayerImgFileId);
if (!pReplacementFileData)
return false;

Expand Down
3 changes: 3 additions & 0 deletions Client/game_sa/CRenderWareSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class CRenderWareSA : public CRenderWare
void ClothesAddReplacement(char* pFileData, size_t fileSize, ushort usFileId);
void ClothesRemoveReplacement(char* pFileData);
bool HasClothesReplacementChanged();
bool ClothesAddFile(const char* pFileData, size_t fileSize, const char* pFileName);
bool ClothesRemoveFile(char* pFileData);
bool HasClothesFile(const char* pFileName);

// Reads and parses a TXD file specified by a path (szTXD)
RwTexDictionary* ReadTXD(const SString& strFilename, const SString& buffer);
Expand Down
20 changes: 20 additions & 0 deletions Client/mods/deathmatch/logic/CClientDFF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ void CClientDFF::UnloadDFF()
m_LoadedClumpInfoMap.clear();
}

bool CClientDFF::AddClothingModel(std::string strModelName)
{
if (strModelName.empty())
return false;

if (m_RawDataBuffer.empty() && m_bIsRawData)
return false;

if (m_RawDataBuffer.empty())
{
if (!FileLoad(std::nothrow, m_strDffFilename, m_RawDataBuffer))
return false;
}

return g_pGame->GetRenderWare()->ClothesAddFile(m_RawDataBuffer.data(), m_RawDataBuffer.size(), strModelName.c_str());
}

bool CClientDFF::ReplaceModel(unsigned short usModel, bool bAlphaTransparency)
{
// Record attempt in case it all goes wrong
Expand Down Expand Up @@ -232,6 +249,9 @@ void CClientDFF::RestoreModels()
InternalRestoreModel(*iter);
}

// Remove all clothes models
g_pGame->GetRenderWare()->ClothesRemoveFile(m_RawDataBuffer.data());

// Clear the list
m_Replaced.clear();
}
Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientDFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class CClientDFF final : public CClientEntity

bool Load(bool isRaw, SString input);

bool AddClothingModel(std::string strModelName);
bool ReplaceModel(unsigned short usModel, bool bAlphaTransparency);

bool HasReplaced(unsigned short usModel);
Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/CClientModelCacheManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void CClientModelCacheManagerImpl::DoPulse()
DoPulseVehicleModels();

// Handle regeneration of possibly replaced clothes textures
if (g_pGame->GetRenderWare()->HasClothesReplacementChanged())
if (g_pGame->GetRenderWare()->HasClothesReplacementChanged() || CClientPlayerClothes::HasClothesChanged())
{
g_pMultiplayer->FlushClothesCache();

Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientPed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4063,6 +4063,7 @@ void CClientPed::RebuildModel(bool bDelayChange)
if (m_ulModel == 0)
{
// Adds only the neccesary textures
m_pClothes->RefreshClothes();
m_pClothes->AddAllToModel();

m_bPendingRebuildPlayer = true;
Expand Down
Loading
Loading