Skip to content

Commit 075dfee

Browse files
TheNormalnijPirulaxVladislav Nikolaevichpatrikjuvonen
authored
Allow load custom IMG containers (#1677)
Co-authored-by: Pirulax <patrikjankovics7@gmail.com> Co-authored-by: Vladislav Nikolaevich <v.nikolaevich@redbrixwall.com> Co-authored-by: patrikjuvonen <22572159+patrikjuvonen@users.noreply.github.com>
1 parent 423dac9 commit 075dfee

27 files changed

+943
-44
lines changed

Client/game_sa/CModelInfoSA.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ bool CModelInfoSA::IsValid()
502502

503503
bool CModelInfoSA::IsAllocatedInArchive()
504504
{
505-
return pGame->GetStreaming()->GetStreamingInfoFromModelId(m_dwModelID)->sizeInBlocks > 0;
505+
return pGame->GetStreaming()->GetStreamingInfo(m_dwModelID)->sizeInBlocks > 0;
506506
}
507507

508508
float CModelInfoSA::GetDistanceFromCentreOfMassToBaseOfModel()
@@ -753,7 +753,7 @@ void CModelInfoSA::StaticFlushPendingRestreamIPL()
753753
for (it = removedModels.begin(); it != removedModels.end(); it++)
754754
{
755755
pGame->GetStreaming()->RemoveModel(*it);
756-
pGame->GetStreaming()->GetStreamingInfoFromModelId(*it)->loadState = 0;
756+
pGame->GetStreaming()->GetStreamingInfo(*it)->loadState = 0;
757757
}
758758
}
759759

@@ -1435,10 +1435,10 @@ void CModelInfoSA::SetVoice(const char* szVoiceType, const char* szVoice)
14351435

14361436
void CModelInfoSA::CopyStreamingInfoFromModel(ushort usBaseModelID)
14371437
{
1438-
CStreamingInfo* pBaseModelStreamingInfo = pGame->GetStreaming()->GetStreamingInfoFromModelId(usBaseModelID);
1439-
CStreamingInfo* pTargetModelStreamingInfo = pGame->GetStreaming()->GetStreamingInfoFromModelId(m_dwModelID);
1438+
CStreamingInfo* pBaseModelStreamingInfo = pGame->GetStreaming()->GetStreamingInfo(usBaseModelID);
1439+
CStreamingInfo* pTargetModelStreamingInfo = pGame->GetStreaming()->GetStreamingInfo(m_dwModelID);
14401440

1441-
pTargetModelStreamingInfo->Reset();
1441+
*pTargetModelStreamingInfo = CStreamingInfo{};
14421442
pTargetModelStreamingInfo->archiveId = pBaseModelStreamingInfo->archiveId;
14431443
pTargetModelStreamingInfo->offsetInBlocks = pBaseModelStreamingInfo->offsetInBlocks;
14441444
pTargetModelStreamingInfo->sizeInBlocks = pBaseModelStreamingInfo->sizeInBlocks;
@@ -1529,7 +1529,7 @@ void CModelInfoSA::DeallocateModel(void)
15291529
}
15301530

15311531
ppModelInfo[m_dwModelID] = nullptr;
1532-
pGame->GetStreaming()->GetStreamingInfoFromModelId(m_dwModelID)->Reset();
1532+
*pGame->GetStreaming()->GetStreamingInfo(m_dwModelID) = CStreamingInfo{};
15331533
}
15341534
//////////////////////////////////////////////////////////////////////////////////////////
15351535
//

Client/game_sa/CStreamingSA.cpp

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@
1313
#include <core/CCoreInterface.h>
1414
#include "CStreamingSA.h"
1515
#include "CModelInfoSA.h"
16+
#include "Fileapi.h"
17+
#include "processthreadsapi.h"
1618

1719
extern CCoreInterface* g_pCore;
1820

