@@ -31,8 +31,9 @@ namespace
3131
3232 std::unordered_map<ushort, char *> ms_ReplacementClothesFileDataMap;
3333 std::unordered_map<ushort, std::uint16_t > ms_OriginalStreamingSizesMap;
34+ std::unordered_map<std::string, char *> ms_ClothesFileDataMap;
3435
35- bool bClothesReplacementChanged = false ;
36+ bool clothesReplacementChanged = false ;
3637
3738 struct SPlayerImgItem
3839 {
@@ -78,7 +79,7 @@ void CRenderWareSA::ClothesAddReplacement(char* pFileData, size_t fileSize, usho
7879 MapSet (ms_OriginalStreamingSizesMap, usFileId, g_clothesDirectory->GetModelStreamingSize (usFileId));
7980 g_clothesDirectory->SetModelStreamingSize (usFileId, GetSizeInBlocks (fileSize));
8081
81- bClothesReplacementChanged = true ;
82+ clothesReplacementChanged = true ;
8283 }
8384}
8485
@@ -107,7 +108,7 @@ void CRenderWareSA::ClothesRemoveReplacement(char* pFileData)
107108 }
108109
109110 iter = ms_ReplacementClothesFileDataMap.erase (iter);
110- bClothesReplacementChanged = true ;
111+ clothesReplacementChanged = true ;
111112 }
112113 else
113114 ++iter;
@@ -123,11 +124,81 @@ void CRenderWareSA::ClothesRemoveReplacement(char* pFileData)
123124// //////////////////////////////////////////////////////////////
124125bool CRenderWareSA::HasClothesReplacementChanged ()
125126{
126- bool bResult = bClothesReplacementChanged ;
127- bClothesReplacementChanged = false ;
127+ bool bResult = clothesReplacementChanged ;
128+ clothesReplacementChanged = false ;
128129 return bResult;
129130}
130131
132+ // //////////////////////////////////////////////////////////////
133+ //
134+ // CRenderWareSA::ClothesAddFile
135+ //
136+ // Add a file to the clothes directory
137+ //
138+ // //////////////////////////////////////////////////////////////
139+ bool CRenderWareSA::ClothesAddFile (const char * fileData, std::size_t fileSize, const char * fileName)
140+ {
141+ if (!fileData || !fileName)
142+ return false ;
143+
144+ if (MapFind (ms_ClothesFileDataMap, fileName))
145+ return false ;
146+
147+ DirectoryInfoSA entry{};
148+ entry.m_streamingSize = GetSizeInBlocks (fileSize);
149+
150+ std::size_t nameSize = sizeof (entry.m_name ) - 1 ;
151+ std::strncpy (entry.m_name , fileName, nameSize);
152+ entry.m_name [nameSize] = ' \0 ' ;
153+
154+ if (!g_clothesDirectory->AddEntry (entry))
155+ return false ;
156+
157+ MapSet (ms_ClothesFileDataMap, fileName, const_cast <char *>(fileData));
158+ clothesReplacementChanged = true ;
159+
160+ return true ;
161+ }
162+
163+ // //////////////////////////////////////////////////////////////
164+ //
165+ // CRenderWareSA::ClothesRemoveFile
166+ //
167+ // Remove a file from the clothes directory
168+ //
169+ // //////////////////////////////////////////////////////////////
170+ bool CRenderWareSA::ClothesRemoveFile (char * fileData)
171+ {
172+ if (!fileData)
173+ return false ;
174+
175+ for (auto iter = ms_ClothesFileDataMap.begin (); iter != ms_ClothesFileDataMap.end ();)
176+ {
177+ if (iter->second == fileData)
178+ {
179+ if (!g_clothesDirectory->RemoveEntry (iter->first .c_str ()))
180+ return false ;
181+
182+ iter = ms_ClothesFileDataMap.erase (iter);
183+ clothesReplacementChanged = true ;
184+ }
185+ else
186+ ++iter;
187+ }
188+ }
189+
190+ // //////////////////////////////////////////////////////////////
191+ //
192+ // CRenderWareSA::HasClothesFile
193+ //
194+ // Check if clothe file exits
195+ //
196+ // //////////////////////////////////////////////////////////////
197+ bool CRenderWareSA::HasClothesFile (const char * fileName) const noexcept
198+ {
199+ return fileName && MapFind (ms_ClothesFileDataMap, fileName);
200+ }
201+
131202// //////////////////////////////////////////////////////////////
132203//
133204// CStreaming_RequestModel_Mid
@@ -143,35 +214,44 @@ __declspec(noinline) bool _cdecl OnCStreaming_RequestModel_Mid(int flags, SImgGT
143214 return false ;
144215
145216 // Early out if no clothes textures to replace with
146- if (ms_ReplacementClothesFileDataMap.empty ())
217+ if (ms_ReplacementClothesFileDataMap.empty () && ms_ClothesFileDataMap. empty () )
147218 return false ;
148219
149220 // Initialze lookup map if needed
150- static std::map<uint, int > blockOffsetToFileIdMap;
221+ static std::map<std::uint32_t , int > blockOffsetToFileIdMap;
222+ static std::map<std::uint32_t , std::string> blockOffsetToFileNameMap;
151223 if (blockOffsetToFileIdMap.empty ())
152224 {
153225 // Check is player.img dir has been loaded by GTA
154226 SPlayerImgItemArray* pItemArray = (SPlayerImgItemArray*)0x00BC12C0 ;
155- if (!pItemArray->pItems || pItemArray->uiArraySize != 542 )
227+ std::uint32_t maxArraySize = 542 + ms_ClothesFileDataMap.size ();
228+
229+ if (!pItemArray->pItems || pItemArray->uiArraySize != maxArraySize)
156230 return false ;
157231
158- for (uint i = 0 ; i < pItemArray->uiArraySize ; i++)
232+ for (std:: uint32_t i = 0 ; i < pItemArray->uiArraySize ; i++)
159233 {
160234 SPlayerImgItem* pImgItem = &pItemArray->pItems [i];
161235 MapSet (blockOffsetToFileIdMap, pImgItem->uiBlockOffset , i);
236+ MapSet (blockOffsetToFileNameMap, pImgItem->uiBlockOffset , pImgItem->szName );
162237 }
163238 }
164239
165- // Get player.img fileId by comparing the supplied BlockOffset with entries in the player.img dir
166- int * piPlayerImgFileId = MapFind (blockOffsetToFileIdMap, pImgGTAInfo->iBlockOffset );
167- if (!piPlayerImgFileId)
168- return false ;
240+ char * replacementFileData = nullptr ;
241+ int * playerImgFileId = MapFind (blockOffsetToFileIdMap, pImgGTAInfo->iBlockOffset );
242+
243+ if (playerImgFileId)
244+ replacementFileData = MapFindRef (ms_ReplacementClothesFileDataMap, *playerImgFileId);
169245
170- int iPlayerImgFileId = *piPlayerImgFileId;
246+ if (!replacementFileData)
247+ {
248+ std::string* fileName = MapFind (blockOffsetToFileNameMap, pImgGTAInfo->iBlockOffset );
249+
250+ if (fileName)
251+ replacementFileData = MapFindRef (ms_ClothesFileDataMap, *fileName);
252+ }
171253
172- // Do we have a replacement for this clothes texture?
173- char * pReplacementFileData = MapFindRef (ms_ReplacementClothesFileDataMap, iPlayerImgFileId);
174- if (!pReplacementFileData)
254+ if (!replacementFileData)
175255 return false ;
176256
177257 // If bLoadingBigModel is set, try to get it unset
@@ -187,7 +267,7 @@ __declspec(noinline) bool _cdecl OnCStreaming_RequestModel_Mid(int flags, SImgGT
187267
188268 // Set results
189269 iReturnFileId = ((char *)pImgGTAInfo - (char *)CStreaming__ms_aInfoForModel) / 20 ;
190- pReturnBuffer = pReplacementFileData ;
270+ pReturnBuffer = replacementFileData ;
191271
192272 // Update flags
193273 pImgGTAInfo->uiLoadflag = 3 ;
0 commit comments