diff --git a/saco/filehooks.cpp b/saco/filehooks.cpp index 667c142..6dc0e1d 100644 --- a/saco/filehooks.cpp +++ b/saco/filehooks.cpp @@ -2,6 +2,11 @@ #include #include "detours.h" #include "filehooks.h" +#include "filesystem.h" +#include "filechecks.h" +#include "runutil.h" +#include "checksums.h" +#include "game/game.h" //---------------------------------------------------------- @@ -15,10 +20,84 @@ def_CloseHandle Real_CloseHandle = NULL; def_GetFileType Real_GetFileType = NULL; def_ShowCursor Real_ShowCursor = NULL; +extern CFileSystem *pFileSystem; +extern CGame *pGame; + ARCH_FILE_RECORD OpenArchRecords[MAX_OPEN_ARCH_FILES]; BOOL bArchRecordSlotState[MAX_OPEN_ARCH_FILES]; BOOL bFileHooksInstalled = FALSE; +int iCustomHandle=CUSTOM_HANDLE_BASE; + +UINT dwHandlingDigest[] = { 0x11C714FD, 0x0A46694E, 0x7AEC2920, 0x2100E2E4, 0x94F7372E }; // handling.cfg +UINT dwWeaponDigest[] = { 0x0B85A2AD, 0x344BDAE1, 0xA19471D5, 0x7D49D3E9, 0x7DDE6CBE }; // weapon.dat +UINT dwMeleeDigest[] = { 0x7C425B89, 0xF67763C0, 0xA2E49F0E, 0x93EBF8D2, 0x0E842901 }; // melee.dat + +char * FileNameOnly(char *sz); +char * ExtensionOnly(char *sz); +char* strtolower(char* sz); +void CheckFileNameHash(UINT *pDigest); + +//---------------------------------------------------------- + +BOOL IsCustomFileHandle(DWORD handle) +{ + if(handle >= CUSTOM_HANDLE_BASE && handle < CUSTOM_HANDLE_LIMIT) { + return TRUE; + } + return FALSE; +} + +//---------------------------------------------------------- + +int FindUnusedArchRecordIndex() +{ + int x=0; + while(x!=MAX_OPEN_ARCH_FILES) { + if(bArchRecordSlotState[x] == FALSE) { + return x; + } + x++; + } + return (-1); +} + +//---------------------------------------------------------- + +int FindArchRecordIndexFromHandle(HANDLE hFile) +{ + int x=0; + while(x!=MAX_OPEN_ARCH_FILES) { + if(bArchRecordSlotState[x] == TRUE) { + if(OpenArchRecords[x].hHandle == hFile) { + return x; + } + } + x++; + } + return (-1); +} + +//---------------------------------------------------------- + +HANDLE CreateArchRecord(DWORD dwFileIndex) +{ + int iArchRecordIndex = FindUnusedArchRecordIndex(); + + if(iArchRecordIndex == (-1)) return 0; + + bArchRecordSlotState[iArchRecordIndex] = TRUE; + + OpenArchRecords[iArchRecordIndex].hHandle = (HANDLE)iCustomHandle; + iCustomHandle+=2; + + OpenArchRecords[iArchRecordIndex].dwReadPosition = 0; + OpenArchRecords[iArchRecordIndex].dwFileSize = pFileSystem->GetFileSize(dwFileIndex); + OpenArchRecords[iArchRecordIndex].pbyteDataStart = pFileSystem->GetFileData(dwFileIndex); + OpenArchRecords[iArchRecordIndex].pbyteDataCurrent = OpenArchRecords[iArchRecordIndex].pbyteDataStart; + + return OpenArchRecords[iArchRecordIndex].hHandle; +} //---------------------------------------------------------- @@ -27,10 +106,38 @@ HANDLE WINAPI Arch_CreateFileA( LPCTSTR lpFileName,DWORD dwDesiredAccess, DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { - // TODO: Arch_CreateFileA +#ifdef _DEBUG +/* + char szBuffer[FILENAME_MAX]; + sprintf(szBuffer, "CreateFileA: %s\n", lpFileName); + OutputDebugString(szBuffer); +*/ +#endif - return Real_CreateFileA(lpFileName,dwDesiredAccess,dwShareMode, - lpSecurityAttributes,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile); + DWORD dwFileIndex = pFileSystem->GetFileIndex(FileNameOnly((PCHAR)lpFileName)); + + HANDLE ret=0; + + if(dwFileIndex != FS_INVALID_FILE) + { + // The request file is in the archive, so we should return the handle. + ret = CreateArchRecord(dwFileIndex); + + CHAR szFileName[MAX_PATH+1]; + strcpy(szFileName, FileNameOnly((PCHAR)lpFileName)); + strtolower(szFileName); + UINT digest[5]; + CalcSHA1(szFileName, strlen(szFileName), digest); + CheckFileNameHash(digest); + } + else + { + // Don't check if it's in the archive + ret = Real_CreateFileA(lpFileName,dwDesiredAccess,dwShareMode, + lpSecurityAttributes,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile); + } + + return ret; } //---------------------------------------------------------- @@ -40,10 +147,21 @@ HANDLE WINAPI Arch_CreateFileW( WORD * lpFileName,DWORD dwDesiredAccess, DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { - // TODO: Arch_CreateFileW (unused) +#ifdef _DEBUG +/* + wchar_t wszBuffer[FILENAME_MAX]; + swprintf(wszBuffer, L"CreateFileW: %s\n", lpFileName); + OutputDebugStringW(wszBuffer); +*/ +#endif - return Real_CreateFileW(lpFileName,dwDesiredAccess,dwShareMode, + HANDLE ret=Real_CreateFileW(lpFileName,dwDesiredAccess,dwShareMode, lpSecurityAttributes,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile); + if (IsCheckableFile(ExtensionOnly((PCHAR)lpFileName))) + { + CheckFileHash(GetFileNameHash(strtolower(FileNameOnly((PCHAR)lpFileName))), ret); + } + return ret; } //---------------------------------------------------------- @@ -53,7 +171,68 @@ BOOL WINAPI Arch_ReadFile( HANDLE hFile,LPVOID lpBuffer, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ) { - // TODO: Arch_ReadFile + int iArch=0; + HANDLE hEvent=0; + + if( IsCustomFileHandle((DWORD)hFile) && + ((iArch = FindArchRecordIndexFromHandle(hFile)) != (-1))) + { + //char s[256]; + //sprintf(s,"ReadFile(0x%X)",hFile); + //OutputDebugString(s); + + DWORD dwFileSize = OpenArchRecords[iArch].dwFileSize; + DWORD dwNumBytesRead; + + if(lpOverlapped) { + OpenArchRecords[iArch].dwReadPosition = lpOverlapped->Offset; + OpenArchRecords[iArch].pbyteDataCurrent = + OpenArchRecords[iArch].pbyteDataStart + lpOverlapped->Offset; + hEvent = lpOverlapped->hEvent; + } + + // First condition: EOF + if(OpenArchRecords[iArch].dwReadPosition >= dwFileSize) + { + if(lpNumberOfBytesRead) *lpNumberOfBytesRead = 0; + if(hEvent) SetEvent(hEvent); + + SetLastError(ERROR_HANDLE_EOF); + return TRUE; + } + + DWORD dwEndPoint = (nNumberOfBytesToRead + OpenArchRecords[iArch].dwReadPosition); + + // Second condition: Read will exceed or match EOF + if( dwEndPoint >= dwFileSize ) { + + DWORD dwOverHang = dwEndPoint - dwFileSize; + dwNumBytesRead = nNumberOfBytesToRead - dwOverHang; + + memcpy(lpBuffer,OpenArchRecords[iArch].pbyteDataCurrent,dwNumBytesRead); + OpenArchRecords[iArch].pbyteDataCurrent += dwNumBytesRead; + OpenArchRecords[iArch].dwReadPosition += dwNumBytesRead; + + if(lpNumberOfBytesRead) *lpNumberOfBytesRead = dwNumBytesRead; + if(hEvent) SetEvent(hEvent); + + SetLastError(ERROR_HANDLE_EOF); + + return TRUE; + } + + // Read will not exceed EOF + memcpy(lpBuffer,OpenArchRecords[iArch].pbyteDataCurrent,nNumberOfBytesToRead); + + dwNumBytesRead = nNumberOfBytesToRead; + OpenArchRecords[iArch].pbyteDataCurrent += dwNumBytesRead; + OpenArchRecords[iArch].dwReadPosition += dwNumBytesRead; + + if(lpNumberOfBytesRead) *lpNumberOfBytesRead = dwNumBytesRead; + if(hEvent) SetEvent(hEvent); + + return TRUE; + } return Real_ReadFile(hFile,lpBuffer,nNumberOfBytesToRead, lpNumberOfBytesRead,lpOverlapped); @@ -63,8 +242,19 @@ BOOL WINAPI Arch_ReadFile( HANDLE hFile,LPVOID lpBuffer, DWORD WINAPI Arch_GetFileSize( HANDLE hFile, PDWORD pdwSize ) { - // TODO: Arch_GetFileSize + int iArch; + if( IsCustomFileHandle((DWORD)hFile) && + ((iArch = FindArchRecordIndexFromHandle(hFile)) != (-1))) + { + //char s[256]; + //sprintf(s,"GetFileSize(0x%X)",hFile); + //OutputDebugString(s); + + if (pdwSize) + *pdwSize = 0; + return OpenArchRecords[iArch].dwFileSize; + } return Real_GetFileSize(hFile,pdwSize); } @@ -73,8 +263,30 @@ DWORD WINAPI Arch_GetFileSize( HANDLE hFile, PDWORD pdwSize ) DWORD WINAPI Arch_SetFilePointer( HANDLE hFile,LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod ) { - // TODO: Arch_SetFilePointer + int iArch; + if( IsCustomFileHandle((DWORD)hFile) && + ((iArch = FindArchRecordIndexFromHandle(hFile)) != (-1))) + { + //char s[256]; + //sprintf(s,"SetFilePointer(0x%X)",hFile); + //OutputDebugString(s); + + if(dwMoveMethod == FILE_BEGIN) { + OpenArchRecords[iArch].dwReadPosition = lDistanceToMove; + OpenArchRecords[iArch].pbyteDataCurrent = OpenArchRecords[iArch].pbyteDataStart + lDistanceToMove; + } + else if(dwMoveMethod == FILE_CURRENT) { + OpenArchRecords[iArch].dwReadPosition += lDistanceToMove; + OpenArchRecords[iArch].pbyteDataCurrent += lDistanceToMove; + } + else if(dwMoveMethod == FILE_END) { + OpenArchRecords[iArch].dwReadPosition = OpenArchRecords[iArch].dwFileSize; + OpenArchRecords[iArch].pbyteDataCurrent = + OpenArchRecords[iArch].pbyteDataStart + OpenArchRecords[iArch].dwFileSize; + } + return OpenArchRecords[iArch].dwReadPosition; + } return Real_SetFilePointer(hFile,lDistanceToMove,lpDistanceToMoveHigh,dwMoveMethod); } @@ -82,7 +294,14 @@ DWORD WINAPI Arch_SetFilePointer( HANDLE hFile,LONG lDistanceToMove, BOOL WINAPI Arch_CloseHandle( HANDLE hObject ) { - // TODO: Arch_CloseHandle + int iArch; + + if( IsCustomFileHandle((DWORD)hObject) && + ((iArch = FindArchRecordIndexFromHandle(hObject)) != (-1))) + { + bArchRecordSlotState[iArch] = FALSE; + return TRUE; + } return Real_CloseHandle(hObject); } @@ -91,7 +310,13 @@ BOOL WINAPI Arch_CloseHandle( HANDLE hObject ) DWORD WINAPI Arch_GetFileType( HANDLE hFile ) { - // TODO: Arch_GetFileType + int iArch; + + if( IsCustomFileHandle((DWORD)hFile) && + ((iArch = FindArchRecordIndexFromHandle(hFile)) != (-1))) + { + return FILE_TYPE_DISK; + } return Real_GetFileType(hFile); } @@ -174,3 +399,92 @@ void UninstallFileSystemHooks() //---------------------------------------------------------- +char * FileNameOnly(char *sz) +{ + // remove the trailing space, if it's there. + if(sz[strlen(sz) - 1] == ' ') { + sz[strlen(sz) - 1] = '\0'; + } + + char * org = sz; + char * search = sz + strlen(sz); + + while(search != org) { + if(*search == '/' || *search == '\\') { + return (search+1); + } + search--; + } + return org; +} + +//---------------------------------------------------------- + +char * ExtensionOnly(char *sz) +{ + // remove the trailing space, if it's there. + if(sz[strlen(sz) - 1] == ' ') { + sz[strlen(sz) - 1] = '\0'; + } + + char * org = sz; + char * search = sz + strlen(sz); + + while(search != org) { + if(*search == '.') { + return (search+1); + } + search--; + } + return org; +} + +//---------------------------------------------------------- + +char* strtolower(char* sz) +{ + char* ret = sz; + while (*sz) + { + *sz = tolower(*sz); + sz++; + } + return ret; +} + +//---------------------------------------------------------- + +void CheckFileNameHash(UINT *pDigest) +{ + if( pDigest[0] == dwHandlingDigest[0] && + pDigest[1] == dwHandlingDigest[1] && + pDigest[2] == dwHandlingDigest[2] && + pDigest[3] == dwHandlingDigest[3] && + pDigest[4] == dwHandlingDigest[4] ) + { + if(pGame) + pGame->sub_10062570(); + } + + if( pDigest[0] == dwWeaponDigest[0] && + pDigest[1] == dwWeaponDigest[1] && + pDigest[2] == dwWeaponDigest[2] && + pDigest[3] == dwWeaponDigest[3] && + pDigest[4] == dwWeaponDigest[4] ) + { + if(pGame) + pGame->sub_10062570(); + } + + if( pDigest[0] == dwMeleeDigest[0] && + pDigest[1] == dwMeleeDigest[1] && + pDigest[2] == dwMeleeDigest[2] && + pDigest[3] == dwMeleeDigest[3] && + pDigest[4] == dwMeleeDigest[4] ) + { + if(pGame) + pGame->sub_10062570(); + } +} + +//---------------------------------------------------------- diff --git a/saco/filehooks.h b/saco/filehooks.h index 7c638ec..198e3d5 100644 --- a/saco/filehooks.h +++ b/saco/filehooks.h @@ -9,10 +9,17 @@ void UninstallFileSystemHooks(); typedef struct _ARCH_FILE_RECORD { - char _gap0[20]; + HANDLE hHandle; + DWORD dwReadPosition; + DWORD dwFileSize; + BYTE * pbyteDataStart; + BYTE * pbyteDataCurrent; } ARCH_FILE_RECORD; +#define CUSTOM_HANDLE_BASE 0xFF000001 +#define CUSTOM_HANDLE_LIMIT 0xFF000101 + // File API definitions typedef DWORD (WINAPI *def_GetFileSize)(HANDLE,PDWORD); typedef DWORD (WINAPI *def_SetFilePointer)(HANDLE,LONG,PLONG,DWORD); diff --git a/saco/game/game.h b/saco/game/game.h index 5320a56..63848ec 100644 --- a/saco/game/game.h +++ b/saco/game/game.h @@ -85,6 +85,8 @@ public: //----------------------------------------------------------- + void sub_10062570() { field_55++; }; + CPlayerPed *FindPlayerPed() { if(m_pGamePlayer==NULL) m_pGamePlayer = new CPlayerPed(); return m_pGamePlayer;