1921
// count: 26316 in unmodified game
20-
CStreamingInfo* CStreamingSA::ms_aInfoForModel = (CStreamingInfo*)CStreaming__ms_aInfoForModel;
22+
CStreamingInfo (&CStreamingSA::ms_aInfoForModel)[26316] = *(CStreamingInfo(*)[26316])0x8E4CC0;
23+
HANDLE (&CStreamingSA::m_aStreamingHandlers)[32] = *(HANDLE(*)[32])0x8E4010; // Contains open files
24+
CArchiveInfo (&CStreamingSA::ms_aAchiveInfo)[8] = *(CArchiveInfo(*)[8])0x8E48D8; // [8][0x30]
25+
HANDLE* phStreamingThread = (HANDLE*)0x8E4008;
26+
uint32 (&CStreamingSA::ms_streamingHalfOfBufferSize) = *(uint32*)0x8E4CA8;
27+
void* (&CStreamingSA::ms_pStreamingBuffer)[2] = *(void*(*)[2])0x8E4CAC;
2128

2229
namespace
2330
{
@@ -149,17 +156,152 @@ void CStreamingSA::RequestSpecialModel(DWORD model, const char* szTexture, DWORD
149156
}
150157
}
151158

152-
CStreamingInfo* CStreamingSA::GetStreamingInfoFromModelId(uint32 id)
159+
void CStreamingSA::ReinitStreaming()
153160
{
154-
return &ms_aInfoForModel[id];
161+
typedef int(__cdecl * Function_ReInitStreaming)();
162+
((Function_ReInitStreaming)(0x40E560))();
155163
}
156164

157-
void CStreamingSA::ReinitStreaming()
165+
// ReinitStreaming should be called after this.
166+
// Otherwise the model wont be restreamed
167+
// TODO: Somehow restream a single model instead of the whole world
168+
void CStreamingSA::SetStreamingInfo(uint modelid, unsigned char usStreamID, uint uiOffset, ushort usSize, uint uiNextInImg)
158169
{
159-
typedef int(__cdecl * Function_ReInitStreaming)();
160-
Function_ReInitStreaming reinitStreaming = (Function_ReInitStreaming)(0x40E560);
170+
CStreamingInfo* pItemInfo = GetStreamingInfo(modelid);
171+
172+
// Change nextInImg field for prev model
173+
for (CStreamingInfo& info : ms_aInfoForModel)
174+
{
175+
if (info.archiveId == pItemInfo->archiveId)
176+
{
177+
// Check if the block after `info` is the beginning of `pItemInfo`'s block
178+
if (info.offsetInBlocks + info.sizeInBlocks == pItemInfo->offsetInBlocks)
179+
{
180+
info.nextInImg = -1;
181+
break;
182+
}
183+
}
184+
}
185+
186+
pItemInfo->archiveId = usStreamID;
187+
pItemInfo->offsetInBlocks = uiOffset;
188+
pItemInfo->sizeInBlocks = usSize;
189+
pItemInfo->nextInImg = uiNextInImg;
190+
}
191+
192+
CStreamingInfo* CStreamingSA::GetStreamingInfo(uint modelid)
193+
{
194+
return &ms_aInfoForModel[modelid];
195+
}
196+
197+
unsigned char CStreamingSA::GetUnusedArchive()
198+
{
199+
// Get internal IMG id
200+
// By default gta sa uses 6 of 8 IMG archives
201+
for (size_t i = 6; i < 8; i++)
202+
{
203+
if (!GetArchiveInfo(i)->uiStreamHandleId)
204+
return (unsigned char)i;
205+
}
206+
return -1;
207+
}
208+
209+
unsigned char CStreamingSA::GetUnusedStreamHandle()
210+
{
211+
for (size_t i = 0; i < VAR_StreamHandlersMaxCount; i++)
212+
{
213+
if (m_aStreamingHandlers[i])
214+
return (unsigned char)i;
215+
}
216+
return -1;
217+
}
218+
219+
unsigned char CStreamingSA::AddArchive(const char* szFilePath)
220+
{
221+
const auto ucArchiveId = GetUnusedArchive();
222+
if (ucArchiveId == -1)
223+
return -1;
224+
225+
// Get free stream handler id
226+
const auto ucStreamID = GetUnusedStreamHandle();
227+
if (ucStreamID == -1)
228+
return -1;
229+
230+
// Create new stream handler
231+
const auto streamCreateFlags = *(DWORD*)0x8E3FE0;
232+
HANDLE hFile = CreateFileA(
233+
szFilePath,
234+
GENERIC_READ,
235+
FILE_SHARE_READ,
236+
NULL,
237+
OPEN_EXISTING,
238+
streamCreateFlags | FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS,
239+
NULL
240+
);
241+
242+
if (hFile == INVALID_HANDLE_VALUE)
243+
return -1;
244+
245+
// Register stream handler
246+
m_aStreamingHandlers[ucStreamID] = hFile;
247+
248+
// Register archive data
249+
ms_aAchiveInfo[ucArchiveId].uiStreamHandleId = (ucStreamID << 24);
250+
251+
return ucArchiveId;
252+
}
253+
254+
void CStreamingSA::RemoveArchive(unsigned char ucArhiveID)
255+
{
256+
unsigned int uiStreamHandlerID = ms_aAchiveInfo[ucArhiveID].uiStreamHandleId >> 24;
257+
if (!uiStreamHandlerID)
258+
return;
259+
260+
ms_aAchiveInfo[ucArhiveID].uiStreamHandleId = 0;
261+
262+
CloseHandle(m_aStreamingHandlers[uiStreamHandlerID]);
263+
m_aStreamingHandlers[uiStreamHandlerID] = NULL;
264+
}
265+
266+
void CStreamingSA::SetStreamingBufferSize(uint32 uiBlockSize)
267+
{
268+
if (uiBlockSize == ms_streamingHalfOfBufferSize * 2)
269+
return;
270+
271+
int pointer = *(int*)0x8E3FFC;
272+
SGtaStream(&streaming)[5] = *(SGtaStream(*)[5])(pointer);
273+
274+
// Wait while streaming threads ends tasks
275+
while (streaming[0].bInUse && streaming[1].bInUse)
276+
277+
// Suspend streaming handle
278+
SuspendThread(*phStreamingThread);
279+
280+
// Create new buffer
281+
if (uiBlockSize & 1)
282+
uiBlockSize++;
283+
284+
typedef void*(__cdecl * Function_CMemoryMgr_MallocAlign)(uint32 uiCount, uint32 uiAlign);
285+
void* pNewBuffer = ((Function_CMemoryMgr_MallocAlign)(0x72F4C0))(uiBlockSize << 11, 2048);
286+
287+
// Copy data from old buffer to new buffer
288+
uint uiCopySize = std::min(ms_streamingHalfOfBufferSize, uiBlockSize / 2);
289+
MemCpyFast(pNewBuffer, (void*)ms_pStreamingBuffer[0], uiCopySize);
290+
MemCpyFast((void*)(reinterpret_cast<int>(pNewBuffer) + 1024 * uiBlockSize), (void*)ms_pStreamingBuffer[1], uiCopySize);
291+
292+
typedef void(__cdecl * Function_CMemoryMgr_FreeAlign)(void* pos);
293+
((Function_CMemoryMgr_FreeAlign)(0x72F4F0))(ms_pStreamingBuffer[0]);
294+
295+
ms_streamingHalfOfBufferSize = uiBlockSize / 2;
296+
297+
ms_pStreamingBuffer[0] = pNewBuffer;
298+
ms_pStreamingBuffer[1] = (void*)(reinterpret_cast<int>(pNewBuffer) + 1024 * uiBlockSize);
299+
300+
streaming[0].pBuffer = ms_pStreamingBuffer[0];
301+
streaming[1].pBuffer = ms_pStreamingBuffer[1];
161302

162-
reinitStreaming();
303+
// Well done
304+
ResumeThread(*phStreamingThread);
163305
}
164306

