Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds functions for adding new CJ clothing files #4050

Merged
merged 22 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
74 changes: 66 additions & 8 deletions Client/game_sa/CDirectorySA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,73 @@
#include "StdInc.h"
#include "CDirectorySA.h"

bool CDirectorySAInterface::AddEntry(DirectoryInfoSA& entry)
{
if (m_numEntries >= m_capacity || GetModelEntry(entry.m_name))
return false;

entry.m_offset = 0;

if (m_numEntries > 0)
{
DirectoryInfoSA* lastEntry = m_entries + m_numEntries - 1;
entry.m_offset = lastEntry->m_offset + lastEntry->m_streamingSize;

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

m_entries[m_numEntries++] = entry;

return true;
}

bool CDirectorySAInterface::RemoveEntry(const char* fileName)
{
if (m_numEntries == 0)
return false;

DirectoryInfoSA* entry = GetModelEntry(fileName);

if (!entry)
return false;

std::ptrdiff_t index = entry - m_entries;

if (index < m_numEntries - 1)
{
DirectoryInfoSA* lastEntry = m_entries + m_numEntries - 1;
entry->m_offset = lastEntry->m_offset + lastEntry->m_sizeInArchive;
}

m_numEntries--;

if (index < m_numEntries)
std::memmove(entry, entry + 1, (m_numEntries - index) * sizeof(DirectoryInfoSA));

return true;
}

DirectoryInfoSA* CDirectorySAInterface::GetModelEntry(const char* fileName)
{
if (m_numEntries == 0)
return nullptr;

for (DirectoryInfoSA* it = m_entries; it != m_entries + m_numEntries; it++)
{
if (std::strcmp(it->m_name, fileName) == 0)
return it;
}

return nullptr;
}

DirectoryInfoSA* CDirectorySAInterface::GetModelEntry(std::uint16_t modelId)
{
if (m_nNumEntries <= 0)
if (m_numEntries == 0)
return nullptr;

DirectoryInfoSA* entry = m_pEntries + modelId;
DirectoryInfoSA* entry = m_entries + modelId;

if (!entry)
return nullptr;
Expand All @@ -28,13 +89,10 @@ bool CDirectorySAInterface::SetModelStreamingSize(std::uint16_t modelId, std::ui
{
DirectoryInfoSA* entry = GetModelEntry(modelId);

if (!entry)
return false;

if (entry->m_nStreamingSize == size)
if (!entry || entry->m_streamingSize == size)
return false;

entry->m_nStreamingSize = size;
entry->m_streamingSize = size;
return true;
}

Expand All @@ -46,5 +104,5 @@ std::uint16_t CDirectorySAInterface::GetModelStreamingSize(std::uint16_t modelId
if (!entry)
return 0;

return entry->m_nStreamingSize;
return entry->m_streamingSize;
}
20 changes: 12 additions & 8 deletions Client/game_sa/CDirectorySA.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,26 @@

struct DirectoryInfoSA
{
std::uint32_t m_nOffset;
std::uint16_t m_nStreamingSize;
std::uint16_t m_nSizeInArchive;
char m_szName[24];
std::uint32_t m_offset;
std::uint16_t m_streamingSize;
std::uint16_t m_sizeInArchive;
char m_name[24];
};

class CDirectorySAInterface
{
public:
bool AddEntry(DirectoryInfoSA& entry);
bool RemoveEntry(const char* fileName);

DirectoryInfoSA* GetModelEntry(const char* fileName);
DirectoryInfoSA* GetModelEntry(std::uint16_t modelId);
bool SetModelStreamingSize(std::uint16_t modelId, std::uint16_t size);
std::uint16_t GetModelStreamingSize(std::uint16_t modelId);

private:
DirectoryInfoSA* m_pEntries{};
std::uint32_t m_nCapacity{};
std::uint32_t m_nNumEntries{};
bool m_bOwnsEntries{};
DirectoryInfoSA* m_entries{};
std::uint32_t m_capacity{};
std::uint32_t m_numEntries{};
bool m_ownsEntries{};
};
116 changes: 98 additions & 18 deletions Client/game_sa/CRenderWareSA.ClothesReplacing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ 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;
bool clothesReplacementChanged = false;

struct SPlayerImgItem
{
Expand Down Expand Up @@ -78,7 +79,7 @@ void CRenderWareSA::ClothesAddReplacement(char* pFileData, size_t fileSize, usho
MapSet(ms_OriginalStreamingSizesMap, usFileId, g_clothesDirectory->GetModelStreamingSize(usFileId));
g_clothesDirectory->SetModelStreamingSize(usFileId, GetSizeInBlocks(fileSize));

bClothesReplacementChanged = true;
clothesReplacementChanged = true;
}
}

Expand Down Expand Up @@ -107,7 +108,7 @@ void CRenderWareSA::ClothesRemoveReplacement(char* pFileData)
}

iter = ms_ReplacementClothesFileDataMap.erase(iter);
bClothesReplacementChanged = true;
clothesReplacementChanged = true;
}
else
++iter;
Expand All @@ -123,11 +124,81 @@ void CRenderWareSA::ClothesRemoveReplacement(char* pFileData)
////////////////////////////////////////////////////////////////
bool CRenderWareSA::HasClothesReplacementChanged()
{
bool bResult = bClothesReplacementChanged;
bClothesReplacementChanged = false;
bool bResult = clothesReplacementChanged;
clothesReplacementChanged = false;
return bResult;
}

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

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

DirectoryInfoSA entry{};
entry.m_streamingSize = GetSizeInBlocks(fileSize);

std::size_t nameSize = sizeof(entry.m_name) - 1;
std::strncpy(entry.m_name, fileName, nameSize);
entry.m_name[nameSize] = '\0';

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

MapSet(ms_ClothesFileDataMap, fileName, const_cast<char*>(fileData));
clothesReplacementChanged = true;

return true;
}

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

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

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

