From 66462d348bec183e6b7cf848a463052759221928 Mon Sep 17 00:00:00 2001 From: aap Date: Fri, 21 Jun 2019 17:28:55 +0200 Subject: [PATCH] more CStreaming; fixed CFileLoader --- src/FileLoader.cpp | 2 +- src/Streaming.cpp | 441 ++++++++++++++++++++++++++++++++++++++++++++- src/Streaming.h | 22 ++- src/patcher.h | 9 + 4 files changed, 465 insertions(+), 9 deletions(-) diff --git a/src/FileLoader.cpp b/src/FileLoader.cpp index 1295177d..f50638b4 100644 --- a/src/FileLoader.cpp +++ b/src/FileLoader.cpp @@ -1082,7 +1082,7 @@ CFileLoader::LoadCullZone(const char *line) &minx, &miny, &minz, &maxx, &maxy, &maxz, &flags, &wantedLevelDrop); - CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxy, flags, wantedLevelDrop); + CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxz, flags, wantedLevelDrop); } // unused diff --git a/src/Streaming.cpp b/src/Streaming.cpp index fa0710ea..1a5800e2 100644 --- a/src/Streaming.cpp +++ b/src/Streaming.cpp @@ -2,11 +2,14 @@ #include "patcher.h" #include "ModelInfo.h" #include "TxdStore.h" +#include "ModelIndices.h" #include "Pools.h" #include "Directory.h" #include "RwHelper.h" +#include "World.h" #include "Entity.h" #include "FileMgr.h" +#include "FileLoader.h" #include "CdStream.h" #include "Streaming.h" @@ -56,11 +59,10 @@ int32 &islandLODcomSub = *(int32*)0x6212D0; int32 &islandLODsubInd = *(int32*)0x6212D4; int32 &islandLODsubCom = *(int32*)0x6212D8; - -WRAPPER void CStreaming::RemoveModel(int32 id) { EAXJMP(0x408830); } -WRAPPER void CStreaming::RequestModel(int32 model, int32 flags) { EAXJMP(0x407EA0); } - WRAPPER void CStreaming::MakeSpaceFor(int32 size) { EAXJMP(0x409B70); } +WRAPPER bool CStreaming::IsTxdUsedByRequestedModels(int32 txdId) { EAXJMP(0x4094C0); } +WRAPPER bool CStreaming::AddToLoadedVehiclesList(int32 modelId) { EAXJMP(0x40B060); } + void CStreaming::Init(void) @@ -279,6 +281,428 @@ CStreaming::LoadCdDirectory(const char *dirname, int n) CFileMgr::CloseFile(fd); } +bool +CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + int cdsize; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + cdsize = ms_aInfoForModel[streamId].GetCdSize(); + mem.start = (uint8*)buf; + mem.length = cdsize * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + + // Txd has to be loaded + if(CTxdStore::GetSlot(mi->GetTxdSlot())->texDict == nil){ + debug("failed to load %s because TXD %s is not in memory\n", mi->GetName(), CTxdStore::GetTxdName(mi->GetTxdSlot())); + RemoveModel(streamId); + RemoveModel(mi->GetTxdSlot() + STREAM_OFFSET_TXD); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + + // Set Txd to use + CTxdStore::AddRef(mi->GetTxdSlot()); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + + if(mi->IsSimple()){ + success = CFileLoader::LoadAtomicFile(stream, streamId - STREAM_OFFSET_MODEL); + }else if(mi->m_type == MITYPE_VEHICLE){ + // load vehicles in two parts + CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->AddRef(); + success = CFileLoader::StartLoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else{ + success = CFileLoader::LoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + } + UpdateMemoryUsed(); + + // Txd no longer needed unless we only read part of the file + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED) + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); + + if(!success){ + debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName()); + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + }else{ + // Txd + assert(streamId < NUMSTREAMINFO); + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && + !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ + RemoveModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + + if(ms_bLoadingBigModel || cdsize > 200){ + success = CTxdStore::StartLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else + success = CTxdStore::LoadTxd(streamId - STREAM_OFFSET_TXD, stream); + UpdateMemoryUsed(); + + if(!success){ + debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + } + + RwStreamClose(stream, &mem); + + // We shouldn't even end up here unless load was successful + if(!success){ + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + if(streamId < STREAM_OFFSET_TXD) + debug("Failed to load %s.dff\n", mi->GetName()); + else + debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); + return false; + } + + if(streamId < STREAM_OFFSET_TXD){ + // Model + // Vehicles and Peds not in loaded list + if(mi->m_type != MITYPE_VEHICLE && mi->m_type != MITYPE_PED){ + CSimpleModelInfo *smi = (CSimpleModelInfo*)mi; + + // Set fading for some objects + if(mi->IsSimple() && !smi->m_isBigBuilding){ + if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOFADE) + smi->m_alpha = 255; + else + smi->m_alpha = 0; + } + + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + }else{ + // Txd + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + + // Mark objects as loaded + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + } + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5){ + if(streamId < STREAM_OFFSET_TXD) + debug("model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + else + debug("txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); + } + + return true; +} + + +bool +CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + if(streamId < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->RemoveRef(); + return false; + } + + mem.start = (uint8*)buf; + mem.length = ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + success = CFileLoader::FinishLoadClumpFile(stream, streamId); + if(success) + success = AddToLoadedVehiclesList(streamId); + mi->RemoveRef(); + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); + }else{ + // Txd + CTxdStore::AddRef(streamId - STREAM_OFFSET_TXD); + success = CTxdStore::FinishLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + CTxdStore::RemoveRefWithoutDelete(streamId - STREAM_OFFSET_TXD); + } + + RwStreamClose(stream, &mem); + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + + if(!success){ + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + UpdateMemoryUsed(); + return false; + } + + UpdateMemoryUsed(); + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5){ + if(streamId < STREAM_OFFSET_TXD) + debug("finish model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + else + debug("finish txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); + } + + return true; +} + +void +CStreaming::RequestModel(int32 id, int32 flags) +{ + CSimpleModelInfo *mi; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ + // updgrade to priority + if(flags & STREAMFLAGS_PRIORITY && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY) == 0){ + ms_numPriorityRequests++; + ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY; + } + }else if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_NOTLOADED){ + flags &= ~STREAMFLAGS_PRIORITY; + } + ms_aInfoForModel[id].m_flags |= flags; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + // Already loaded, only check changed flags + + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOFADE && id < STREAM_OFFSET_TXD){ + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + + // reinsert into list + if(ms_aInfoForModel[id].m_next){ + ms_aInfoForModel[id].RemoveFromList(); + if((ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED || + ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ // how can this be true again? + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED){ + if(id < STREAM_OFFSET_TXD) + RequestTxd(CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->GetTxdSlot(), flags); + ms_aInfoForModel[id].AddToList(&ms_startRequestedList); + ms_numModelsRequested++; + if(flags & STREAMFLAGS_PRIORITY) + ms_numPriorityRequests++; + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_INQUEUE; + ms_aInfoForModel[id].m_flags = flags; + } +} + +void +CStreaming::RequestSubway(void) +{ + RequestModel(MI_SUBWAY1, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY2, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY3, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY4, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY5, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY6, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY7, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY8, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY9, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY10, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY11, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY12, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY13, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY14, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY15, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY16, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY17, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY18, STREAMFLAGS_NOFADE); + + switch(CGame::currLevel){ + case LEVEL_INDUSTRIAL: + RequestModel(MI_SUBPLATFORM_IND, STREAMFLAGS_NOFADE); + break; + case LEVEL_COMMERCIAL: + if(FindPlayerTrain()->GetPosition().y < -700.0f){ + RequestModel(MI_SUBPLATFORM_COMS, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBPLATFORM_COMS2, STREAMFLAGS_NOFADE); + }else{ + RequestModel(MI_SUBPLATFORM_COMN, STREAMFLAGS_NOFADE); + } + break; + case LEVEL_SUBURBAN: + RequestModel(MI_SUBPLATFORM_SUB, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBPLATFORM_SUB2, STREAMFLAGS_NOFADE); + break; + } +} + +void +CStreaming::RequestBigBuildings(eLevelName level) +{ + int i, n; + CBuilding *b; + + n = CPools::GetBuildingPool()->GetSize(); + for(i = 0; i < n; i++){ + b = CPools::GetBuildingPool()->GetSlot(i); + if(b && b->bIsBIGBuilding && b->m_level == level) + RequestModel(b->GetModelIndex(), STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + } + RequestIslands(level); + ms_hasLoadedLODs = false; +} + +void +CStreaming::RequestIslands(eLevelName level) +{ + switch(level){ + case LEVEL_INDUSTRIAL: + RequestModel(islandLODcomInd, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODsubInd, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + case LEVEL_COMMERCIAL: + RequestModel(islandLODindust, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODsubCom, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + case LEVEL_SUBURBAN: + RequestModel(islandLODindust, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODcomSub, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + } +} + +void +CStreaming::RequestSpecialModel(int32 modelId, const char *modelName, int32 flags) +{ + CBaseModelInfo *mi; + int txdId; + char oldName[48]; + uint32 pos, size; + + mi = CModelInfo::GetModelInfo(modelId); + if(strcmp(mi->GetName(), modelName) == 0){ + // Already have the correct name, just request it + RequestModel(modelId, flags); + return; + } + + strcpy(oldName, mi->GetName()); + mi->SetName(modelName); + + // What exactly is going on here? + if(CModelInfo::GetModelInfo(oldName, nil)){ + txdId = CTxdStore::FindTxdSlot(oldName); + if(txdId != -1 && CTxdStore::GetSlot(txdId)->texDict){ + CTxdStore::AddRef(txdId); + RemoveModel(modelId); + CTxdStore::RemoveRefWithoutDelete(txdId); + }else + RemoveModel(modelId); + }else + RemoveModel(modelId); + + ms_pExtraObjectsDir->FindItem(modelName, pos, size); + mi->ClearTexDictionary(); + if(CTxdStore::FindTxdSlot(modelName) == -1) + mi->SetTexDictionary("generic"); + else + mi->SetTexDictionary(modelName); + ms_aInfoForModel[modelId].SetCdPosnAndSize(pos, size); + RequestModel(modelId, flags); +} + +void +CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags) +{ + RequestSpecialModel(charId + MI_SPECIAL01, modelName, flags); +} + +void +CStreaming::RemoveModel(int32 id) +{ + int i; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED) + return; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + if(id < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->DeleteRwObject(); + else + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + ms_memoryUsed -= ms_aInfoForModel[id].GetCdSize()*CDSTREAM_SECTOR_SIZE; + } + + if(ms_aInfoForModel[id].m_next){ + // Remove from list, model is neither loaded nor requested + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ + ms_numModelsRequested--; + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY){ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; + ms_numPriorityRequests--; + } + } + ms_aInfoForModel[id].RemoveFromList(); + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){ + for(i = 0; i < 4; i++){ + if(ms_channel[0].modelIds[i] == id - STREAM_OFFSET_MODEL) + ms_channel[0].modelIds[i] = -1; + if(ms_channel[1].modelIds[i] == id - STREAM_OFFSET_MODEL) + ms_channel[1].modelIds[i] = -1; + } + } + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ + if(id < STREAM_OFFSET_TXD) + RpClumpGtaCancelStream(); + else + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_NOTLOADED; +} + + void CStreaming::ImGonnaUseStreamingMemory(void) { @@ -338,6 +762,15 @@ STARTPATCHES InjectHook(0x406C80, CStreaming::Shutdown, PATCH_JUMP); InjectHook(0x406CC0, (void (*)(void))CStreaming::LoadCdDirectory, PATCH_JUMP); InjectHook(0x406DA0, (void (*)(const char*, int))CStreaming::LoadCdDirectory, PATCH_JUMP); + InjectHook(0x409740, CStreaming::ConvertBufferToObject, PATCH_JUMP); + InjectHook(0x409580, CStreaming::FinishLoadingLargeFile, PATCH_JUMP); + InjectHook(0x407EA0, CStreaming::RequestModel, PATCH_JUMP); + InjectHook(0x407FD0, CStreaming::RequestSubway, PATCH_JUMP); + InjectHook(0x408190, CStreaming::RequestBigBuildings, PATCH_JUMP); + InjectHook(0x408210, CStreaming::RequestIslands, PATCH_JUMP); + InjectHook(0x40A890, CStreaming::RequestSpecialModel, PATCH_JUMP); + InjectHook(0x40ADA0, CStreaming::RequestSpecialChar, PATCH_JUMP); + InjectHook(0x408830, CStreaming::RemoveModel, PATCH_JUMP); InjectHook(0x4063E0, &CStreamingInfo::GetCdPosnAndSize, PATCH_JUMP); InjectHook(0x406410, &CStreamingInfo::SetCdPosnAndSize, PATCH_JUMP); diff --git a/src/Streaming.h b/src/Streaming.h index f31faf9c..ef51f54a 100644 --- a/src/Streaming.h +++ b/src/Streaming.h @@ -13,6 +13,9 @@ enum StreamFlags STREAMFLAGS_DEPENDENCY = 0x04, STREAMFLAGS_PRIORITY = 0x08, STREAMFLAGS_NOFADE = 0x10, + + STREAMFLAGS_NOT_IN_LIST = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED, + STREAMFLAGS_KEEP_IN_MEMORY = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED|STREAMFLAGS_DEPENDENCY, }; enum StreamLoadState @@ -21,7 +24,7 @@ enum StreamLoadState STREAMSTATE_LOADED = 1, STREAMSTATE_INQUEUE = 2, STREAMSTATE_READING = 3, // what is this? - STREAMSTATE_BIGFILE = 4, + STREAMSTATE_STARTED = 4, // first part read }; enum ChannelState @@ -96,10 +99,21 @@ public: static void Init(void); static void Shutdown(void); static void LoadCdDirectory(void); - static void LoadCdDirectory(const char *dirname, int n); - - static void RemoveModel(int32 id); + static void LoadCdDirectory(const char *dirname, int32 n); + static bool ConvertBufferToObject(int8 *buf, int32 streamId); + static bool FinishLoadingLargeFile(int8 *buf, int32 streamId); static void RequestModel(int32 model, int32 flags); + static void RequestTxd(int32 txd, int32 flags) { RequestModel(txd + STREAM_OFFSET_TXD, flags); } + static void RequestSubway(void); + static void RequestBigBuildings(eLevelName level); + static void RequestIslands(eLevelName level); + static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags); + static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags); + static void RemoveModel(int32 id); + + static bool IsTxdUsedByRequestedModels(int32 txdId); + static bool AddToLoadedVehiclesList(int32 modelId); + static void MakeSpaceFor(int32 size); static void ImGonnaUseStreamingMemory(void); static void IHaveUsedStreamingMemory(void); diff --git a/src/patcher.h b/src/patcher.h index 4ac1111b..43618b47 100644 --- a/src/patcher.h +++ b/src/patcher.h @@ -101,6 +101,15 @@ Nop(AT address, unsigned int nCount) VirtualProtect((void*)address, nCount, dwProtect[0], &dwProtect[1]); } +template inline void +ClearCC(AT address, unsigned int nCount) +{ + DWORD dwProtect[2]; + VirtualProtect((void*)address, nCount, PAGE_EXECUTE_READWRITE, &dwProtect[0]); + memset((void*)address, 0xCC, nCount); + VirtualProtect((void*)address, nCount, dwProtect[0], &dwProtect[1]); +} + template inline void InjectHook(AT address, HT hook, unsigned int nType=PATCH_NOTHING) {