165307
void CStreamingSA::MakeSpaceFor(std::uint32_t memoryToCleanInBytes)

Client/game_sa/CStreamingSA.h

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,67 @@
1313

1414
#include <game/CStreaming.h>
1515

16+
#define VAR_StreamHandlersMaxCount 32
17+
#define VAR_MaxArchives 8
18+
1619
#define FUNC_CStreaming__RequestModel 0x4087E0
1720
#define FUNC_LoadAllRequestedModels 0x40EA10
1821
#define FUNC_CStreaming__HasVehicleUpgradeLoaded 0x407820
1922
#define FUNC_CStreaming_RequestSpecialModel 0x409d10
2023

24+
25+
struct CArchiveInfo
26+
{
27+
char szName[40];
28+
BYTE bUnknow = 1; // Only in player.img is 0. Maybe, it is DWORD value
29+
BYTE bUnused[3];
30+
DWORD uiStreamHandleId;
31+
};
32+
33+
struct SGtaStream
34+
{
35+
uint32_t nSectorsOffset;
36+
uint32_t nSectorsToRead;
37+
void* pBuffer;
38+
uint8_t bUnknow1;
39+
uint8_t bLocked;
40+
uint8_t bInUse;
41+
uint8_t bUnknow2;
42+
uint32_t uiStatus;
43+
uint32_t handle;
44+
uint32_t file;
45+
uint8_t pad[20];
46+
};
47+
static_assert(sizeof(SGtaStream) == 0x30, "Invalid size for SGtaStream");
48+
2149
class CStreamingSA final : public CStreaming
2250
{
51+
private:
52+
static CArchiveInfo* GetArchiveInfo(uint id) { return &ms_aAchiveInfo[id]; };
2353
public:
2454
void RequestModel(DWORD dwModelID, DWORD dwFlags);
2555
void RemoveModel(std::uint32_t model) override;
2656
void LoadAllRequestedModels(bool bOnlyPriorityModels = false, const char* szTag = NULL);
2757
bool HasModelLoaded(DWORD dwModelID);
2858
void RequestSpecialModel(DWORD model, const char* szTexture, DWORD channel);
2959
void ReinitStreaming();
30-
CStreamingInfo* GetStreamingInfoFromModelId(uint32 id);
60+
61+
CStreamingInfo* GetStreamingInfo(uint32 id);
62+
void SetStreamingInfo(uint32 modelid, unsigned char usStreamID, uint uiOffset, ushort usSize, uint uiNextInImg = -1);
63+
unsigned char GetUnusedArchive();
64+
unsigned char GetUnusedStreamHandle();
65+
unsigned char AddArchive(const char* szFilePath);
66+
void RemoveArchive(unsigned char ucStreamHandler);
67+
void SetStreamingBufferSize(uint32 uiSize);
68+
uint32 GetStreamingBufferSize() { return ms_streamingHalfOfBufferSize * 2; };
69+
3170
void MakeSpaceFor(std::uint32_t memoryToCleanInBytes) override;
3271
std::uint32_t GetMemoryUsed() const override;
3372

3473
private:
35-
static CStreamingInfo* ms_aInfoForModel; // count: 26316 in unmodified game
74+
static void* (&ms_pStreamingBuffer)[2];
75+
static uint32 (&ms_streamingHalfOfBufferSize);
76+
static CStreamingInfo (&ms_aInfoForModel)[26316]; // count: 26316 in unmodified game
77+
static HANDLE (&m_aStreamingHandlers)[32];
78+
static CArchiveInfo (&ms_aAchiveInfo)[8];
3679
};

