|
13 | 13 | #include <core/CCoreInterface.h>
|
14 | 14 | #include "CStreamingSA.h"
|
15 | 15 | #include "CModelInfoSA.h"
|
| 16 | +#include "Fileapi.h" |
| 17 | +#include "processthreadsapi.h" |
16 | 18 |
|
17 | 19 | extern CCoreInterface* g_pCore;
|
18 | 20 |
|
19 | 21 | // 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; |
21 | 28 |
|
22 | 29 | namespace
|
23 | 30 | {
|
@@ -149,17 +156,152 @@ void CStreamingSA::RequestSpecialModel(DWORD model, const char* szTexture, DWORD
|
149 | 156 | }
|
150 | 157 | }
|
151 | 158 |
|
152 |
| -CStreamingInfo* CStreamingSA::GetStreamingInfoFromModelId(uint32 id) |
| 159 | +void CStreamingSA::ReinitStreaming() |
153 | 160 | {
|
154 |
| - return &ms_aInfoForModel[id]; |
| 161 | + typedef int(__cdecl * Function_ReInitStreaming)(); |
| 162 | + ((Function_ReInitStreaming)(0x40E560))(); |
155 | 163 | }
|
156 | 164 |
|
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) |
158 | 169 | {
|
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]; |
161 | 302 |
|
162 |
| - reinitStreaming(); |
| 303 | + // Well done |
| 304 | + ResumeThread(*phStreamingThread); |
163 | 305 | }
|
164 | 306 |
|
165 | 307 | void CStreamingSA::MakeSpaceFor(std::uint32_t memoryToCleanInBytes)
|
|
0 commit comments