////////////////////////////////////////////////////////////////
//
// CRenderWareSA::HasClothesFile
//
// Check if clothe file exits
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::HasClothesFile(const char* fileName) const noexcept
{
return fileName && MapFind(ms_ClothesFileDataMap, fileName);
}

////////////////////////////////////////////////////////////////
//
// CStreaming_RequestModel_Mid
Expand All @@ -143,35 +214,44 @@ __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<std::uint32_t, int> blockOffsetToFileIdMap;
static std::map<std::uint32_t, 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++)
for (std::uint32_t 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* replacementFileData = nullptr;
int* playerImgFileId = MapFind(blockOffsetToFileIdMap, pImgGTAInfo->iBlockOffset);

if (playerImgFileId)
replacementFileData = MapFindRef(ms_ReplacementClothesFileDataMap, *playerImgFileId);

int iPlayerImgFileId = *piPlayerImgFileId;
if (!replacementFileData)
{
std::string* fileName = MapFind(blockOffsetToFileNameMap, pImgGTAInfo->iBlockOffset);

if (fileName)
replacementFileData = MapFindRef(ms_ClothesFileDataMap, *fileName);
}

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

// If bLoadingBigModel is set, try to get it unset
Expand All @@ -187,7 +267,7 @@ __declspec(noinline) bool _cdecl OnCStreaming_RequestModel_Mid(int flags, SImgGT

// Set results
iReturnFileId = ((char*)pImgGTAInfo - (char*)CStreaming__ms_aInfoForModel) / 20;
pReturnBuffer = pReplacementFileData;
pReturnBuffer = replacementFileData;

// Update flags
pImgGTAInfo->uiLoadflag = 3;
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* fileData, std::size_t fileSize, const char* fileName) override;
bool ClothesRemoveFile(char* fileData) override;
bool HasClothesFile(const char* fileName) const noexcept override;

// 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(const std::string& modelName)
{
if (modelName.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(), modelName.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(const std::string& modelName);
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