diff --git a/include/modloader/gta3/gta3.hpp b/include/modloader/gta3/gta3.hpp index 77c4eea7..3f4c5bfa 100644 --- a/include/modloader/gta3/gta3.hpp +++ b/include/modloader/gta3/gta3.hpp @@ -79,8 +79,8 @@ namespace modloader template using WinCreateFileA = modloader::basic_file_detour, - HANDLE, LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE>; + injector::function_hooker_stdcall, + HANDLE, LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE>; template diff --git a/include/modloader/modloader.h b/include/modloader/modloader.h index 03353480..e1815dc7 100644 --- a/include/modloader/modloader.h +++ b/include/modloader/modloader.h @@ -29,7 +29,7 @@ extern "C" { /* Version */ #define MODLOADER_VERSION_MAJOR 0 #define MODLOADER_VERSION_MINOR 3 -#define MODLOADER_VERSION_REVISION 5 +#define MODLOADER_VERSION_REVISION 7 #ifdef NDEBUG #define MODLOADER_VERSION_ISDEV 0 #else diff --git a/include/modloader/util/path.hpp b/include/modloader/util/path.hpp index 2c322a0d..3ea30559 100644 --- a/include/modloader/util/path.hpp +++ b/include/modloader/util/path.hpp @@ -311,9 +311,9 @@ namespace modloader * WinAPI-like function to check if a directory exists * @szPath: Directory to check */ - inline BOOL IsDirectoryA(LPCTSTR szPath) + inline BOOL IsDirectoryA(LPCSTR szPath) { - DWORD dwAttrib = GetFileAttributes(szPath); + DWORD dwAttrib = GetFileAttributesA(szPath); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } @@ -323,7 +323,7 @@ namespace modloader * WinAPI-like function to check if a file or directory exists * @szPath: Directory to check */ - inline BOOL IsPathA(LPCTSTR szPath) + inline BOOL IsPathA(LPCSTR szPath) { DWORD dwAttrib = GetFileAttributesA(szPath); return (dwAttrib != INVALID_FILE_ATTRIBUTES); @@ -352,7 +352,7 @@ namespace modloader * WinAPI-like function to make sure a directory exists, if not, create it * @szPath: Directory to check */ - inline BOOL MakeSureDirectoryExistA(LPCTSTR szPath) + inline BOOL MakeSureDirectoryExistA(LPCSTR szPath) { if(!IsDirectoryA(szPath)) { @@ -371,7 +371,7 @@ namespace modloader * WinAPI-like function that copies the full directory @szFrom to @szTo * If @szTo doesn't exist, it is created */ - inline BOOL CopyDirectoryA(LPCTSTR szFrom, LPCTSTR szTo) + inline BOOL CopyDirectoryA(LPCSTR szFrom, LPCSTR szTo) { if(CreateDirectoryA(szTo, NULL)) { @@ -403,7 +403,7 @@ namespace modloader * DestroyDirectoryA * WinAPI-like function that deletes the path @szPath fully */ - inline BOOL DestroyDirectoryA(LPCTSTR szPath) + inline BOOL DestroyDirectoryA(LPCSTR szPath) { FilesWalk(szPath, "*.*", false, [&szPath](FileWalkInfo& file) { @@ -429,7 +429,7 @@ namespace modloader * GetFileSize * WinAPI-like function that gets the file size of @szPath */ - inline LONGLONG GetFileSize(LPCTSTR szPath) + inline LONGLONG GetFileSize(LPCSTR szPath) { WIN32_FILE_ATTRIBUTE_DATA fad; return GetFileAttributesExA(szPath, GetFileExInfoStandard, &fad)? @@ -471,7 +471,8 @@ namespace modloader /* Enter on ctor, Leave on dtor */ scoped_lock(CRITICAL_SECTION& cs) - { c = &cs; EnterCriticalSection(&cs); } + : c(&cs) + { EnterCriticalSection(&cs); } ~scoped_lock() { LeaveCriticalSection(c); } }; diff --git a/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp b/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp index e5862efa..893cc4c5 100644 --- a/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp +++ b/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp @@ -247,7 +247,7 @@ namespace hacks * Hacked FindFirstFileA */ template<> - bool FindCleoScripts::Finder(HANDLE& result, LPCTSTR& lpFileName, LPWIN32_FIND_DATAA& lpFindFileData) + bool FindCleoScripts::Finder(HANDLE& result, LPCSTR& lpFileName, LPWIN32_FIND_DATAA& lpFindFileData) { char version; diff --git a/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp b/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp index 352539a6..cd9bd4fa 100644 --- a/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp +++ b/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp @@ -77,6 +77,7 @@ struct path_translator_base bool bFindFirstFile; // ^ bool bFindNextFile; // ^ bool bFindClose; // ^ + bool bLoadLibrary; // ^ bool bDoBassHack; // ^ // Almost static vars (he), the value is always the same in all objects @@ -463,6 +464,7 @@ struct path_translator_basic : public path_translator_base bFindFirstFile = (Symbol == aFindFirstFileA) || (Symbol == aFindFirstFileW); bFindNextFile = (Symbol == aFindNextFileA) || (Symbol == aFindNextFileW); bFindClose = (Symbol == aFindClose); + bLoadLibrary = (Symbol == aLoadLibraryA) || (Symbol == aLoadLibraryW) || (Symbol == aLoadLibraryExA) || (Symbol == aLoadLibraryExW); bIniOperations = false; } } diff --git a/src/plugins/gta3/std.asi/args_translator/translator_stdcall.hpp b/src/plugins/gta3/std.asi/args_translator/translator_stdcall.hpp index 7ef9cab9..5e897635 100644 --- a/src/plugins/gta3/std.asi/args_translator/translator_stdcall.hpp +++ b/src/plugins/gta3/std.asi/args_translator/translator_stdcall.hpp @@ -72,7 +72,21 @@ struct path_translator_stdcall : public path_tran } if(!bDetoured) - info.TranslateForCall(a...); // Translate the paths + { + if(info.base->bLoadLibrary) + { + auto f = (func_type) info.base->fun; + result = f(a...); + if(result != NULL) + { + // If DLL loaded without translating the path, abort translation and don't try to call it again + bDetoured = true; + } + + } + if(!bDetoured) + info.TranslateForCall(a...); // Translate the paths + } } if(!bDetoured) diff --git a/src/plugins/gta3/std.stream/backend.cpp b/src/plugins/gta3/std.stream/backend.cpp index 86720da3..17beff0e 100644 --- a/src/plugins/gta3/std.stream/backend.cpp +++ b/src/plugins/gta3/std.stream/backend.cpp @@ -78,6 +78,53 @@ extern "C" }; +void __stdcall CdStreamShutdownSync_Stub( CdStream* stream, size_t idx ) +{ + streaming->cdStreamSyncFuncs.Shutdown( &stream[idx] ); +} + +uint32_t CdStreamSync( int32_t streamID ) +{ + static bool bInitFields = false; + static CdStream** ppStreams; + static BOOL* streamingInitialized; + static BOOL* overlappedIO; + + if ( !bInitFields ) + { + if( gvm.IsSA() ) + { + auto& cdinfo = *memory_pointer(0x8E3FE0).get(); + ppStreams = &cdinfo.pStreams; + streamingInitialized = &cdinfo.streamingInitialized; + overlappedIO = &cdinfo.overlappedIO; + } + else if( gvm.IsVC() || gvm.IsIII() ) + { + ppStreams = memory_pointer(xVc(0x6F76FC)).get(); + streamingInitialized = memory_pointer(xVc(0x6F7718)).get(); + overlappedIO = memory_pointer(xVc(0x6F7714)).get(); + } + + bInitFields = true; + } + + CdStream* stream = &((*ppStreams)[streamID]); + if ( *streamingInitialized ) + { + scoped_lock lock( streaming->cdStreamSyncLock ); + streaming->cdStreamSyncFuncs.SleepCS( stream, &streaming->cdStreamSyncLock ); + stream->bInUse = 0; + return stream->status; + } + + if ( *overlappedIO && stream->hFile != nullptr ) + { + DWORD numBytesRead; + return GetOverlappedResult( stream->hFile, &stream->overlapped, &numBytesRead, TRUE ) != 0 ? 0 : 254; + } + return 0; +} /* * Streaming thread @@ -92,7 +139,7 @@ int __stdcall CdStreamThread() // Get reference to the addresses we'll use. if(gvm.IsSA()) { - auto& cdinfo = *memory_pointer(0x8E3FEC).get(); + auto& cdinfo = *memory_pointer(0x8E3FE0).get(); pSemaphore = &cdinfo.semaphore; pQueue = &cdinfo.queue; ppStreams = &cdinfo.pStreams; @@ -192,9 +239,14 @@ int __stdcall CdStreamThread() // Cleanup if(bIsAbstract) streaming->CloseModel(sfile); - cd->nSectorsToRead = 0; - if(cd->bLocked) ReleaseSemaphore(cd->semaphore, 1, 0); - cd->bInUse = false; + { + // This critical section fixes a deadlock with CdStreamThread present in original code + scoped_lock xlock(streaming->cdStreamSyncLock); + + cd->nSectorsToRead = 0; + streaming->cdStreamSyncFuncs.Wake(cd); + cd->bInUse = false; + } } return 0; } @@ -442,6 +494,36 @@ void CAbstractStreaming::Patch() // Making our our code for the stream thread would make things so much better MakeJMP(0x406560, raw_ptr(CdStreamThread)); + // These are required so we can fix CdStream race condition + MakeJMP( 0x406460, raw_ptr(CdStreamSync) ); + if( gvm.IsSA() ) + { + const uint8_t mem[] = { 0xFF, 0x15 }; + WriteMemoryRaw( 0x406910, mem, sizeof(mem), true ); + WriteMemory( 0x406910 + 2, &streaming->cdStreamSyncFuncs.Initialize, true ); + MakeNOP( 0x406910 + 6, 4 ); + MakeNOP( 0x406910 + 0x16, 2 ); + } + else if( gvm.IsVC() || gvm.IsIII() ) + { + MakeNOP( xVc(0x4088F7), 8 ); + WriteMemory( xVc(0x4088F7) + 10, &streaming->cdStreamSyncFuncs.Initialize, true ); + WriteMemory( xVc(0x408919), uint8_t(0xEB), true ); + } + + if( gvm.IsSA() ) + { + const uint8_t mem[] = { 0x56, 0x50 }; + WriteMemoryRaw( 0x4063B5, mem, sizeof(mem), true ); + MakeCALL( 0x4063B5 + 2, raw_ptr(CdStreamShutdownSync_Stub), true ); + } + else if( gvm.IsVC() || gvm.IsIII() ) + { + const uint8_t mem[] = { 0x8D, 0x04, 0x29, 0x90 }; + WriteMemoryRaw( xVc(0x4086B6), mem, sizeof(mem), true ); + WriteMemory( xVc(0x4086B6) + 5 + 2, &streaming->cdStreamSyncFuncs.Shutdown, true ); + } + // We need to know the next model to be read before the CdStreamRead call happens if(gvm.IsSA()) { @@ -731,3 +813,12 @@ void CAbstractStreaming::Patch() } } +/* +* Export for SilentPatch so it knows that this ML version patches the race condition +*/ +extern "C" __declspec(dllexport) +uint32_t CdStreamRaceConditionAware() +{ + return 1; +} + diff --git a/src/plugins/gta3/std.stream/streaming.cpp b/src/plugins/gta3/std.stream/streaming.cpp index 2c1fb0f6..eca6602f 100644 --- a/src/plugins/gta3/std.stream/streaming.cpp +++ b/src/plugins/gta3/std.stream/streaming.cpp @@ -6,6 +6,7 @@ */ #include #include "streaming.hpp" +#include "cdstreamsync.inl" using namespace modloader; CAbstractStreaming* streaming; @@ -17,10 +18,13 @@ CAbstractStreaming* streaming; CAbstractStreaming::CAbstractStreaming() { InitializeCriticalSection(&cs); + InitializeCriticalSectionAndSpinCount(&cdStreamSyncLock, 10); + cdStreamSyncFuncs = CdStreamSyncFix::InitializeSyncFuncs(); } CAbstractStreaming::~CAbstractStreaming() { + DeleteCriticalSection(&cdStreamSyncLock); DeleteCriticalSection(&cs); Fastman92LimitAdjusterDestroy(this->f92la); } diff --git a/src/plugins/gta3/std.stream/streaming.hpp b/src/plugins/gta3/std.stream/streaming.hpp index 23608ddc..54486951 100644 --- a/src/plugins/gta3/std.stream/streaming.hpp +++ b/src/plugins/gta3/std.stream/streaming.hpp @@ -20,6 +20,8 @@ #include #include +#include "cdstreamsync.hpp" + using namespace modloader; // Streaming file type @@ -124,6 +126,8 @@ class CAbstractStreaming public: // Friends template friend class Refresher; friend int __stdcall CdStreamThread(); + friend void __stdcall CdStreamShutdownSync_Stub( CdStream* stream, size_t idx ); + friend uint32_t CdStreamSync( int32_t streamID ); private: LibF92LA f92la; // @@ -134,6 +138,9 @@ class CAbstractStreaming std::string fbuffer; // File buffer to avoid a dynamic allocation everytime we open a model std::list imgFiles; // List of img files imported with Mod Loader + CRITICAL_SECTION cdStreamSyncLock; // Used to bugfix a deadlock in CdStream + CdStreamSyncFix::SyncFuncs cdStreamSyncFuncs; // Initialize/Finalize/Sleep/Wake functions for CdStream synchronization + public: // Basic types using id_t = uint32_t; // should have sizeof(int) to allow -1 comparision diff --git a/src/shared/cdstreamsync.hpp b/src/shared/cdstreamsync.hpp new file mode 100644 index 00000000..ba9c75b0 --- /dev/null +++ b/src/shared/cdstreamsync.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include "CdStreamInfo.h" + +struct CdStream; + +namespace CdStreamSyncFix +{ + union SyncObj + { + HANDLE semaphore; + CONDITION_VARIABLE cv; + }; + + struct SyncFuncs + { + SyncObj (__stdcall* Initialize)(); + void (__stdcall* Shutdown)( CdStream* stream ); + void (__stdcall* SleepCS)( CdStream* stream, PCRITICAL_SECTION critSec ); + void (__stdcall* Wake)( CdStream* stream ); + }; + + namespace Sema + { + SyncObj __stdcall Initialize(); + void __stdcall Shutdown( CdStream* stream ); + void __stdcall SleepCS( CdStream* stream, PCRITICAL_SECTION critSec ); + void __stdcall Wake( CdStream* stream ); + } + + namespace CV + { + SyncObj __stdcall Initialize(); + void __stdcall Shutdown( CdStream* stream ); + void __stdcall SleepCS( CdStream* stream, PCRITICAL_SECTION critSec ); + void __stdcall Wake( CdStream* stream ); + } + + bool TryInitCV(); + + inline SyncFuncs InitializeSyncFuncs() + { + SyncFuncs funcs; + if ( TryInitCV() ) + { + using namespace CV; + funcs.Initialize = Initialize; + funcs.Shutdown = Shutdown; + funcs.SleepCS = SleepCS; + funcs.Wake = Wake; + } + else + { + using namespace Sema; + funcs.Initialize = Initialize; + funcs.Shutdown = Shutdown; + funcs.SleepCS = SleepCS; + funcs.Wake = Wake; + } + return funcs; + } +} + +static_assert(sizeof(CdStreamSyncFix::SyncObj) == sizeof(HANDLE), "Incorrect struct size: CdStreamSync::SyncObj"); \ No newline at end of file diff --git a/src/shared/cdstreamsync.inl b/src/shared/cdstreamsync.inl new file mode 100644 index 00000000..bfa17a37 --- /dev/null +++ b/src/shared/cdstreamsync.inl @@ -0,0 +1,83 @@ +#pragma once +#include "cdstreamsync.hpp" + +namespace CdStreamSyncFix +{ + namespace Sema + { + inline SyncObj __stdcall Initialize() + { + SyncObj object; + object.semaphore = CreateSemaphore( nullptr, 0, 2, nullptr ); + return object; + } + + inline void __stdcall Shutdown( CdStream* stream ) + { + CloseHandle( stream->sync.semaphore ); + } + + inline void __stdcall SleepCS( CdStream* stream, PCRITICAL_SECTION critSec ) + { + if ( stream->nSectorsToRead != 0 ) + { + stream->bLocked = 1; + LeaveCriticalSection( critSec ); + WaitForSingleObject( stream->sync.semaphore, INFINITE ); + EnterCriticalSection( critSec ); + } + } + + inline void __stdcall Wake( CdStream* stream ) + { + if( stream->bLocked ) ReleaseSemaphore( stream->sync.semaphore, 1, nullptr ); + } + } + + namespace CV + { + namespace Funcs + { + static decltype(InitializeConditionVariable)* pInitializeConditionVariable = nullptr; + static decltype(SleepConditionVariableCS)* pSleepConditionVariableCS = nullptr; + static decltype(WakeConditionVariable)* pWakeConditionVariable = nullptr; + } + + inline SyncObj __stdcall Initialize() + { + SyncObj object; + Funcs::pInitializeConditionVariable( &object.cv ); + return object; + } + + inline void __stdcall Shutdown( CdStream* stream ) + { + } + + inline void __stdcall SleepCS( CdStream* stream, PCRITICAL_SECTION critSec ) + { + while ( stream->nSectorsToRead != 0 ) + { + Funcs::pSleepConditionVariableCS( &stream->sync.cv, critSec, INFINITE ); + } + } + + inline void __stdcall Wake( CdStream* stream ) + { + Funcs::pWakeConditionVariable( &stream->sync.cv ); + } + } + + bool TryInitCV() + { + const HMODULE kernelDLL = GetModuleHandle( TEXT("kernel32") ); + assert( kernelDLL != nullptr ); + + using namespace CV::Funcs; + pInitializeConditionVariable = (decltype(pInitializeConditionVariable))GetProcAddress( kernelDLL, "InitializeConditionVariable" ); + pSleepConditionVariableCS = (decltype(pSleepConditionVariableCS))GetProcAddress( kernelDLL, "SleepConditionVariableCS" ); + pWakeConditionVariable = (decltype(pWakeConditionVariable))GetProcAddress( kernelDLL, "WakeConditionVariable" ); + + return pInitializeConditionVariable != nullptr && pSleepConditionVariableCS != nullptr && pWakeConditionVariable != nullptr; + } +} \ No newline at end of file diff --git a/src/shared/game/gta3/CdStreamInfo.h b/src/shared/game/gta3/CdStreamInfo.h index 032bb0cc..7d857aa4 100644 --- a/src/shared/game/gta3/CdStreamInfo.h +++ b/src/shared/game/gta3/CdStreamInfo.h @@ -1,8 +1,8 @@ #pragma once #include #include "Queue.h" +#include "cdstreamsync.hpp" -#pragma pack(push, 1) struct CdStream // sizeof = 0x30 { DWORD nSectorOffset; @@ -13,15 +13,16 @@ struct CdStream // sizeof = 0x30 BYTE bInUse; BYTE field_F; DWORD status; - HANDLE semaphore; + CdStreamSyncFix::SyncObj sync; HANDLE hFile; OVERLAPPED overlapped; }; -#pragma pack(pop) -#pragma pack(push, 1) -struct CdStreamInfoSA // sizeof = 0x8C0 +struct CdStreamInfoSA // sizeof = 0x8CC { + DWORD streamCreateFlags; + BOOL streamingInitialized; + BOOL overlappedIO; Queue queue; CdStream* pStreams; DWORD thread_id; @@ -38,8 +39,6 @@ struct CdStreamInfoSA // sizeof = 0x8C0 DWORD gtaint_id; DWORD gta3_id; }; -#pragma pack(pop) -static_assert(sizeof(CdStreamInfoSA) == 0x8C0, "Incorrect struct size: CdStreamInfoSA"); +static_assert(sizeof(CdStreamInfoSA) == 0x8CC, "Incorrect struct size: CdStreamInfoSA"); static_assert(sizeof(CdStream) == 0x30, "Incorrect struct size: CdStream"); - diff --git a/src/translator/gta3/3/10.hpp b/src/translator/gta3/3/10.hpp index f15212a1..ec034491 100644 --- a/src/translator/gta3/3/10.hpp +++ b/src/translator/gta3/3/10.hpp @@ -118,6 +118,8 @@ static void III_10(std::map& map) map[xVc(0x6F76F4)] = 0x62129C; // HANDLE hCdSemaphore map[xVc(0x6F7700)] = 0x6212A8; // Queue CdQueue; map[xVc(0x6F76FC)] = 0x6212A4; // CdStream* channelFile + map[xVc(0x6F7718)] = 0x6212C0; // BOOL streamingInitialized + map[xVc(0x6F7714)] = 0x6212BC; // BOOL overlappedIO map[0x8E4CAC] = 0x87F818; // void* CStreaming::ms_pStreamingBuffer[2] map[0x8E4CA8] = 0x942FB0; // unsigned int CStreaming::ms_streamingBufferSize @@ -154,6 +156,11 @@ static void III_10(std::map& map) map[xIII(0x4038FC)] = 0x4038FC; // call __loadIfp ; @CAnimManager::LoadAnimFiles "anim/ped.ifp" map[0x40E2C5] = 0x40A571; // call _ZN10CStreaming21ConvertBufferToObjectEPcii map[0x40E1BE] = 0x40A585; // call _ZN10CStreaming22FinishLoadingLargeFileEPci + map[xVc(0x4088F7)] = 0x405B67; // loc_4088F7 + map[xVc(0x4088F7) + 10] = 0x405B67 + 10; // call ds:CreateSemaphoreA + map[xVc(0x4088F7) + 0x22] = 0x405B67 + 0x22; // jnz short loc_408930 + map[xVc(0x4086B6)] = 0x405E16; // mov eax, [ecx+ebp+14h] + map[0x406460] = 0x406010; // _Z12CdStreamSynci map[0xB74490] = 0x8F2C60; // CPool<> *CPools::ms_pPedPool map[0xB74494] = 0x9430DC; // CPool<> *CPools::ms_pVehiclePool diff --git a/src/translator/gta3/sa/10us.hpp b/src/translator/gta3/sa/10us.hpp index cded94b2..d9ba30a3 100644 --- a/src/translator/gta3/sa/10us.hpp +++ b/src/translator/gta3/sa/10us.hpp @@ -105,8 +105,7 @@ static void sa_10us(std::map& map) map[0x5A419B] = 0x5A419B; // -> offset clothesDirectory map[0x5B8AFC] = 0x5B8AFC; // -> &ms_aInfoForModel[MAX_INFO_FOR_MODEL] - map[0x8E3FE0] = 0x8E3FE0; // DWORD StreamCreateFlags - map[0x8E3FEC] = 0x8E3FEC; // CdStreamInfo cdinfo + map[0x8E3FE0] = 0x8E3FE0; // DWORD StreamCreateFlags and CdStreamInfo cdinfo map[0x8E4CAC] = 0x8E4CAC; // void* CStreaming::ms_pStreamingBuffer[2] map[0x8E4CA8] = 0x8E4CA8; // unsigned int CStreaming::ms_streamingBufferSize @@ -141,6 +140,10 @@ static void sa_10us(std::map& map) map[0x4D565A] = 0x4D565A; // call _RwStreamOpen ; @CAnimManager::LoadAnimFiles "anim/ped.ifp" map[0x40E2C5] = 0x40E2C5; // call _ZN10CStreaming21ConvertBufferToObjectEPcii map[0x40E1BE] = 0x40E1BE; // call _ZN10CStreaming22FinishLoadingLargeFileEPci + map[0x406910] = 0x406910; // loc_406910 + map[0x406910 + 0x16] = 0x406910 + 0x16; // jz short loc_406995 + map[0x4063B5] = 0x4063B5; // loc_4063B0 + map[0x406460] = 0x406460; // _Z12CdStreamSynci map[0xB74490] = 0xB74490; // CPool<> *CPools::ms_pPedPool map[0xB74494] = 0xB74494; // CPool<> *CPools::ms_pVehiclePool diff --git a/src/translator/gta3/vc/10.hpp b/src/translator/gta3/vc/10.hpp index e1445651..bfeda86b 100644 --- a/src/translator/gta3/vc/10.hpp +++ b/src/translator/gta3/vc/10.hpp @@ -163,6 +163,8 @@ static void vc_10(std::map& map) map[xVc(0x6F76F4)] = 0x6F76F4; // HANDLE hCdSemaphore map[xVc(0x6F7700)] = 0x6F7700; // Queue CdQueue; map[xVc(0x6F76FC)] = 0x6F76FC; // CdStream* channelFile + map[xVc(0x6F7718)] = 0x6F7718; // BOOL streamingInitialized + map[xVc(0x6F7714)] = 0x6F7714; // BOOL overlappedIO map[0x8E4CAC] = 0x94B840; // void* CStreaming::ms_pStreamingBuffer[2] map[0x8E4CA8] = 0xA0FC90; // unsigned int CStreaming::ms_streamingBufferSize @@ -201,6 +203,11 @@ static void vc_10(std::map& map) map[0x4D565A] = 0x4055EA; // call _RwStreamOpen ; @CAnimManager::LoadAnimFiles "anim/ped.ifp" map[0x40E2C5] = 0x40C086; // call _ZN10CStreaming21ConvertBufferToObjectEPcii map[0x40E1BE] = 0x40BF0B; // call _ZN10CStreaming22FinishLoadingLargeFileEPci + map[xVc(0x4088F7)] = 0x4088F7; // loc_4088F7 + map[xVc(0x4088F7) + 10] = 0x4088F7 + 10; // call ds:CreateSemaphoreA + map[xVc(0x4088F7) + 0x22] = 0x4088F7 + 0x22; // jnz short loc_408930 + map[xVc(0x4086B6)] = 0x4086B6; // mov eax, [ecx+ebp+14h] + map[0x406460] = 0x4083D0; // _Z12CdStreamSynci map[0xB74490] = 0x97F2AC; // CPool<> *CPools::ms_pPedPool map[0xB74494] = 0xA0FDE4; // CPool<> *CPools::ms_pVehiclePool