Client/mods/deathmatch/StdInc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include <CClientStreamSectorRow.h>
8080
#include <CClientTask.h>
8181
#include <CClientTXD.h>
82+
#include <CClientIMG.h>
8283
#include <CClientIFP.h>
8384
#include <CClientWater.h>
8485
#include <CClientWeapon.h>

Client/mods/deathmatch/logic/CClientColManager.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void CClientColManager::DoHitDetectionForColShape(CClientColShape* pShape)
6969
case CCLIENTDFF:
7070
case CCLIENTCOL:
7171
case CCLIENTTXD:
72+
case CCLIENTIMG:
7273
case CCLIENTSOUND:
7374
break;
7475
default:

Client/mods/deathmatch/logic/CClientEntity.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ enum eClientEntityType
7878
CCLIENTIFP,
7979
CCLIENTVECTORGRAPHIC,
8080
CCLIENTUNKNOWN,
81+
CCLIENTIMG,
8182
};
8283

8384
class CEntity;
@@ -140,7 +141,8 @@ enum eCClientEntityClassTypes
140141
CLASS_CClientWeapon,
141142
CLASS_CClientEffect,
142143
CLASS_CClientPointLights,
143-
CLASS_CClientSearchLight
144+
CLASS_CClientSearchLight,
145+
CLASS_CClientIMG,
144146
};
145147

146148
class CClientEntity : public CClientEntityBase

Client/mods/deathmatch/logic/CClientGame.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,9 @@ void CClientGame::DoPulses()
12041204

12051205
// Initialize the game
12061206
g_pCore->GetGame()->Initialize();
1207+
1208+
// Save default streamer buffer size in IMG manager
1209+
m_pManager->GetIMGManager()->InitDefaultBufferSize();
12071210
}
12081211

12091212
unsigned char ucError = g_pNet->GetConnectionError();

0 commit comments

Comments
 (0)