diff --git a/Client/game_sa/CDirectorySA.cpp b/Client/game_sa/CDirectorySA.cpp new file mode 100644 index 0000000000..6074d13a46 --- /dev/null +++ b/Client/game_sa/CDirectorySA.cpp @@ -0,0 +1,48 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDirectorySA.cpp + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CDirectorySA.h" + +DirectoryInfo* CDirectorySA::GetModelEntry(ushort modelId) +{ + if (m_nNumEntries <= 0) + return nullptr; + + DirectoryInfo* entry = m_pEntries + modelId; + + if (!entry) + return nullptr; + + return entry; +} + +bool CDirectorySA::SetModelStreamingSize(ushort modelId, uint16 size) +{ + DirectoryInfo* entry = GetModelEntry(modelId); + + if (!entry) + return false; + + if (entry->m_nStreamingSize == size) + return false; + + entry->m_nStreamingSize = size; + return true; +} + + +uint16 CDirectorySA::GetModelStreamingSize(ushort modelId) +{ + DirectoryInfo* entry = GetModelEntry(modelId); + + if (!entry) + return false; + + return entry->m_nStreamingSize; +} diff --git a/Client/game_sa/CDirectorySA.h b/Client/game_sa/CDirectorySA.h new file mode 100644 index 0000000000..37e3dca7a5 --- /dev/null +++ b/Client/game_sa/CDirectorySA.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDirectorySA.h + * + *****************************************************************************/ + +#pragma once + +struct DirectoryInfo +{ + uint32 m_nOffset; + uint16 m_nStreamingSize; + uint16 m_nSizeInArchive; + char m_szName[24]; +}; + +class CDirectorySA +{ + public: + DirectoryInfo* GetModelEntry(ushort modelId); + bool SetModelStreamingSize(ushort modelId, uint16 size); + uint16 GetModelStreamingSize(ushort modelId); + + private: + DirectoryInfo* m_pEntries{}; + uint32 m_nCapacity{}; + uint32 m_nNumEntries{}; + bool m_bOwnsEntries{}; +}; diff --git a/Client/game_sa/CRenderWareSA.ClothesReplacing.cpp b/Client/game_sa/CRenderWareSA.ClothesReplacing.cpp index dc51a90777..fd0c80a259 100644 --- a/Client/game_sa/CRenderWareSA.ClothesReplacing.cpp +++ b/Client/game_sa/CRenderWareSA.ClothesReplacing.cpp @@ -8,6 +8,7 @@ #include "StdInc.h" #include "CGameSA.h" +#include "CDirectorySA.h" #include "gamesa_renderware.h" extern CGameSA* pGame; @@ -28,8 +29,10 @@ namespace uint uiLoadflag; // 0-not loaded 2-requested 3-loaded 1-processed }; - std::map ms_ReplacementClothesFileDataMap; - bool bClothesReplacementChanged = false; + std::map ms_ReplacementClothesFileDataMap; + std::map ms_OriginalStreamingSizesMap; + + bool bClothesReplacementChanged = false; struct SPlayerImgItem { @@ -45,44 +48,67 @@ namespace uint uiArraySize; }; - DWORD FUNC_CStreamingConvertBufferToObject = 0x40C6B0; - int iReturnFileId; - char* pReturnBuffer; + DWORD FUNC_CStreamingConvertBufferToObject = 0x40C6B0; + CDirectorySA* CLOTHES_DIRECTORY = *reinterpret_cast(0x5A419B); + int iReturnFileId; + char* pReturnBuffer; + + + uint32_t GetSizeInBlocks(uint32_t size) + { + auto div = std::div(size, 2048); + return (div.quot + (div.rem ? 1 : 0)); + } + + uint32_t GetSizeInBlocks(uint64_t size) + { + return GetSizeInBlocks((uint32_t)size); + } } // namespace //////////////////////////////////////////////////////////////// // -// CRenderWareSA::ClothesAddReplacementTxd +// CRenderWareSA::ClothesAddReplacement // -// Add replacement txd for a clothing component +// Add replacement txd/dff for a clothing component // //////////////////////////////////////////////////////////////// -void CRenderWareSA::ClothesAddReplacementTxd(char* pFileData, ushort usFileId) +void CRenderWareSA::ClothesAddReplacement(char* pFileData, size_t fileSize, ushort usFileId) { if (!pFileData) return; + if (pFileData != MapFindRef(ms_ReplacementClothesFileDataMap, usFileId)) { MapSet(ms_ReplacementClothesFileDataMap, usFileId, pFileData); + MapSet(ms_OriginalStreamingSizesMap, usFileId, CLOTHES_DIRECTORY->GetModelStreamingSize(usFileId)); + CLOTHES_DIRECTORY->SetModelStreamingSize(usFileId, GetSizeInBlocks(fileSize)); + bClothesReplacementChanged = true; } } //////////////////////////////////////////////////////////////// // -// CRenderWareSA::ClothesRemoveReplacementTxd +// CRenderWareSA::ClothesRemoveReplacement // -// Remove replacement txd for a clothing component +// Remove replacement txd/dff for a clothing component // //////////////////////////////////////////////////////////////// -void CRenderWareSA::ClothesRemoveReplacementTxd(char* pFileData) +void CRenderWareSA::ClothesRemoveReplacement(char* pFileData) { if (!pFileData) return; + for (std::map::iterator iter = ms_ReplacementClothesFileDataMap.begin(); iter != ms_ReplacementClothesFileDataMap.end();) { if (iter->second == pFileData) { + uint16 originalStreamingSize = MapFindRef(ms_OriginalStreamingSizesMap, iter->first); + + if (originalStreamingSize) + CLOTHES_DIRECTORY->SetModelStreamingSize(iter->first, originalStreamingSize); + ms_ReplacementClothesFileDataMap.erase(iter++); bClothesReplacementChanged = true; } @@ -110,7 +136,7 @@ bool CRenderWareSA::HasClothesReplacementChanged() // CStreaming_RequestModel_Mid // // If request is for a file inside player.img (imgId 5) -// then maybe switch to replacement txd file data +// then maybe switch to replacement txd/dff file data // //////////////////////////////////////////////////////////////// __declspec(noinline) bool _cdecl OnCStreaming_RequestModel_Mid(int flags, SImgGTAItemInfo* pImgGTAInfo) diff --git a/Client/game_sa/CRenderWareSA.h b/Client/game_sa/CRenderWareSA.h index 6a0e64ea50..d79d930720 100644 --- a/Client/game_sa/CRenderWareSA.h +++ b/Client/game_sa/CRenderWareSA.h @@ -32,8 +32,8 @@ class CRenderWareSA : public CRenderWare bool ModelInfoTXDLoadTextures(SReplacementTextures* pReplacementTextures, const SString& strFilename, const SString& buffer, bool bFilteringEnabled); bool ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTextures, ushort usModelId); void ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacementTextures); - void ClothesAddReplacementTxd(char* pFileData, ushort usFileId); - void ClothesRemoveReplacementTxd(char* pFileData); + void ClothesAddReplacement(char* pFileData, size_t fileSize, ushort usFileId); + void ClothesRemoveReplacement(char* pFileData); bool HasClothesReplacementChanged(); // Reads and parses a TXD file specified by a path (szTXD) diff --git a/Client/mods/deathmatch/logic/CClientDFF.cpp b/Client/mods/deathmatch/logic/CClientDFF.cpp index 7eb2a40a70..07e718a8b9 100644 --- a/Client/mods/deathmatch/logic/CClientDFF.cpp +++ b/Client/mods/deathmatch/logic/CClientDFF.cpp @@ -139,6 +139,9 @@ bool CClientDFF::DoReplaceModel(unsigned short usModel, bool bAlphaTransparency) if (!CClientDFFManager::IsReplacableModel(usModel)) return false; + if (CClientPlayerClothes::IsValidModel(usModel)) + return ReplaceClothes(usModel); + // Get clump loaded for this model id RpClump* pClump = GetLoadedClump(usModel); @@ -268,6 +271,12 @@ void CClientDFF::InternalRestoreModel(unsigned short usModel) m_pManager->GetObjectManager()->RestreamObjects(usModel); g_pGame->GetModelInfo(usModel)->RestreamIPL(); } + // Is This a clothe ID? + else if (CClientPlayerClothes::IsValidModel(usModel)) + { + g_pGame->GetRenderWare()->ClothesRemoveReplacement(m_RawDataBuffer.data()); + return; + } else return; @@ -296,6 +305,22 @@ void CClientDFF::InternalRestoreModel(unsigned short usModel) } } +bool CClientDFF::ReplaceClothes(ushort usModel) +{ + if (m_RawDataBuffer.empty() && m_bIsRawData) + return false; + + if (m_RawDataBuffer.empty()) + { + if (!FileLoad(std::nothrow, m_strDffFilename, m_RawDataBuffer)) + return false; + } + + m_Replaced.push_back(usModel); + g_pGame->GetRenderWare()->ClothesAddReplacement(m_RawDataBuffer.data(), m_RawDataBuffer.size(), usModel - CLOTHES_MODEL_ID_FIRST); + return true; +} + bool CClientDFF::ReplaceObjectModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency) { // Stream out all the object models with matching ID. diff --git a/Client/mods/deathmatch/logic/CClientDFF.h b/Client/mods/deathmatch/logic/CClientDFF.h index 3b6392c561..0a4db23894 100644 --- a/Client/mods/deathmatch/logic/CClientDFF.h +++ b/Client/mods/deathmatch/logic/CClientDFF.h @@ -59,6 +59,7 @@ class CClientDFF final : public CClientEntity void UnloadDFF(); void InternalRestoreModel(unsigned short usModel); + bool ReplaceClothes(ushort usModel); bool ReplaceObjectModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); bool ReplaceVehicleModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); bool ReplaceWeaponModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); diff --git a/Client/mods/deathmatch/logic/CClientDFFManager.cpp b/Client/mods/deathmatch/logic/CClientDFFManager.cpp index ab93a65ff2..f466dd8407 100644 --- a/Client/mods/deathmatch/logic/CClientDFFManager.cpp +++ b/Client/mods/deathmatch/logic/CClientDFFManager.cpp @@ -79,7 +79,8 @@ CClientDFF* CClientDFFManager::GetElementThatReplaced(unsigned short usModel, CC bool CClientDFFManager::IsReplacableModel(unsigned short usModel) { // Either a vehicle model or an object model - return CClientObjectManager::IsValidModel(usModel) || CClientVehicleManager::IsValidModel(usModel) || CClientPlayerManager::IsValidModel(usModel); + return CClientObjectManager::IsValidModel(usModel) || CClientVehicleManager::IsValidModel(usModel) || CClientPlayerManager::IsValidModel(usModel) || + CClientPlayerClothes::IsValidModel(usModel); } bool CClientDFFManager::RestoreModel(unsigned short usModel) diff --git a/Client/mods/deathmatch/logic/CClientPlayerClothes.cpp b/Client/mods/deathmatch/logic/CClientPlayerClothes.cpp index 50af2ea544..7a3ed663a4 100644 --- a/Client/mods/deathmatch/logic/CClientPlayerClothes.cpp +++ b/Client/mods/deathmatch/logic/CClientPlayerClothes.cpp @@ -577,3 +577,8 @@ const int CClientPlayerClothes::GetClothingGroupMax(unsigned char ucType) return 0; } + +bool CClientPlayerClothes::IsValidModel(unsigned short usModel) +{ + return usModel >= CLOTHES_MODEL_ID_FIRST && usModel <= CLOTHES_MODEL_ID_LAST; +} diff --git a/Client/mods/deathmatch/logic/CClientPlayerClothes.h b/Client/mods/deathmatch/logic/CClientPlayerClothes.h index 420856377d..eeb3e25f8f 100644 --- a/Client/mods/deathmatch/logic/CClientPlayerClothes.h +++ b/Client/mods/deathmatch/logic/CClientPlayerClothes.h @@ -67,7 +67,7 @@ class CClientPlayerClothes static const SPlayerClothing* GetClothingGroup(unsigned char ucType); static const int GetClothingGroupMax(unsigned char ucType); - + static bool IsValidModel(unsigned short usModel); private: static const SPlayerClothing* GetClothing(const char* szTexture, const char* szModel, unsigned char ucType); diff --git a/Client/mods/deathmatch/logic/CClientTXD.cpp b/Client/mods/deathmatch/logic/CClientTXD.cpp index 761784e056..8a37f87157 100644 --- a/Client/mods/deathmatch/logic/CClientTXD.cpp +++ b/Client/mods/deathmatch/logic/CClientTXD.cpp @@ -29,7 +29,7 @@ CClientTXD::~CClientTXD() } // Remove us from all the clothes replacement doo dah - g_pGame->GetRenderWare()->ClothesRemoveReplacementTxd(m_FileData.data()); + g_pGame->GetRenderWare()->ClothesRemoveReplacement(m_FileData.data()); } bool CClientTXD::Load(bool isRaw, SString input, bool enableFiltering) @@ -75,8 +75,8 @@ bool CClientTXD::Import(unsigned short usModelID) return false; } m_bUsingFileDataForClothes = true; - // Note: ClothesAddReplacementTxd uses the pointer from m_FileData, so don't touch m_FileData until matching ClothesRemove call - g_pGame->GetRenderWare()->ClothesAddReplacementTxd(m_FileData.data(), usModelID - CLOTHES_MODEL_ID_FIRST); + // Note: ClothesAddReplacement uses the pointer from m_FileData, so don't touch m_FileData until matching ClothesRemove call + g_pGame->GetRenderWare()->ClothesAddReplacement(m_FileData.data(), m_FileData.size(), usModelID - CLOTHES_MODEL_ID_FIRST); return true; } else diff --git a/Client/sdk/game/CRenderWare.h b/Client/sdk/game/CRenderWare.h index bef9d8f79e..92686978b1 100644 --- a/Client/sdk/game/CRenderWare.h +++ b/Client/sdk/game/CRenderWare.h @@ -76,8 +76,8 @@ class CRenderWare bool bFilteringEnabled) = 0; virtual bool ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTextures, ushort usModelId) = 0; virtual void ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacementTextures) = 0; - virtual void ClothesAddReplacementTxd(char* pFileData, ushort usFileId) = 0; - virtual void ClothesRemoveReplacementTxd(char* pFileData) = 0; + virtual void ClothesAddReplacement(char* pFileData, size_t fileSize, ushort usFileId) = 0; + virtual void ClothesRemoveReplacement(char* pFileData) = 0; virtual bool HasClothesReplacementChanged() = 0; virtual RwTexDictionary* ReadTXD(const SString& strFilename, const SString& buffer) = 0; virtual RpClump* ReadDFF(const SString& strFilename, const SString& buffer, unsigned short usModelID, bool bLoadEmbeddedCollisions) = 0;