diff options
175 files changed, 7304 insertions, 3822 deletions
diff --git a/dep/CascLib/CMakeLists.txt b/dep/CascLib/CMakeLists.txt index 429ebfb78e8..83b7ea38493 100644 --- a/dep/CascLib/CMakeLists.txt +++ b/dep/CascLib/CMakeLists.txt @@ -1,7 +1,7 @@ set(HEADER_FILES src/CascCommon.h src/CascLib.h - src/CascMndxRoot.h + src/CascMndx.h src/CascPort.h src/common/Common.h src/common/FileStream.h @@ -13,19 +13,26 @@ set(HEADER_FILES set(SRC_FILES src/common/Common.cpp src/common/Directory.cpp + src/common/DumpContext.cpp + src/common/DynamicArray.cpp src/common/FileStream.cpp src/common/ListFile.cpp src/common/Map.cpp + src/common/RootHandler.cpp src/jenkins/lookup3.c - src/CascBuildCfg.cpp src/CascCommon.cpp src/CascDecompress.cpp + src/CascDecrypt.cpp src/CascDumpData.cpp + src/CascFiles.cpp src/CascFindFile.cpp - src/CascMndxRoot.cpp src/CascOpenFile.cpp src/CascOpenStorage.cpp src/CascReadFile.cpp + src/CascRootFile_Diablo3.cpp + src/CascRootFile_Mndx.cpp + src/CascRootFile_Ovr.cpp + src/CascRootFile_WoW6.cpp ) set(TOMCRYPT_FILES diff --git a/dep/CascLib/src/CascBuildCfg.cpp b/dep/CascLib/src/CascBuildCfg.cpp deleted file mode 100644 index f39f09d886a..00000000000 --- a/dep/CascLib/src/CascBuildCfg.cpp +++ /dev/null @@ -1,1032 +0,0 @@ -/*****************************************************************************/ -/* CascBuildCfg.cpp Copyright (c) Ladislav Zezula 2014 */ -/*---------------------------------------------------------------------------*/ -/* Build configuration for CascLib */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */ -/*****************************************************************************/ - -#define __CASCLIB_SELF__ -#include "CascLib.h" -#include "CascCommon.h" - -//----------------------------------------------------------------------------- -// Local functions - -static bool inline IsValueSeparator(LPBYTE pbVarValue) -{ - return ((0 <= pbVarValue[0] && pbVarValue[0] <= 0x20) || (pbVarValue[0] == '|')); -} - -static bool IsCharDigit(BYTE OneByte) -{ - return ('0' <= OneByte && OneByte <= '9'); -} - -static void FreeCascBlob(PQUERY_KEY pBlob) -{ - if(pBlob != NULL) - { - if(pBlob->pbData != NULL) - CASC_FREE(pBlob->pbData); - - pBlob->pbData = NULL; - pBlob->cbData = 0; - } -} - -static DWORD GetLocaleMask(const char * szTag) -{ - if(!strcmp(szTag, "enUS")) - return CASC_LOCALE_ENUS; - - if(!strcmp(szTag, "koKR")) - return CASC_LOCALE_KOKR; - - if(!strcmp(szTag, "frFR")) - return CASC_LOCALE_FRFR; - - if(!strcmp(szTag, "deDE")) - return CASC_LOCALE_DEDE; - - if(!strcmp(szTag, "zhCN")) - return CASC_LOCALE_ZHCN; - - if(!strcmp(szTag, "esES")) - return CASC_LOCALE_ESES; - - if(!strcmp(szTag, "zhTW")) - return CASC_LOCALE_ZHTW; - - if(!strcmp(szTag, "enGB")) - return CASC_LOCALE_ENGB; - - if(!strcmp(szTag, "enCN")) - return CASC_LOCALE_ENCN; - - if(!strcmp(szTag, "enTW")) - return CASC_LOCALE_ENTW; - - if(!strcmp(szTag, "esMX")) - return CASC_LOCALE_ESMX; - - if(!strcmp(szTag, "ruRU")) - return CASC_LOCALE_RURU; - - if(!strcmp(szTag, "ptBR")) - return CASC_LOCALE_PTBR; - - if(!strcmp(szTag, "itIT")) - return CASC_LOCALE_ITIT; - - if(!strcmp(szTag, "ptPT")) - return CASC_LOCALE_PTPT; - - return 0; -} - -static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType) -{ - size_t nLength; - - // Check the variable name - nLength = strlen(szVarName); - if((size_t)(szLineEnd - szLineBegin) > nLength) - { - // Check the variable name - if(!_strnicmp(szLineBegin, szVarName, nLength)) - { - // Skip variable name and the exclamation mark - szLineBegin += nLength; - if(szLineBegin < szLineEnd && szLineBegin[0] == '!') - { - // Skip the exclamation mark - szLineBegin++; - - // Check the variable type - nLength = strlen(szVarType); - if((size_t)(szLineEnd - szLineBegin) > nLength) - { - // Check the variable name - if(!_strnicmp(szLineBegin, szVarType, nLength)) - { - // Skip variable type and the doublecolon - szLineBegin += nLength; - return (szLineBegin < szLineEnd && szLineBegin[0] == ':'); - } - } - } - } - } - - return false; -} - -static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd) -{ - while(szLineBegin < szLineEnd) - { - if(szLineBegin[0] == '|') - return szLineBegin + 1; - - szLineBegin++; - } - - return NULL; -} - -static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir) -{ - TCHAR * szIndexPath; - - // Cpmbine the index path - szIndexPath = CombinePath(hs->szDataPath, szSubDir); - if(DirectoryExists(szIndexPath)) - { - hs->szIndexPath = szIndexPath; - return hs->szIndexPath; - } - - CASC_FREE(szIndexPath); - return NULL; -} - -TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator) -{ - // Put the separator, if any - if(chSeparator != 0) - *szBuffer++ = chSeparator; - - // Copy the blob data as text - for(DWORD i = 0; i < cbData; i++) - { - *szBuffer++ = IntToHexChar[pbData[0] >> 0x04]; - *szBuffer++ = IntToHexChar[pbData[0] & 0x0F]; - pbData++; - } - - // Terminate the string - *szBuffer = 0; - - // Return new buffer position - return szBuffer; -} - -static int StringBlobToBinaryBlob( - PQUERY_KEY pBlob, - LPBYTE pbBlobBegin, - LPBYTE pbBlobEnd) -{ - // Sanity checks - assert(pBlob != NULL && pBlob->pbData != NULL); - - // Reset the blob length - pBlob->cbData = 0; - - // Convert the blob - while(pbBlobBegin < pbBlobEnd) - { - BYTE DigitOne; - BYTE DigitTwo; - - DigitOne = (BYTE)(AsciiToUpperTable_BkSlash[pbBlobBegin[0]] - '0'); - if(DigitOne > 9) - DigitOne -= 'A' - '9' - 1; - - DigitTwo = (BYTE)(AsciiToUpperTable_BkSlash[pbBlobBegin[1]] - '0'); - if(DigitTwo > 9) - DigitTwo -= 'A' - '9' - 1; - - if(DigitOne > 0x0F || DigitTwo > 0x0F || pBlob->cbData >= MAX_CASC_KEY_LENGTH) - return ERROR_BAD_FORMAT; - - pBlob->pbData[pBlob->cbData++] = (DigitOne << 0x04) | DigitTwo; - pbBlobBegin += 2; - } - - return ERROR_SUCCESS; -} - - -static LPBYTE FindNextSeparator(PQUERY_KEY pFileBlob, LPBYTE pbFilePtr) -{ - LPBYTE pbFileBegin = pFileBlob->pbData; - LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData; - - if(pbFileBegin <= pbFilePtr && pbFilePtr < pbFileEnd) - { - while(pbFilePtr < pbFileEnd && pbFilePtr[0] != '|') - pbFilePtr++; - - return pbFilePtr; - } - - return NULL; -} - -static bool GetNextFileLine(PQUERY_KEY pFileBlob, LPBYTE * ppbLineBegin, LPBYTE * ppbLineEnd) -{ - LPBYTE pbLineBegin = *ppbLineBegin; - LPBYTE pbLineEnd = *ppbLineEnd; - LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData; - - // If there was a previous line, skip all end-of-line chars - if(pbLineEnd != NULL) - { - // Go to the next line - while(pbLineEnd < pbFileEnd && (pbLineEnd[0] == 0x0A || pbLineEnd[0] == 0x0D)) - pbLineEnd++; - pbLineBegin = pbLineEnd; - - // If there is no more data, return false - if(pbLineEnd >= pbFileEnd) - return false; - } - - // Skip all spaces before the line begins - while(pbLineBegin < pbFileEnd && (pbLineBegin[0] == 0x09 || pbLineBegin[0] == 0x20)) - pbLineBegin++; - pbLineEnd = pbLineBegin; - - // Go to the end of the line - while(pbLineEnd < pbFileEnd && pbLineEnd[0] != 0x0A && pbLineEnd[0] != 0x0D) - pbLineEnd++; - - // Give the results to the caller - *ppbLineBegin = pbLineBegin; - *ppbLineEnd = pbLineEnd; - return true; -} - -static LPBYTE CheckLineVariable(LPBYTE pbLineBegin, LPBYTE pbLineEnd, const char * szVarName) -{ - size_t nLineLength = (size_t)(pbLineEnd - pbLineBegin); - size_t nNameLength = strlen(szVarName); - - // If the line longer than the variable name? - if(nLineLength > nNameLength) - { - if(!_strnicmp((const char *)pbLineBegin, szVarName, nNameLength)) - { - // Skip the variable name - pbLineBegin += nNameLength; - - // Skip the separator(s) - while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) - pbLineBegin++; - - // Check if there is "=" - if(pbLineBegin >= pbLineEnd || pbLineBegin[0] != '=') - return NULL; - pbLineBegin++; - - // Skip the separator(s) - while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) - pbLineBegin++; - - // Check if there is "=" - if(pbLineBegin >= pbLineEnd) - return NULL; - - // Return the begin of the variable - return pbLineBegin; - } - } - - return NULL; -} - -static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue) -{ - const char * szLinePtr = szLineBegin; - - // Sanity checks - assert(pVarBlob->pbData == NULL); - assert(pVarBlob->cbData == 0); - - // Check length of the variable - while(szLinePtr < szLineEnd && szLinePtr[0] != '|') - szLinePtr++; - - // Allocate space for the blob - if(bHexaValue) - { - // Initialize the blob - pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2); - return StringBlobToBinaryBlob(pVarBlob, (LPBYTE)szLineBegin, (LPBYTE)szLinePtr); - } - - // Initialize the blob - pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1); - pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin); - - // Check for success - if(pVarBlob->pbData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Copy the string - memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData); - pVarBlob->pbData[pVarBlob->cbData] = 0; - return ERROR_SUCCESS; -} - - -static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey) -{ - // Get to the end of the file name - szFileName = szFileName + _tcslen(szFileName); - - // Append the "config" directory - _tcscat(szFileName, _T("/config")); - szFileName += 7; - - // Append the first level directory - szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/')); - szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/')); - szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/')); -} - -static DWORD GetBlobCount(LPBYTE pbLineBegin, LPBYTE pbLineEnd) -{ - DWORD dwBlobCount = 0; - - // Until we find an end of the line - while(pbLineBegin < pbLineEnd) - { - // Skip the blob - while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin) == false) - pbLineBegin++; - - // Increment the number of blobs - dwBlobCount++; - - // Skip the separator - while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) - pbLineBegin++; - } - - return dwBlobCount; -} - -static int LoadBlobArray( - PQUERY_KEY pBlob, - DWORD dwMaxBlobs, - LPBYTE pbLineBegin, - LPBYTE pbLineEnd, - LPBYTE pbBuffer, - DWORD dwBufferSize) -{ - LPBYTE pbBlobBegin = pbLineBegin; - LPBYTE pbBlobEnd = pbLineBegin; - int nError = ERROR_SUCCESS; - - // Sanity check - assert(pbBuffer != NULL); - - // Until we find an end of the line - while(pbBlobBegin < pbLineEnd) - { - // Convert the blob from string to binary - if(dwBufferSize < MAX_CASC_KEY_LENGTH) - return ERROR_NOT_ENOUGH_MEMORY; - - // Find the end of the text blob - while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd) == false) - pbBlobEnd++; - - // Convert the blob from ANSI to binary - pBlob->pbData = pbBuffer; - nError = StringBlobToBinaryBlob(pBlob, pbBlobBegin, pbBlobEnd); - if(nError != ERROR_SUCCESS || dwMaxBlobs == 1) - break; - - // Move the blob, buffer, and limits - dwBufferSize -= MAX_CASC_KEY_LENGTH; - pbBuffer += MAX_CASC_KEY_LENGTH; - dwMaxBlobs--; - pBlob++; - - // Skip the separator - while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd)) - pbBlobEnd++; - pbBlobBegin = pbBlobEnd; - } - - return nError; -} - -static int LoadSingleBlob(PQUERY_KEY pBlob, LPBYTE pbBlobBegin, LPBYTE pbBlobEnd) -{ - LPBYTE pbBuffer; - size_t nLength = (pbBlobEnd - pbBlobBegin) / 2; - - // Check maximum size - if(nLength > MAX_CASC_KEY_LENGTH) - return ERROR_INVALID_PARAMETER; - - // Allocate the blob buffer - pbBuffer = CASC_ALLOC(BYTE, MAX_CASC_KEY_LENGTH); - if(pbBuffer == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - return LoadBlobArray(pBlob, 1, pbBlobBegin, pbBlobEnd, pbBuffer, MAX_CASC_KEY_LENGTH); -} - -static PQUERY_KEY LoadMultipleBlobs(LPBYTE pbLineBegin, LPBYTE pbLineEnd, DWORD * pdwBlobCount) -{ - PQUERY_KEY pBlobArray = NULL; - LPBYTE pbBuffer = NULL; - DWORD dwBlobCount = GetBlobCount(pbLineBegin, pbLineEnd); - int nError; - - // Only if there is at least 1 blob - if(dwBlobCount != 0) - { - // Allocate the array of blobs - pBlobArray = CASC_ALLOC(QUERY_KEY, dwBlobCount); - if(pBlobArray != NULL) - { - // Zero the blob array - memset(pBlobArray, 0, dwBlobCount * sizeof(QUERY_KEY)); - - // Allocate buffer for the blobs - pbBuffer = CASC_ALLOC(BYTE, dwBlobCount * MAX_CASC_KEY_LENGTH); - if(pbBuffer != NULL) - { - // Zero the buffer - memset(pbBuffer, 0, dwBlobCount * MAX_CASC_KEY_LENGTH); - - // Load the entire blob array - nError = LoadBlobArray(pBlobArray, dwBlobCount, pbLineBegin, pbLineEnd, pbBuffer, dwBlobCount * MAX_CASC_KEY_LENGTH); - if(nError == ERROR_SUCCESS) - { - *pdwBlobCount = dwBlobCount; - return pBlobArray; - } - - // Free the buffer - CASC_FREE(pbBuffer); - } - - // Free the array of blobs - CASC_FREE(pBlobArray); - pBlobArray = NULL; - } - - // Reset the blob count - dwBlobCount = 0; - } - - *pdwBlobCount = dwBlobCount; - return pBlobArray; -} - -static int LoadTextFile(const TCHAR * szFileName, PQUERY_KEY pFileBlob) -{ - TFileStream * pStream; - ULONGLONG FileSize = 0; - int nError = ERROR_SUCCESS; - - // Open the agent file - pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - // Retrieve its size - FileStream_GetSize(pStream, &FileSize); - - // Load the file to memory - if(0 < FileSize && FileSize < 0x100000) - { - // Initialize the blob - pFileBlob->cbData = (DWORD)FileSize; - pFileBlob->pbData = CASC_ALLOC(BYTE, pFileBlob->cbData + 1); - - // Load the file data into the blob - if(pFileBlob->pbData != NULL) - { - FileStream_Read(pStream, NULL, pFileBlob->pbData, (DWORD)FileSize); - pFileBlob->pbData[pFileBlob->cbData] = 0; - } - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - else - nError = ERROR_INVALID_PARAMETER; - - FileStream_Close(pStream); - } - else - nError = GetLastError(); - - return nError; -} - -static int GetGameType(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd) -{ - // Alpha build of Heroes of the Storm - if((pbLineEnd - pbVarBegin) == 4 && !_strnicmp((const char *)pbVarBegin, "Hero", 4)) - { - hs->dwGameInfo = CASC_GAME_HOTS; - return ERROR_SUCCESS; - } - - // Alpha build of World of Warcraft - Warlords of Draenor - if((pbLineEnd - pbVarBegin) == 3 && !_strnicmp((const char *)pbVarBegin, "WoW", 3)) - { - hs->dwGameInfo = CASC_GAME_WOW6; - return ERROR_SUCCESS; - } - - // Diablo III BETA 2.2.0 - if((pbLineEnd - pbVarBegin) == 7 && !_strnicmp((const char *)pbVarBegin, "Diablo3", 7)) - { - hs->dwGameInfo = CASC_GAME_DIABLO3; - return ERROR_SUCCESS; - } - - // An unknown game - assert(false); - return ERROR_BAD_FORMAT; -} - -// "B29049" -// "WOW-18125patch6.0.1" -// "30013_Win32_2_2_0_Ptr_ptr" -static int GetBuildNumber(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd) -{ - DWORD dwBuildNumber = 0; - - // Skip all non-digit characters - while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0]) == false) - pbVarBegin++; - - // Convert the build number - while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0])) - dwBuildNumber = (dwBuildNumber * 10) + (*pbVarBegin++ - '0'); - - assert(dwBuildNumber != 0); - hs->dwBuildNumber = dwBuildNumber; - return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; -} - -static int GetDefaultLocaleMask(TCascStorage * hs, PQUERY_KEY pTagsString) -{ - char * szTagEnd = (char *)pTagsString->pbData + pTagsString->cbData; - char * szTagPtr = (char *)pTagsString->pbData; - char * szNext; - DWORD dwLocaleMask = 0; - - while(szTagPtr < szTagEnd) - { - // Get the next part - szNext = strchr(szTagPtr, ' '); - if(szNext != NULL) - *szNext++ = 0; - - // Check whether the current tag is a language identifier - dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr); - - // Get the next part - if(szNext == NULL) - break; - - // Skip spaces - while(szNext < szTagEnd && szNext[0] == ' ') - szNext++; - szTagPtr = szNext; - } - - hs->dwDefaultLocale = dwLocaleMask; - return ERROR_SUCCESS; -} - -static int FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PQUERY_KEY pFileBlob) -{ - TCHAR * szFileName; - int nError; - - // Construct the local file name - szFileName = NewStr(hs->szDataPath, 8 + 3 + 3 + 32); - if(szFileName == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Add the part where the config file path is - AppendConfigFilePath(szFileName, pFileKey); - - // Load the config file - nError = LoadTextFile(szFileName, pFileBlob); - if(nError == ERROR_SUCCESS) - { - // Verify the blob's MD5 - if(!VerifyDataBlockHash(pFileBlob->pbData, pFileBlob->cbData, pFileKey->pbData)) - { - FreeCascBlob(pFileBlob); - nError = ERROR_BAD_FORMAT; - } - } - - CASC_FREE(szFileName); - return nError; -} - -static int ParseInfoFile(TCascStorage * hs, PQUERY_KEY pFileBlob) -{ - QUERY_KEY Active = {NULL, 0}; - QUERY_KEY TagString = {NULL, 0}; - QUERY_KEY CdnHost = {NULL, 0}; - QUERY_KEY CdnPath = {NULL, 0}; - const char * szLineBegin1 = NULL; - const char * szLinePtr1 = NULL; - const char * szLineBegin2 = NULL; - const char * szLineEnd1 = NULL; - const char * szLineEnd2 = NULL; - const char * szFileEnd = (const char *)(pFileBlob->pbData + pFileBlob->cbData); - const char * szFilePtr = (const char *)pFileBlob->pbData; - int nError = ERROR_BAD_FORMAT; - - // Find the first line - szLineBegin1 = szFilePtr; - while(szFilePtr < szFileEnd) - { - // Check for the end of the line - if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) - { - szLineEnd1 = szFilePtr; - break; - } - - szFilePtr++; - } - - while (szFilePtr < szFileEnd) - { - szLinePtr1 = szLineBegin1; - - // Skip the newline character(s) - while (szFilePtr < szFileEnd && (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A)) - szFilePtr++; - - // Find the next line - szLineBegin2 = szFilePtr; - while (szFilePtr < szFileEnd) - { - // Check for the end of the line - if (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) - { - szLineEnd2 = szFilePtr; - break; - } - - szFilePtr++; - } - - // Find the build key, CDN config key and the URL path - while (szLinePtr1 < szLineEnd1) - { - // Check for variables we need - if (IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC")) - LoadInfoVariable(&Active, szLineBegin2, szLineEnd2, false); - if (IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX")) - LoadInfoVariable(&hs->CdnBuildKey, szLineBegin2, szLineEnd2, true); - if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX")) - LoadInfoVariable(&hs->CdnConfigKey, szLineBegin2, szLineEnd2, true); - if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING")) - LoadInfoVariable(&CdnHost, szLineBegin2, szLineEnd2, false); - if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING")) - LoadInfoVariable(&CdnPath, szLineBegin2, szLineEnd2, false); - if (IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING")) - LoadInfoVariable(&TagString, szLineBegin2, szLineEnd2, false); - - // Move both line pointers - szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1); - if (szLineBegin1 == NULL) - break; - - szLineBegin2 = SkipInfoVariable(szLineBegin2, szLineEnd2); - if (szLineBegin2 == NULL) - break; - } - - // Stop parsing if found active config - if (Active.pbData != NULL && *Active.pbData == '1') - break; - } - - // All four must be present - if(hs->CdnBuildKey.pbData != NULL && - hs->CdnConfigKey.pbData != NULL && - CdnHost.pbData != NULL && - CdnPath.pbData != NULL) - { - // Merge the CDN host and CDN path - hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1); - if(hs->szUrlPath != NULL) - { - CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData); - CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData); - nError = ERROR_SUCCESS; - } - } - - // If we found tags, we can extract language build from it - if(TagString.pbData != NULL) - GetDefaultLocaleMask(hs, &TagString); - - FreeCascBlob(&CdnHost); - FreeCascBlob(&CdnPath); - FreeCascBlob(&TagString); - return nError; -} - -static int ParseAgentFile(TCascStorage * hs, PQUERY_KEY pFileBlob) -{ - LPBYTE pbBlobBegin = pFileBlob->pbData; - LPBYTE pbBlobEnd; - int nError = ERROR_SUCCESS; - - // Extract the CDN build hash - pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); - if(pbBlobEnd != NULL) - { - // Convert the string to a blob - nError = LoadSingleBlob(&hs->CdnBuildKey, pbBlobBegin, pbBlobEnd); - - // Move to the next part - if(pbBlobEnd[0] == _T('|')) - pbBlobEnd++; - pbBlobBegin = pbBlobEnd; - } - - // Extract the CDN config hash - pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); - if(pbBlobEnd != NULL) - { - // Convert the string to a blob - nError = LoadSingleBlob(&hs->CdnConfigKey, pbBlobBegin, pbBlobEnd); - - // Move to the next part - if(pbBlobEnd[0] == _T('|')) - pbBlobEnd++; - pbBlobBegin = pbBlobEnd; - } - - // Skip the intermediate part - pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); - if(pbBlobEnd != NULL) - { - // Move to the next part - if(pbBlobEnd[0] == _T('|')) - pbBlobEnd++; - pbBlobBegin = pbBlobEnd; - } - - // Extract the URL config hash - pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); - if(pbBlobEnd != NULL) - { - // Convert the string to a blob - hs->szUrlPath = NewStrFromAnsi(pbBlobBegin, pbBlobEnd); - } - - // Verify all variables - if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL) - nError = ERROR_BAD_FORMAT; - return nError; -} - -static int LoadCdnConfigFile(TCascStorage * hs, PQUERY_KEY pFileBlob) -{ - LPBYTE pbLineBegin = pFileBlob->pbData; - LPBYTE pbVarBegin; - LPBYTE pbLineEnd = NULL; - int nError; - - while(pbLineBegin != NULL) - { - // Get the next line - if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd)) - break; - - // Archive group - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archive-group"); - if(pbVarBegin != NULL) - { - nError = LoadSingleBlob(&hs->ArchiveGroup, pbVarBegin, pbLineEnd); - if(nError != ERROR_SUCCESS) - return nError; - continue; - } - - // Archives - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archives"); - if(pbVarBegin != NULL) - { - hs->pArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->ArchiveCount); - if(hs->pArchiveArray == NULL || hs->ArchiveCount == 0) - return ERROR_BAD_FORMAT; - continue; - } - - // Patch archive group - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archive-group"); - if(pbVarBegin != NULL) - { - LoadSingleBlob(&hs->PatchArchiveGroup, pbVarBegin, pbLineEnd); - continue; - } - - // Patch archives - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archives"); - if(pbVarBegin != NULL) - { - hs->pPatchArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->PatchArchiveCount); - continue; - } - } - - // Check if all required fields are present - if(hs->ArchiveGroup.pbData == NULL || hs->ArchiveGroup.cbData == 0 || hs->pArchiveArray == NULL || hs->ArchiveCount == 0) - return ERROR_BAD_FORMAT; - - return ERROR_SUCCESS; -} - -static int LoadCdnBuildFile(TCascStorage * hs, PQUERY_KEY pFileBlob) -{ - LPBYTE pbLineBegin = pFileBlob->pbData; - LPBYTE pbVarBegin; - LPBYTE pbLineEnd = NULL; - - while(pbLineBegin != NULL) - { - // Get the next line - if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd)) - break; - - // Game name - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-product"); - if(pbVarBegin != NULL) - { - GetGameType(hs, pbVarBegin, pbLineEnd); - continue; - } - - // Game build number - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-name"); - if(pbVarBegin != NULL) - { - GetBuildNumber(hs, pbVarBegin, pbLineEnd); - continue; - } - - // Root - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "root"); - if(pbVarBegin != NULL) - { - LoadSingleBlob(&hs->RootKey, pbVarBegin, pbLineEnd); - continue; - } - - // Patch - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch"); - if(pbVarBegin != NULL) - { - LoadSingleBlob(&hs->PatchKey, pbVarBegin, pbLineEnd); - continue; - } - - // Download - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "download"); - if(pbVarBegin != NULL) - { - LoadSingleBlob(&hs->DownloadKey, pbVarBegin, pbLineEnd); - continue; - } - - // Install - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "install"); - if(pbVarBegin != NULL) - { - LoadSingleBlob(&hs->InstallKey, pbVarBegin, pbLineEnd); - continue; - } - - // Encoding keys - pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "encoding"); - if(pbVarBegin != NULL) - { - hs->pEncodingKeys = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->EncodingKeys); - if(hs->pEncodingKeys == NULL || hs->EncodingKeys != 2) - return ERROR_BAD_FORMAT; - - hs->EncodingKey = hs->pEncodingKeys[0]; - hs->EncodingEKey = hs->pEncodingKeys[1]; - continue; - } - } - - // Check the encoding keys - if(hs->pEncodingKeys == NULL || hs->EncodingKeys == 0) - return ERROR_BAD_FORMAT; - return ERROR_SUCCESS; -} - -//----------------------------------------------------------------------------- -// Public functions - -int LoadBuildInfo(TCascStorage * hs) -{ - QUERY_KEY InfoFile = {NULL, 0}; - QUERY_KEY FileData = {NULL, 0}; - TCHAR * szAgentFile; - TCHAR * szInfoFile; - bool bBuildConfigComplete = false; - int nError = ERROR_SUCCESS; - - // Since HOTS build 30027, the game uses build.info file for storage info - if(bBuildConfigComplete == false) - { - szInfoFile = CombinePath(hs->szRootPath, _T(".build.info")); - if(szInfoFile != NULL) - { - nError = LoadTextFile(szInfoFile, &InfoFile); - if(nError == ERROR_SUCCESS) - { - // Parse the info file - nError = ParseInfoFile(hs, &InfoFile); - if(nError == ERROR_SUCCESS) - bBuildConfigComplete = true; - - // Free the loaded blob - FreeCascBlob(&InfoFile); - } - - CASC_FREE(szInfoFile); - } - } - - // If the info file has not been loaded, try the legacy .build.db - if(bBuildConfigComplete == false) - { - szAgentFile = CombinePath(hs->szRootPath, _T(".build.db")); - if(szAgentFile != NULL) - { - nError = LoadTextFile(szAgentFile, &FileData); - if(nError == ERROR_SUCCESS) - { - nError = ParseAgentFile(hs, &FileData); - if(nError == ERROR_SUCCESS) - bBuildConfigComplete = true; - - FreeCascBlob(&FileData); - } - CASC_FREE(szAgentFile); - } - } - - // If the .build.info and .build.db file hasn't been loaded, - if(nError == ERROR_SUCCESS && bBuildConfigComplete == false) - { - nError = ERROR_FILE_CORRUPT; - } - - // Load the configuration file - if(nError == ERROR_SUCCESS) - { - nError = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey, &FileData); - if(nError == ERROR_SUCCESS) - { - nError = LoadCdnConfigFile(hs, &FileData); - FreeCascBlob(&FileData); - } - } - - // Load the build file - if(nError == ERROR_SUCCESS) - { - nError = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey, &FileData); - if(nError == ERROR_SUCCESS) - { - nError = LoadCdnBuildFile(hs, &FileData); - FreeCascBlob(&FileData); - } - } - - // Fill the index directory - if(nError == ERROR_SUCCESS) - { - // First, check for more common "data" subdirectory - if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) - return ERROR_SUCCESS; - - // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) - if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) - return ERROR_SUCCESS; - - nError = ERROR_FILE_NOT_FOUND; - } - - return nError; -} diff --git a/dep/CascLib/src/CascCommon.cpp b/dep/CascLib/src/CascCommon.cpp index 8ad7d716b82..34c3df66b5c 100644 --- a/dep/CascLib/src/CascCommon.cpp +++ b/dep/CascLib/src/CascCommon.cpp @@ -65,3 +65,26 @@ ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes) return Value; } + +void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes) +{ + ValueAsBytes[0] = (Value >> 0x18) & 0xFF; + ValueAsBytes[1] = (Value >> 0x10) & 0xFF; + ValueAsBytes[2] = (Value >> 0x08) & 0xFF; + ValueAsBytes[3] = (Value >> 0x00) & 0xFF; +} + +//----------------------------------------------------------------------------- +// Common fre routine of a CASC blob + +void FreeCascBlob(PQUERY_KEY pBlob) +{ + if(pBlob != NULL) + { + if(pBlob->pbData != NULL) + CASC_FREE(pBlob->pbData); + + pBlob->pbData = NULL; + pBlob->cbData = 0; + } +} diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h index 7a2b4071dc6..42e158bfcb2 100644 --- a/dep/CascLib/src/CascCommon.h +++ b/dep/CascLib/src/CascCommon.h @@ -23,9 +23,13 @@ #include "CascPort.h" #include "common/Common.h" +#include "common/DynamicArray.h" #include "common/Map.h" #include "common/FileStream.h" +#include "common/Directory.h" #include "common/ListFile.h" +#include "common/DumpContext.h" +#include "common/RootHandler.h" // Headers from LibTomCrypt #include "libtomcrypt/src/headers/tomcrypt.h" @@ -36,16 +40,17 @@ //----------------------------------------------------------------------------- // CascLib private defines -#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm -#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor -#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 Since PTR 2.2.0 -#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID +#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm +#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor +#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 since PTR 2.2.0 +#define CASC_GAME_OVERWATCH 0x00040000 // Overwatch since PTR 24919 +#define CASC_GAME_STARCRAFT2 0x00050000 // Starcraft II - Legacy of the Void, since build 38996 +#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID #define CASC_INDEX_COUNT 0x10 #define CASC_FILE_KEY_SIZE 0x09 // Size of the file key #define CASC_MAX_DATA_FILES 0x100 -#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported -#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX' +#define CASC_EXTRA_FILES 0x20 // Number of extra entries to be reserved for additionally inserted files #define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name @@ -71,41 +76,28 @@ #define CASC_PACKAGE_BUFFER 0x1000 -//----------------------------------------------------------------------------- -// On-disk structures - -typedef struct _FILE_LOCALE_BLOCK -{ - DWORD NumberOfFiles; // Number of entries - DWORD Flags; - DWORD Locales; // File locale mask (CASC_LOCALE_XXX) +#ifndef _maxchars +#define _maxchars(buff) ((sizeof(buff) / sizeof(buff[0])) - 1) +#endif - // Followed by a block of 32-bit integers (count: NumberOfFiles) - // Followed by the MD5 and file name hash (count: NumberOfFiles) +//----------------------------------------------------------------------------- +// In-memory structures +// See http://pxr.dk/wowdev/wiki/index.php?title=CASC for more information -} FILE_LOCALE_BLOCK, *PFILE_LOCALE_BLOCK; +struct TFileStream; -typedef struct _FILE_ROOT_ENTRY +typedef enum _CBLD_TYPE { - DWORD EncodingKey[4]; // MD5 of the file - ULONGLONG FileNameHash; // Jenkins hash of the file name - -} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY; + CascBuildNone = 0, // No build type found + CascBuildInfo, // .build.info + CascBuildDb, // .build.db (older storages) +} CBLD_TYPE, *PCBLD_TYPE; -typedef struct _ROOT_BLOCK_INFO +typedef struct _ENCODING_KEY { - PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block - PDWORD pInt32Array; // Pointer to the array of 32-bit integers - PFILE_ROOT_ENTRY pRootEntries; - -} ROOT_BLOCK_INFO, *PROOT_BLOCK_INFO; - -//----------------------------------------------------------------------------- -// In-memory structures + BYTE Value[MD5_HASH_SIZE]; // MD5 of the file -class TMndxFindResult; -struct TFileStream; -struct _MAR_FILE; +} ENCODING_KEY, *PENCODING_KEY; typedef struct _CASC_INDEX_ENTRY { @@ -140,25 +132,34 @@ typedef struct _CASC_FILE_FRAME BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector } CASC_FILE_FRAME, *PCASC_FILE_FRAME; +// The encoding file is in the form of: +// * File header +// * String block #1 +// * Table A header +// * Table A entries +// * Table B header +// * Table B entries +// * String block #2 +// http://pxr.dk/wowdev/wiki/index.php?title=CASC#Key_CASC_Files typedef struct _CASC_ENCODING_HEADER { BYTE Magic[2]; // "EN" - BYTE field_2; - BYTE field_3; - BYTE field_4; - BYTE field_5[2]; - BYTE field_7[2]; - BYTE NumSegments[4]; // Number of entries (big endian) - BYTE field_D[4]; + BYTE Version; // Expected to be 1 by CascLib + BYTE ChecksumSizeA; // The length of the checksums in Encoding Table + BYTE ChecksumSizeB; // The length of the checksums in Encoding Layout Table + BYTE Flags_TableA[2]; // Flags for Encoding Table + BYTE Flags_TableB[2]; // Flags for Encoding Layout Table + BYTE Entries_TableA[4]; // Number of segments in Encoding Table (big endian) + BYTE Entries_TableB[4]; // Number of segments in Encoding Layout Table (big endian) BYTE field_11; - BYTE SegmentsPos[4]; // Offset of encoding segments + BYTE Size_StringTable1[4]; // Size of the string block #1 } CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER; typedef struct _CASC_ENCODING_ENTRY { - USHORT KeyCount; // Number of subitems - BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes + USHORT KeyCount; // Number of index keys + BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key // Followed by the index keys @@ -166,87 +167,21 @@ typedef struct _CASC_ENCODING_ENTRY // Followed by the index keys (number of items = KeyCount) } CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY; -typedef struct _CASC_ROOT_LOCALE_BLOCK -{ - DWORD NumberOfFiles; // Number of entries - DWORD Flags; - DWORD FileLocales; // File locales (CASC_LOCALE_XXX) - - // Followed by a block of 32-bit integers (count: NumberOfFiles) - // Followed by the MD5 and file name hash (count: NumberOfFiles) - -} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK; - -// Root file entry for CASC storages with MNDX root file (Heroes of the Storm) -// Corresponds to the in-file structure -typedef struct _CASC_ROOT_ENTRY_MNDX +// A version of CASC_ENCODING_ENTRY with one index key +typedef struct _CASC_ENCODING_ENTRY_1 { - DWORD Flags; // High 8 bits: Flags, low 24 bits: package index - BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file - DWORD FileSize; // Uncompressed file size, in bytes - -} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX; - -// Root file entry for CASC storages without MNDX root file (World of Warcraft 6.0+) -// Does not match to the in-file structure of the root entry -typedef struct _CASC_ROOT_ENTRY -{ - ULONGLONG FileNameHash; // Jenkins hash of the file name - DWORD SumValue; // Sum value - DWORD Locales; // Locale flags of the file - DWORD EncodingKey[4]; // File encoding key (MD5) - -} CASC_ROOT_ENTRY, *PCASC_ROOT_ENTRY; - -// Definition of the hash table for CASC root items -typedef struct _CASC_ROOT_HASH_TABLE -{ - PCASC_ROOT_ENTRY TablePtr; // Pointer to the CASC root table - DWORD TableSize; // Total size of the root table - DWORD ItemCount; // Number of items currently in the table - -} CASC_ROOT_HASH_TABLE, *PCASC_ROOT_HASH_TABLE; - -typedef struct _CASC_MNDX_INFO -{ - bool bRootFileLoaded; // true if the root info file was properly loaded - BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file - DWORD HeaderVersion; // Must be <= 2 - DWORD FormatVersion; - DWORD field_1C; - DWORD field_20; - DWORD MarInfoOffset; // Offset of the first MAR entry info - DWORD MarInfoCount; // Number of the MAR info entries - DWORD MarInfoSize; // Size of the MAR info entry - DWORD MndxEntriesOffset; - DWORD MndxEntriesTotal; // Total number of MNDX root entries - DWORD MndxEntriesValid; // Number of valid MNDX root entries - DWORD MndxEntrySize; // Size of one MNDX root entry - struct _MAR_FILE * pMarFile1; // File name list for the packages - struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names - struct _MAR_FILE * pMarFile3; // File name list for complete names - PCASC_ROOT_ENTRY_MNDX pMndxEntries; - PCASC_ROOT_ENTRY_MNDX * ppValidEntries; - -} CASC_MNDX_INFO, *PCASC_MNDX_INFO; - -typedef struct _CASC_PACKAGE -{ - char * szFileName; // Pointer to file name - size_t nLength; // Length of the file name - -} CASC_PACKAGE, *PCASC_PACKAGE; + USHORT KeyCount; // Number of index keys + BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes + BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key + BYTE IndexKey[MD5_HASH_SIZE]; // File index key -typedef struct _CASC_PACKAGES -{ - char * szNameBuffer; // Pointer to the buffer for file names - size_t NameEntries; // Number of name entries in Names - size_t NameBufferUsed; // Number of bytes used in the name buffer - size_t NameBufferMax; // Total size of the name buffer +} CASC_ENCODING_ENTRY_1, *PCASC_ENCODING_ENTRY_1; - CASC_PACKAGE Packages[1]; // List of packages +#define GET_INDEX_KEY(pEncodingEntry) (pEncodingEntry->EncodingKey + MD5_HASH_SIZE) +#define FAKE_ENCODING_ENTRY_SIZE (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE) -} CASC_PACKAGES, *PCASC_PACKAGES; +//----------------------------------------------------------------------------- +// Structures for CASC storage and CASC file typedef struct _TCascStorage { @@ -254,6 +189,7 @@ typedef struct _TCascStorage const TCHAR * szIndexFormat; // Format of the index file name TCHAR * szRootPath; // This is the game directory TCHAR * szDataPath; // This is the directory where data files are + TCHAR * szBuildFile; // Build file name (.build.info or .build.db) TCHAR * szIndexPath; // This is the directory where index files are TCHAR * szUrlPath; // URL to the Blizzard servers DWORD dwRefCount; // Number of references @@ -262,40 +198,29 @@ typedef struct _TCascStorage DWORD dwFileBeginDelta; // This is number of bytes to shift back from archive offset (from index entry) to actual begin of file data DWORD dwDefaultLocale; // Default locale, read from ".build.info" + CBLD_TYPE BuildFileType; // Type of the build file + QUERY_KEY CdnConfigKey; QUERY_KEY CdnBuildKey; - - PQUERY_KEY pArchiveArray; // Array of the archives - QUERY_KEY ArchiveGroup; // Name of the group archive file - DWORD ArchiveCount; // Number of archives in the array - - PQUERY_KEY pPatchArchiveArray; // Array of the patch archives - QUERY_KEY PatchArchiveGroup; // Name of the patch group archive file - DWORD PatchArchiveCount; // Number of patch archives in the array - + QUERY_KEY ArchivesGroup; // Key array of the "archive-group" + QUERY_KEY ArchivesKey; // Key array of the "archives" + QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives" QUERY_KEY RootKey; QUERY_KEY PatchKey; QUERY_KEY DownloadKey; QUERY_KEY InstallKey; - - PQUERY_KEY pEncodingKeys; QUERY_KEY EncodingKey; - QUERY_KEY EncodingEKey; - DWORD EncodingKeys; TFileStream * DataFileArray[CASC_MAX_DATA_FILES]; // Data file handles CASC_MAPPING_TABLE KeyMapping[CASC_INDEX_COUNT]; // Key mapping PCASC_MAP pIndexEntryMap; // Map of index entries - PCASC_ENCODING_HEADER pEncodingHeader; // The encoding file - PCASC_ENCODING_ENTRY * ppEncodingEntries; // Map of encoding entries - size_t nEncodingEntries; + QUERY_KEY EncodingFile; // Content of the ENCODING file + PCASC_MAP pEncodingMap; // Map of encoding entries + DYNAMIC_ARRAY ExtraEntries; // Extra encoding entries - CASC_ROOT_HASH_TABLE RootTable; // Hash table for the root entries - - PCASC_MNDX_INFO pMndxInfo; // Used for storages which have MNDX/MAR file - PCASC_PACKAGES pPackages; // Linear list of present packages + TRootHandler * pRootHandler; // Common handler for various ROOT file formats } TCascStorage; @@ -336,16 +261,19 @@ typedef struct _TCascFile typedef struct _TCascSearch { TCascStorage * hs; // Pointer to the storage handle - const char * szClassName; - TCHAR * szListFile; + const char * szClassName; // Contains "TCascSearch" + TCHAR * szListFile; // Name of the listfile void * pCache; // Listfile cache - TMndxFindResult * pStruct1C; // Search structure for MNDX info - char * szMask; + char * szMask; // Search mask char szFileName[MAX_PATH]; // Buffer for the file name - char szNormName[MAX_PATH]; // Buffer for normalized file name - DWORD RootIndex; // Root index of the previously found item - DWORD dwState; // Pointer to the state (0 = listfile, 1 = nameless, 2 = done) - BYTE BitArray[1]; // Bit array of already-reported files + + // Provider-specific data + void * pRootContext; // Root-specific search context + size_t IndexLevel1; // Root-specific search context + size_t IndexLevel2; // Root-specific search context + DWORD dwState; // Pointer to the search state (0 = listfile, 1 = nameless, 2 = done) + + BYTE BitArray[1]; // Bit array of encoding keys. Set for each entry that has already been reported } TCascSearch; @@ -384,10 +312,15 @@ DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes); DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes); ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes); +void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes); +void FreeCascBlob(PQUERY_KEY pQueryKey); + //----------------------------------------------------------------------------- -// Build configuration reading +// Text file parsing (CascFiles.cpp) int LoadBuildInfo(TCascStorage * hs); +int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory); +int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, PQUERY_KEY pEncodingKey, char * szFileName, size_t nMaxChars); //----------------------------------------------------------------------------- // Internal file functions @@ -395,11 +328,20 @@ int LoadBuildInfo(TCascStorage * hs); TCascStorage * IsValidStorageHandle(HANDLE hStorage); TCascFile * IsValidFileHandle(HANDLE hFile); -PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex); -PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex); +PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex); PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey); -int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer); +int CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer); +int CascDecrypt (LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex); +int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer); + +//----------------------------------------------------------------------------- +// Support for ROOT file + +int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); +int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); +int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); +int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask); //----------------------------------------------------------------------------- // Dumping CASC data structures @@ -409,8 +351,7 @@ void CascDumpSparseArray(const char * szFileName, void * pvSparseArray); void CascDumpNameFragTable(const char * szFileName, void * pvMarFile); void CascDumpFileNames(const char * szFileName, void * pvMarFile); void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs); -void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo); -void CascDumpRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, const char * szFormat, const TCHAR * szListFile, int nDumpLevel); +void CascDumpEncodingEntry(TCascStorage * hs, TDumpContext * dc, PCASC_ENCODING_ENTRY pEncodingEntry, int nDumpLevel); void CascDumpFile(const char * szFileName, HANDLE hFile); #endif // _DEBUG diff --git a/dep/CascLib/src/CascDecompress.cpp b/dep/CascLib/src/CascDecompress.cpp index 2858bcec5ab..290b08d64d8 100644 --- a/dep/CascLib/src/CascDecompress.cpp +++ b/dep/CascLib/src/CascDecompress.cpp @@ -13,9 +13,9 @@ #include "CascCommon.h" //----------------------------------------------------------------------------- -// Local functions +// Public functions -static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) +int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) { z_stream z; // Stream information for zlib int nResult; @@ -44,40 +44,3 @@ static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInB // Return an error code return (nResult == Z_OK || nResult == Z_STREAM_END) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; } - -//----------------------------------------------------------------------------- -// Public functions - -int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer) -{ - LPBYTE pbOutBuffer = (LPBYTE)pvOutBuffer; - LPBYTE pbInBuffer = (LPBYTE)pvInBuffer; - DWORD cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer - BYTE uCompression; // Decompressions applied to the data - - // Verify buffer sizes - if(cbInBuffer <= 1) - return 0; - - // Get applied compression types and decrement data length - uCompression = *pbInBuffer++; - cbInBuffer--; - - // Perform the decompressions - switch(uCompression) - { - case 'N': // Uncompressed - - assert(cbOutBuffer == cbInBuffer); - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); - *pcbOutBuffer = cbOutBuffer; - return ERROR_SUCCESS; - - case 'Z': // ZLIB - - return Decompress_ZLIB(pbOutBuffer, pcbOutBuffer, pbInBuffer, cbInBuffer); - } - - assert(false); - return ERROR_NOT_SUPPORTED; -} diff --git a/dep/CascLib/src/CascDecrypt.cpp b/dep/CascLib/src/CascDecrypt.cpp new file mode 100644 index 00000000000..5f4dc77dfe8 --- /dev/null +++ b/dep/CascLib/src/CascDecrypt.cpp @@ -0,0 +1,300 @@ +/*****************************************************************************/ +/* CascDecrypt.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Decryption functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 31.10.15 1.00 Lad The first version of CascDecrypt.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +typedef struct _CASC_ENCRYPTION_KEY +{ + ULONGLONG KeyName; // "Name" of the key + BYTE Key[0x10]; // The key itself +} CASC_ENCRYPTION_KEY, *PCASC_ENCRYPTION_KEY; + +typedef struct _CASC_SALSA20 +{ + DWORD Key[0x10]; + DWORD dwRounds; + +} CASC_SALSA20, *PCASC_SALSA20; + +//----------------------------------------------------------------------------- +// Local variables + +//keyName FB680CB6A8BF81F3 key 62D90EFA7F36D71C398AE2F1FE37BDB9 keyNameSize 8 keySize 16 +//keyName 402CD9D8D6BFED98 key AEB0EADEA47612FE6C041A03958DF241 keyNameSize 8 keySize 16 +//keyName 87AEBBC9C4E6B601 key 685E86C6063DFDA6C9E85298076B3D42 keyNameSize 8 keySize 16 +//keyName A19C4F859F6EFA54 key 0196CB6F5ECBAD7CB5283891B9712B4B keyNameSize 8 keySize 16 +//keyName 11A9203C9881710A key 2E2CB8C397C2F24ED0B5E452F18DC267 keyNameSize 8 keySize 16 +//keyName DBD3371554F60306 key 34E397ACE6DD30EEFDC98A2AB093CD3C keyNameSize 8 keySize 16 +//keyName DEE3A0521EFF6F03 key AD740CE3FFFF9231468126985708E1B9 keyNameSize 8 keySize 16 +//keyName 8C9106108AA84F07 key 53D859DDA2635A38DC32E72B11B32F29 keyNameSize 8 keySize 16 + +static CASC_ENCRYPTION_KEY CascKeys[] = +{ + {0xFB680CB6A8BF81F3ULL, {0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9}}, + {0x402CD9D8D6BFED98ULL, {0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41}}, + {0x87AEBBC9C4E6B601ULL, {0x68, 0x5E, 0x86, 0xC6, 0x06, 0x3D, 0xFD, 0xA6, 0xC9, 0xE8, 0x52, 0x98, 0x07, 0x6B, 0x3D, 0x42}}, + {0xA19C4F859F6EFA54ULL, {0x01, 0x96, 0xCB, 0x6F, 0x5E, 0xCB, 0xAD, 0x7C, 0xB5, 0x28, 0x38, 0x91, 0xB9, 0x71, 0x2B, 0x4B}}, + {0x11A9203C9881710AULL, {0x2E, 0x2C, 0xB8, 0xC3, 0x97, 0xC2, 0xF2, 0x4E, 0xD0, 0xB5, 0xE4, 0x52, 0xF1, 0x8D, 0xC2, 0x67}}, + {0xDBD3371554F60306ULL, {0x34, 0xE3, 0x97, 0xAC, 0xE6, 0xDD, 0x30, 0xEE, 0xFD, 0xC9, 0x8A, 0x2A, 0xB0, 0x93, 0xCD, 0x3C}}, + {0xDEE3A0521EFF6F03ULL, {0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9}}, + {0x8C9106108AA84F07ULL, {0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29}}, + {0x49166D358A34D815ULL, {0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA}}, + {0, {0}} +}; + +static const char * szKeyConstant16 = "expand 16-byte k"; +static const char * szKeyConstant32 = "expand 32-byte k"; + +//----------------------------------------------------------------------------- +// Local functions + +static DWORD Rol32(DWORD dwValue, DWORD dwRolCount) +{ + return (dwValue << dwRolCount) | (dwValue >> (32 - dwRolCount)); +} + +static LPBYTE FindCascKey(ULONGLONG KeyName) +{ + // Search the known keys + for(size_t i = 0; CascKeys[i].KeyName != 0; i++) + { + if(CascKeys[i].KeyName == KeyName) + return CascKeys[i].Key; + } + + // Key not found + return NULL; +} + +static void Initialize(PCASC_SALSA20 pState, LPBYTE pbKey, DWORD cbKeyLength, LPBYTE pbVector) +{ + const char * szConstants = (cbKeyLength == 32) ? szKeyConstant32 : szKeyConstant16; + DWORD KeyIndex = cbKeyLength - 0x10; + + memset(pState, 0, sizeof(CASC_SALSA20)); + pState->Key[0] = *(PDWORD)(szConstants + 0x00); + pState->Key[1] = *(PDWORD)(pbKey + 0x00); + pState->Key[2] = *(PDWORD)(pbKey + 0x04); + pState->Key[3] = *(PDWORD)(pbKey + 0x08); + pState->Key[4] = *(PDWORD)(pbKey + 0x0C); + pState->Key[5] = *(PDWORD)(szConstants + 0x04); + pState->Key[6] = *(PDWORD)(pbVector + 0x00); + pState->Key[7] = *(PDWORD)(pbVector + 0x04); + pState->Key[8] = 0; + pState->Key[9] = 0; + pState->Key[10] = *(PDWORD)(szConstants + 0x08); + pState->Key[11] = *(PDWORD)(pbKey + KeyIndex + 0x00); + pState->Key[12] = *(PDWORD)(pbKey + KeyIndex + 0x04); + pState->Key[13] = *(PDWORD)(pbKey + KeyIndex + 0x08); + pState->Key[14] = *(PDWORD)(pbKey + KeyIndex + 0x0C); + pState->Key[15] = *(PDWORD)(szConstants + 0x0C); + + pState->dwRounds = 20; +} + +static int Decrypt(PCASC_SALSA20 pState, LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer) +{ + LPBYTE pbXorValue; + DWORD KeyMirror[0x10]; + DWORD XorValue[0x10]; + DWORD BlockSize; + DWORD i; + + // Repeat until we have data to read + while(cbInBuffer > 0) + { + // Create the copy of the key + memcpy(KeyMirror, pState->Key, sizeof(KeyMirror)); + + // Shuffle the key + for(i = 0; i < pState->dwRounds; i += 2) + { + KeyMirror[0x04] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x0C]), 0x07); + KeyMirror[0x08] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x00]), 0x09); + KeyMirror[0x0C] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x04]), 0x0D); + KeyMirror[0x00] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x08]), 0x12); + + KeyMirror[0x09] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x01]), 0x07); + KeyMirror[0x0D] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x05]), 0x09); + KeyMirror[0x01] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x09]), 0x0D); + KeyMirror[0x05] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x0D]), 0x12); + + KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x06]), 0x07); + KeyMirror[0x02] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0A]), 0x09); + KeyMirror[0x06] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x0E]), 0x0D); + KeyMirror[0x0A] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x02]), 0x12); + + KeyMirror[0x03] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0B]), 0x07); + KeyMirror[0x07] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x0F]), 0x09); + KeyMirror[0x0B] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x03]), 0x0D); + KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x07]), 0x12); + + KeyMirror[0x01] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x03]), 0x07); + KeyMirror[0x02] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x00]), 0x09); + KeyMirror[0x03] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x01]), 0x0D); + KeyMirror[0x00] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x02]), 0x12); + + KeyMirror[0x06] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x04]), 0x07); + KeyMirror[0x07] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x05]), 0x09); + KeyMirror[0x04] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x06]), 0x0D); + KeyMirror[0x05] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x07]), 0x12); + + KeyMirror[0x0B] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x09]), 0x07); + KeyMirror[0x08] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x0A]), 0x09); + KeyMirror[0x09] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x0B]), 0x0D); + KeyMirror[0x0A] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x08]), 0x12); + + KeyMirror[0x0C] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0E]), 0x07); + KeyMirror[0x0D] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x0F]), 0x09); + KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x0C]), 0x0D); + KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0D]), 0x12); + } + + // Set the number of remaining bytes + pbXorValue = (LPBYTE)XorValue; + BlockSize = (DWORD)CASCLIB_MIN(cbInBuffer, 0x40); + + // Prepare the XOR constants + for(i = 0; i < 16; i++) + { + XorValue[i] = KeyMirror[i] + pState->Key[i]; + } + + // Decrypt the block + for(i = 0; i < BlockSize; i++) + { + pbOutBuffer[i] = pbInBuffer[i] ^ pbXorValue[i]; + } + + pState->Key[8] = pState->Key[8] + 1; + if(pState->Key[8] == 0) + pState->Key[9] = pState->Key[9] + 1; + + // Adjust buffers + pbOutBuffer += BlockSize; + pbInBuffer += BlockSize; + cbInBuffer -= BlockSize; + } + + return ERROR_SUCCESS; +} + +static int Decrypt_Salsa20(LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer, LPBYTE pbKey, DWORD cbKeySize, LPBYTE pbVector) +{ + CASC_SALSA20 SalsaState; + + Initialize(&SalsaState, pbKey, cbKeySize, pbVector); + return Decrypt(&SalsaState, pbOutBuffer, pbInBuffer, cbInBuffer); +} + +//----------------------------------------------------------------------------- +// Public functions + +int CascDecrypt(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex) +{ + ULONGLONG KeyName = 0; + LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer; + LPBYTE pbKey; + DWORD KeyNameSize; + DWORD dwShift = 0; + DWORD IVSize; + BYTE Vector[0x08]; + BYTE EncryptionType; + int nError; + + // Verify and retrieve the key name size + if(pbInBuffer >= pbBufferEnd) + return ERROR_FILE_CORRUPT; + if(pbInBuffer[0] != 0 && pbInBuffer[0] != 8) + return ERROR_NOT_SUPPORTED; + KeyNameSize = *pbInBuffer++; + + // Copy the key name + if((pbInBuffer + KeyNameSize) >= pbBufferEnd) + return ERROR_FILE_CORRUPT; + memcpy(&KeyName, pbInBuffer, KeyNameSize); + pbInBuffer += KeyNameSize; + + // Verify and retrieve the Vector size + if(pbInBuffer >= pbBufferEnd) + return ERROR_FILE_CORRUPT; + if(pbInBuffer[0] != 4 && pbInBuffer[0] != 8) + return ERROR_NOT_SUPPORTED; + IVSize = *pbInBuffer++; + + // Copy the initialization vector + if((pbInBuffer + IVSize) >= pbBufferEnd) + return ERROR_FILE_CORRUPT; + memset(Vector, 0, sizeof(Vector)); + memcpy(Vector, pbInBuffer, IVSize); + pbInBuffer += IVSize; + + // Verify and retrieve the encryption type + if(pbInBuffer >= pbBufferEnd) + return ERROR_FILE_CORRUPT; + if(pbInBuffer[0] != 'S' && pbInBuffer[0] != 'A') + return ERROR_NOT_SUPPORTED; + EncryptionType = *pbInBuffer++; + + // Do we have enough space in the output buffer? + if((DWORD)(pbBufferEnd - pbInBuffer) > pcbOutBuffer[0]) + return ERROR_INSUFFICIENT_BUFFER; + + // Check if we know the key + pbKey = FindCascKey(KeyName); + if(pbKey == NULL) + return ERROR_UNKNOWN_FILE_KEY; + + // Shuffle the Vector with the block index + // Note that there's no point to go beyond 32 bits, unless the file has + // more than 0xFFFFFFFF frames. + for(int i = 0; i < sizeof(dwFrameIndex); i++) + { + Vector[i] = Vector[i] ^ (BYTE)((dwFrameIndex >> dwShift) & 0xFF); + dwShift += 8; + } + + // Perform the decryption-specific action + switch(EncryptionType) + { + case 'S': // Salsa20 + nError = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector); + if(nError != ERROR_SUCCESS) + return nError; + + // Supply the size of the output buffer + pcbOutBuffer[0] = (DWORD)(pbBufferEnd - pbInBuffer); + return ERROR_SUCCESS; + +// case 'A': +// return ERROR_NOT_SUPPORTED; + } + + assert(false); + return ERROR_NOT_SUPPORTED; +} + +int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) +{ + // Check the buffer size + if((cbInBuffer - 1) > pcbOutBuffer[0]) + return ERROR_INSUFFICIENT_BUFFER; + + // Copy the data + memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + pcbOutBuffer[0] = cbInBuffer; + return ERROR_SUCCESS; +} + diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp index 5dc9110b6cd..3c0e385ac07 100644 --- a/dep/CascLib/src/CascDumpData.cpp +++ b/dep/CascLib/src/CascDumpData.cpp @@ -11,14 +11,14 @@ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" -#include "CascMndxRoot.h" +#include "CascMndx.h" -#ifdef _DEBUG // The entire file is only valid for debug purposes +#ifdef _DEBUG // The entire feature is only valid for debug purposes //----------------------------------------------------------------------------- // Forward definitions -LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd); +//LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd); //----------------------------------------------------------------------------- // Sort compare functions @@ -50,160 +50,6 @@ static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, } //----------------------------------------------------------------------------- -// Local functions - -static char * StringFromMD5(LPBYTE md5, char * szBuffer) -{ - return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer); -} - -static char * FormatFileName(const char * szFormat, TCascStorage * hs) -{ - char * szFileName; - char * szSrc; - char * szTrg; - - // Create copy of the file name - szFileName = szSrc = szTrg = NewStr(szFormat, 0); - if(szFileName != NULL) - { - // Format the file name - while(szSrc[0] != 0) - { - if(szSrc[0] == '%') - { - // Replace "%build%" with a build number - if(!strncmp(szSrc, "%build%", 7)) - { - szTrg += sprintf(szTrg, "%u", hs->dwBuildNumber); - szSrc += 7; - continue; - } - } - - // Just copy the character - *szTrg++ = *szSrc++; - } - - // Terminate the target file name - szTrg[0] = 0; - } - - return szFileName; -} - -FILE * CreateDumpFile(const char * szFormat, TCascStorage * hs) -{ - FILE * fp = NULL; - char * szFileName; - - // Validate the storage handle - if(hs != NULL) - { - // Format the real file name - szFileName = FormatFileName(szFormat, hs); - if(szFileName != NULL) - { - // Create the dump file - fp = fopen(szFileName, "wt"); - CASC_FREE(szFileName); - } - } - - return fp; -} - -static void DumpIndexKey( - FILE * fp, - TCascStorage * hs, - LPBYTE pbIndexKey, - int nDumpLevel) -{ - PCASC_INDEX_ENTRY pIndexEntry; - TCascFile * hf; - QUERY_KEY QueryKey; - HANDLE hFile; - BYTE HeaderArea[MAX_HEADER_AREA_SIZE]; - char szBuffer[0x20]; - - QueryKey.pbData = pbIndexKey; - QueryKey.cbData = MD5_HASH_SIZE; - pIndexEntry = FindIndexEntry(hs, &QueryKey); - if(pIndexEntry != NULL) - { - ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE); - DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E); - DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE); - - // Mask the file offset - FileOffset &= 0x3FFFFFFF; - fprintf(fp, " data.%03u at 0x%08x (0x%lx bytes)\n", - ArchIndex, - (DWORD)FileOffset, - FileSize); - - if(nDumpLevel > 2) - { - QueryKey.pbData = pIndexEntry->IndexKey; - QueryKey.cbData = MD5_HASH_SIZE; - if(CascOpenFileByIndexKey((HANDLE)hs, &QueryKey, 0, &hFile)) - { - // Make sure that the data file is open and frame header loaded - CascGetFileSize(hFile, NULL); - hf = IsValidFileHandle(hFile); - assert(hf->pStream != NULL); - - // Read the header area - FileOffset = hf->HeaderOffset - BLTE_HEADER_DELTA; - FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea)); - CascCloseFile(hFile); - - // Dump the header area - fprintf(fp, " FileSize: %X Rest: %s\n", - ConvertBytesToInteger_4_LE(&HeaderArea[0x10]), - StringFromBinary(&HeaderArea[0x14], 10, szBuffer)); - } - } - } - else - { - fprintf(fp, " NO INDEX ENTRY\n"); - } -} - -static void DumpEncodingEntry( - FILE * fp, - TCascStorage * hs, - PCASC_ENCODING_ENTRY pEncodingEntry, - int nDumpLevel) -{ - LPBYTE pbIndexKey; - char szMd5[MD5_STRING_SIZE]; - - // If the encoding key exists - if(pEncodingEntry != NULL) - { - fprintf(fp, " Size %lx Key Count: %u\n", - ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE), - pEncodingEntry->KeyCount); - - // Dump all index keys - pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; - for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++) - { - fprintf(fp, " %s\n", StringFromMD5(pbIndexKey, szMd5)); - DumpIndexKey(fp, hs, pbIndexKey, nDumpLevel); - pbIndexKey += MD5_HASH_SIZE; - } - - } - else - { - fprintf(fp, " NO ENCODING KEYS\n"); - } -} - -//----------------------------------------------------------------------------- // Public functions void CascDumpSparseArray(const char * szFileName, void * pvSparseArray) @@ -323,27 +169,92 @@ void CascDumpFileNames(const char * szFileName, void * pvMarFile) Struct1C.FreeStruct40(); } -void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo) +void CascDumpIndexEntry( + TCascStorage * /* hs */, + TDumpContext * dc, + PCASC_INDEX_ENTRY pIndexEntry, + int /* nDumpLevel */) { - PCASC_ROOT_ENTRY_MNDX pRootEntry; - FILE * fp; - char szMd5[MD5_STRING_SIZE]; + if(pIndexEntry != NULL) + { + ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE); + DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E); + DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE); - // Create the dump file - fp = fopen(szFileName, "wt"); - if(fp != NULL) + // Mask the file offset + FileOffset &= 0x3FFFFFFF; + dump_print(dc, " data.%03u at 0x%08x (0x%lx bytes)\n", + ArchIndex, + (DWORD)FileOffset, + FileSize); + + //if(nDumpLevel > 2) + //{ + // QueryKey.pbData = pIndexEntry->IndexKey; + // QueryKey.cbData = MD5_HASH_SIZE; + // if(CascOpenFileByIndexKey((HANDLE)hs, &QueryKey, 0, &hFile)) + // { + // // Make sure that the data file is open and frame header loaded + // CascGetFileSize(hFile, NULL); + // hf = IsValidFileHandle(hFile); + // assert(hf->pStream != NULL); + + // // Read the header area + // FileOffset = hf->HeaderOffset - BLTE_HEADER_DELTA; + // FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea)); + // CascCloseFile(hFile); + + // // Dump the header area + // dump_print(dc, " FileSize: %X Rest: %s\n", + // ConvertBytesToInteger_4_LE(&HeaderArea[0x10]), + // StringFromBinary(&HeaderArea[0x14], 10, szBuffer)); + // } + //} + } + else { - fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n"); - for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++) - { - pRootEntry = pMndxInfo->ppValidEntries[i]; + dump_print(dc, " NO INDEX ENTRY\n"); + } +} + +void CascDumpEncodingEntry( + TCascStorage * hs, + TDumpContext * dc, + PCASC_ENCODING_ENTRY pEncodingEntry, + int nDumpLevel) +{ + PCASC_INDEX_ENTRY pIndexEntry; + QUERY_KEY QueryKey; + LPBYTE pbIndexKey; + char szMd5[MD5_STRING_SIZE+1]; - fprintf(fp, "%04X %08X %s %08X\n", i, - pRootEntry->Flags, - StringFromMD5(pRootEntry->EncodingKey, szMd5), - pRootEntry->FileSize); + // If the encoding key exists + if(pEncodingEntry != NULL) + { + dump_print(dc, " Size %lx Key Count: %u\n", + ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE), + pEncodingEntry->KeyCount); + + // Dump all index keys + pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++, pbIndexKey += MD5_HASH_SIZE) + { + // Dump the index key + dump_print(dc, " %s\n", StringFromMD5(pbIndexKey, szMd5)); + + // Dump the index entry as well + if(nDumpLevel >= DUMP_LEVEL_INDEX_ENTRIES) + { + QueryKey.pbData = pbIndexKey; + QueryKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(hs, &QueryKey); + CascDumpIndexEntry(hs, dc, pIndexEntry, nDumpLevel); + } } - fclose(fp); + } + else + { + dump_print(dc, " NO ENCODING KEYS\n"); } } @@ -380,7 +291,7 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs) FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE); ArchOffset &= 0x3FFFFFFF; - fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey)); + fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, (DWORD)ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey)); } CASC_FREE(ppIndexEntries); @@ -390,81 +301,6 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs) } } -void CascDumpRootFile( - TCascStorage * hs, - LPBYTE pbRootFile, - DWORD cbRootFile, - const char * szFormat, - const TCHAR * szListFile, - int nDumpLevel) -{ - PCASC_ENCODING_ENTRY pEncodingEntry; - ROOT_BLOCK_INFO BlockInfo; - PLISTFILE_MAP pListMap; - QUERY_KEY EncodingKey; - LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; - LPBYTE pbFilePointer; - FILE * fp; - char szOneLine[0x100]; - DWORD i; - - // This function only dumps WoW-style root file - assert(*(PDWORD)pbRootFile != CASC_MNDX_SIGNATURE); - - // Create the dump file - fp = CreateDumpFile(szFormat, hs); - if(fp != NULL) - { - // Create the listfile map -// DWORD dwTickCount = GetTickCount(); - pListMap = ListFile_CreateMap(szListFile); -// dwTickCount = GetTickCount() - dwTickCount; - - // Dump the root entries as-is - for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; ) - { - // Validate the root block - pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd); - if(pbFilePointer == NULL) - break; - - // Dump the locale block - fprintf(fp, "Flags: %08X Locales: %08X NumberOfFiles: %u\n" - "=========================================================\n", - BlockInfo.pLocaleBlockHdr->Flags, - BlockInfo.pLocaleBlockHdr->Locales, - BlockInfo.pLocaleBlockHdr->NumberOfFiles); - - // Dump the hashes and encoding keys - for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++) - { - // Dump the entry - fprintf(fp, "%08X %08X-%08X %s %s\n", - (DWORD)(BlockInfo.pInt32Array[i]), - (DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20), - (DWORD)(BlockInfo.pRootEntries[i].FileNameHash), - StringFromMD5((LPBYTE)BlockInfo.pRootEntries[i].EncodingKey, szOneLine), - ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash)); - - // Find the encoding entry in the encoding table - if(nDumpLevel > 1) - { - EncodingKey.pbData = (LPBYTE)BlockInfo.pRootEntries[i].EncodingKey; - EncodingKey.cbData = MD5_HASH_SIZE; - pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL); - DumpEncodingEntry(fp, hs, pEncodingEntry, nDumpLevel); - } - } - - // Put extra newline - fprintf(fp, "\n"); - } - - ListFile_FreeMap(pListMap); - fclose(fp); - } -} - void CascDumpFile(const char * szFileName, HANDLE hFile) { FILE * fp; diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp new file mode 100644 index 00000000000..8709ea09e36 --- /dev/null +++ b/dep/CascLib/src/CascFiles.cpp @@ -0,0 +1,981 @@ +/*****************************************************************************/ +/* CascFiles.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Various text file parsers */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */ +/* 30.10.15 1.00 Lad Renamed to CascFiles.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +typedef int (*PARSEINFOFILE)(TCascStorage * hs, void * pvListFile); + +//----------------------------------------------------------------------------- +// Local structures + +struct TBuildFileInfo +{ + const TCHAR * szFileName; + CBLD_TYPE BuildFileType; +}; + +struct TGameIdString +{ + const char * szGameInfo; + size_t cchGameInfo; + DWORD dwGameInfo; +}; + +static const TBuildFileInfo BuildTypes[] = +{ + {_T(".build.info"), CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info + {_T(".build.db"), CascBuildDb}, // Older CASC storages + {NULL, CascBuildNone} +}; + +static const TCHAR * DataDirs[] = +{ + _T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749 + _T("Data\\Casc"), // Overwatch + _T("Data"), // World of Warcraft, Diablo + _T("HeroesData"), // Heroes of the Storm + _T("BNTData"), // Heroes of the Storm, until build 30414 + NULL, +}; + +static const TGameIdString GameIds[] = +{ + {"Hero", 0x04, CASC_GAME_HOTS}, // Alpha build of Heroes of the Storm + {"WoW", 0x03, CASC_GAME_WOW6}, // Alpha build of World of Warcraft - Warlords of Draenor + {"Diablo3", 0x07, CASC_GAME_DIABLO3}, // Diablo III BETA 2.2.0 + {"Prometheus", 0x0A, CASC_GAME_OVERWATCH}, // Overwatch BETA since build 24919 + {"SC2", 0x03, CASC_GAME_STARCRAFT2}, // Starcraft II - Legacy of the Void + {NULL, 0, 0}, +}; + +//----------------------------------------------------------------------------- +// Local functions + +static bool inline IsValueSeparator(const char * szVarValue) +{ + return ((0 <= szVarValue[0] && szVarValue[0] <= 0x20) || (szVarValue[0] == '|')); +} + +static bool IsCharDigit(BYTE OneByte) +{ + return ('0' <= OneByte && OneByte <= '9'); +} + +static DWORD GetLocaleMask(const char * szTag) +{ + if(!strcmp(szTag, "enUS")) + return CASC_LOCALE_ENUS; + + if(!strcmp(szTag, "koKR")) + return CASC_LOCALE_KOKR; + + if(!strcmp(szTag, "frFR")) + return CASC_LOCALE_FRFR; + + if(!strcmp(szTag, "deDE")) + return CASC_LOCALE_DEDE; + + if(!strcmp(szTag, "zhCN")) + return CASC_LOCALE_ZHCN; + + if(!strcmp(szTag, "esES")) + return CASC_LOCALE_ESES; + + if(!strcmp(szTag, "zhTW")) + return CASC_LOCALE_ZHTW; + + if(!strcmp(szTag, "enGB")) + return CASC_LOCALE_ENGB; + + if(!strcmp(szTag, "enCN")) + return CASC_LOCALE_ENCN; + + if(!strcmp(szTag, "enTW")) + return CASC_LOCALE_ENTW; + + if(!strcmp(szTag, "esMX")) + return CASC_LOCALE_ESMX; + + if(!strcmp(szTag, "ruRU")) + return CASC_LOCALE_RURU; + + if(!strcmp(szTag, "ptBR")) + return CASC_LOCALE_PTBR; + + if(!strcmp(szTag, "itIT")) + return CASC_LOCALE_ITIT; + + if(!strcmp(szTag, "ptPT")) + return CASC_LOCALE_PTPT; + + return 0; +} + +static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType) +{ + size_t nLength; + + // Check the variable name + nLength = strlen(szVarName); + if((size_t)(szLineEnd - szLineBegin) > nLength) + { + // Check the variable name + if(!_strnicmp(szLineBegin, szVarName, nLength)) + { + // Skip variable name and the exclamation mark + szLineBegin += nLength; + if(szLineBegin < szLineEnd && szLineBegin[0] == '!') + { + // Skip the exclamation mark + szLineBegin++; + + // Check the variable type + nLength = strlen(szVarType); + if((size_t)(szLineEnd - szLineBegin) > nLength) + { + // Check the variable name + if(!_strnicmp(szLineBegin, szVarType, nLength)) + { + // Skip variable type and the doublecolon + szLineBegin += nLength; + return (szLineBegin < szLineEnd && szLineBegin[0] == ':'); + } + } + } + } + } + + return false; +} + +static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd) +{ + while(szLineBegin < szLineEnd) + { + if(szLineBegin[0] == '|') + return szLineBegin + 1; + + szLineBegin++; + } + + return NULL; +} + +static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir) +{ + TCHAR * szIndexPath; + + // Cpmbine the index path + szIndexPath = CombinePath(hs->szDataPath, szSubDir); + if(DirectoryExists(szIndexPath)) + { + hs->szIndexPath = szIndexPath; + return hs->szIndexPath; + } + + CASC_FREE(szIndexPath); + return NULL; +} + +TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator) +{ + // Put the separator, if any + if(chSeparator != 0) + *szBuffer++ = chSeparator; + + // Copy the blob data as text + for(DWORD i = 0; i < cbData; i++) + { + *szBuffer++ = IntToHexChar[pbData[0] >> 0x04]; + *szBuffer++ = IntToHexChar[pbData[0] & 0x0F]; + pbData++; + } + + // Terminate the string + *szBuffer = 0; + + // Return new buffer position + return szBuffer; +} + +static const char * CheckLineVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName) +{ + size_t nLineLength = (size_t)(szLineEnd - szLineBegin); + size_t nNameLength = strlen(szVarName); + + // If the line longer than the variable name? + if(nLineLength > nNameLength) + { + if(!_strnicmp((const char *)szLineBegin, szVarName, nNameLength)) + { + // Skip the variable name + szLineBegin += nNameLength; + + // Skip the separator(s) + while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin)) + szLineBegin++; + + // Check if there is "=" + if(szLineBegin >= szLineEnd || szLineBegin[0] != '=') + return NULL; + szLineBegin++; + + // Skip the separator(s) + while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin)) + szLineBegin++; + + // Check if there is "=" + if(szLineBegin >= szLineEnd) + return NULL; + + // Return the begin of the variable + return szLineBegin; + } + } + + return NULL; +} + +static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue) +{ + const char * szLinePtr = szLineBegin; + + // Sanity checks + assert(pVarBlob->pbData == NULL); + assert(pVarBlob->cbData == 0); + + // Check length of the variable + while(szLinePtr < szLineEnd && szLinePtr[0] != '|') + szLinePtr++; + + // Allocate space for the blob + if(bHexaValue) + { + // Initialize the blob + pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2); + pVarBlob->cbData = (DWORD)((szLinePtr - szLineBegin) / 2); + return ConvertStringToBinary(szLineBegin, (size_t)(szLinePtr - szLineBegin), pVarBlob->pbData); + } + + // Initialize the blob + pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1); + pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin); + + // Check for success + if(pVarBlob->pbData == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Copy the string + memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData); + pVarBlob->pbData[pVarBlob->cbData] = 0; + return ERROR_SUCCESS; +} + +static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey) +{ + size_t nLength = _tcslen(szFileName); + + // If there is no slash, append if + if(nLength > 0 && szFileName[nLength - 1] != '\\' && szFileName[nLength - 1] != '/') + szFileName[nLength++] = _T('/'); + + // Get to the end of the file name + szFileName = szFileName + nLength; + + // Append the "config" directory + _tcscpy(szFileName, _T("config")); + szFileName += 6; + + // Append the first level directory + szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/')); + szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/')); + szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/')); +} + +static DWORD GetBlobCount(const char * szLineBegin, const char * szLineEnd) +{ + DWORD dwBlobCount = 0; + + // Until we find an end of the line + while(szLineBegin < szLineEnd) + { + // Skip the blob + while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin) == false) + szLineBegin++; + + // Increment the number of blobs + dwBlobCount++; + + // Skip the separator + while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin)) + szLineBegin++; + } + + return dwBlobCount; +} + +static int LoadBlobArray( + PQUERY_KEY pBlob, + const char * szLineBegin, + const char * szLineEnd, + DWORD dwMaxBlobs) +{ + LPBYTE pbBufferEnd = pBlob->pbData + pBlob->cbData; + LPBYTE pbBuffer = pBlob->pbData; + int nError = ERROR_SUCCESS; + + // Sanity check + assert(pBlob->pbData != NULL); + assert(pBlob->cbData != 0); + + // Until we find an end of the line + while(szLineBegin < szLineEnd && dwMaxBlobs > 0) + { + const char * szBlobEnd = szLineBegin; + + // Find the end of the text blob + while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd) == false) + szBlobEnd++; + + // Verify the length of the found blob + if((szBlobEnd - szLineBegin) != MD5_STRING_SIZE) + return ERROR_BAD_FORMAT; + + // Verify if there is enough space in the buffer + if((pbBufferEnd - pbBuffer) < MD5_HASH_SIZE) + return ERROR_NOT_ENOUGH_MEMORY; + + // Perform the conversion + nError = ConvertStringToBinary(szLineBegin, MD5_STRING_SIZE, pbBuffer); + if(nError != ERROR_SUCCESS) + return nError; + + // Move pointers + pbBuffer += MD5_HASH_SIZE; + dwMaxBlobs--; + + // Skip the separator + while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd)) + szBlobEnd++; + szLineBegin = szBlobEnd; + } + + return nError; +} + +static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd, DWORD dwBlobCount) +{ + size_t nLength = (szLineEnd - szLineBegin); + + // We expect each blob to have length of the encoding key and one space between + if(nLength > (dwBlobCount * MD5_STRING_SIZE) + ((dwBlobCount - 1) * sizeof(char))) + return ERROR_INVALID_PARAMETER; + + // Allocate the blob buffer + pBlob->pbData = CASC_ALLOC(BYTE, dwBlobCount * MD5_HASH_SIZE); + if(pBlob->pbData == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Set the buffer size and load the blob array + pBlob->cbData = dwBlobCount * MD5_HASH_SIZE; + return LoadBlobArray(pBlob, szLineBegin, szLineEnd, dwBlobCount); +} + +static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd) +{ + return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, GetBlobCount(szLineBegin, szLineEnd)); +} + +static int LoadSingleBlob(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd) +{ + return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, 1); +} + +static int GetGameType(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd) +{ + // Go through all games that we support + for(size_t i = 0; GameIds[i].szGameInfo != NULL; i++) + { + // Check the length of the variable + if((size_t)(szLineEnd - szVarBegin) == GameIds[i].cchGameInfo) + { + // Check the string + if(!_strnicmp(szVarBegin, GameIds[i].szGameInfo, GameIds[i].cchGameInfo)) + { + hs->dwGameInfo = GameIds[i].dwGameInfo; + return ERROR_SUCCESS; + } + } + } + + // Unknown/unsupported game + assert(false); + return ERROR_BAD_FORMAT; +} + +// "B29049" +// "WOW-18125patch6.0.1" +// "30013_Win32_2_2_0_Ptr_ptr" +// "prometheus-0_8_0_0-24919" +static int GetBuildNumber(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd) +{ + DWORD dwBuildNumber = 0; + + // Skip all non-digit characters + while(szVarBegin < szLineEnd) + { + // There must be at least three digits (build 99 anyone?) + if(IsCharDigit(szVarBegin[0]) && IsCharDigit(szVarBegin[1]) && IsCharDigit(szVarBegin[2])) + { + // Convert the build number string to value + while(szVarBegin < szLineEnd && IsCharDigit(szVarBegin[0])) + dwBuildNumber = (dwBuildNumber * 10) + (*szVarBegin++ - '0'); + break; + } + + // Move to the next + szVarBegin++; + } + + assert(dwBuildNumber != 0); + hs->dwBuildNumber = dwBuildNumber; + return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; +} + +static int GetDefaultLocaleMask(TCascStorage * hs, PQUERY_KEY pTagsString) +{ + char * szTagEnd = (char *)pTagsString->pbData + pTagsString->cbData; + char * szTagPtr = (char *)pTagsString->pbData; + char * szNext; + DWORD dwLocaleMask = 0; + + while(szTagPtr < szTagEnd) + { + // Get the next part + szNext = strchr(szTagPtr, ' '); + if(szNext != NULL) + *szNext++ = 0; + + // Check whether the current tag is a language identifier + dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr); + + // Get the next part + if(szNext == NULL) + break; + + // Skip spaces + while(szNext < szTagEnd && szNext[0] == ' ') + szNext++; + szTagPtr = szNext; + } + + hs->dwDefaultLocale = dwLocaleMask; + return ERROR_SUCCESS; +} + +static void * FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey) +{ + TCHAR * szFileName; + void * pvListFile = NULL; + + // Construct the local file name + szFileName = CascNewStr(hs->szDataPath, 8 + 3 + 3 + 32); + if(szFileName != NULL) + { + // Add the part where the config file path is + AppendConfigFilePath(szFileName, pFileKey); + + // Load and verify the external listfile + pvListFile = ListFile_OpenExternal(szFileName); + if(pvListFile != NULL) + { + if(!ListFile_VerifyMD5(pvListFile, pFileKey->pbData)) + { + ListFile_Free(pvListFile); + pvListFile = NULL; + } + } + + // Free the file name + CASC_FREE(szFileName); + } + + return pvListFile; +} + +static int ParseFile_BuildInfo(TCascStorage * hs, void * pvListFile) +{ + QUERY_KEY Active = {NULL, 0}; + QUERY_KEY TagString = {NULL, 0}; + QUERY_KEY CdnHost = {NULL, 0}; + QUERY_KEY CdnPath = {NULL, 0}; + char szOneLine1[0x200]; + char szOneLine2[0x200]; + size_t nLength1; + size_t nLength2; + int nError = ERROR_BAD_FORMAT; + + // Extract the first line, cotaining the headers + nLength1 = ListFile_GetNextLine(pvListFile, szOneLine1, _maxchars(szOneLine1)); + if(nLength1 == 0) + return ERROR_BAD_FORMAT; + + // Now parse the second and the next lines. We are looking for line + // with "Active" set to 1 + for(;;) + { + const char * szLinePtr1 = szOneLine1; + const char * szLineEnd1 = szOneLine1 + nLength1; + const char * szLinePtr2 = szOneLine2; + const char * szLineEnd2; + + // Read the next line + nLength2 = ListFile_GetNextLine(pvListFile, szOneLine2, _maxchars(szOneLine2)); + if(nLength2 == 0) + break; + szLineEnd2 = szLinePtr2 + nLength2; + + // Parse all variables + while(szLinePtr1 < szLineEnd1) + { + // Check for variables we need + if(IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC")) + LoadInfoVariable(&Active, szLinePtr2, szLineEnd2, false); + if(IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX")) + LoadInfoVariable(&hs->CdnBuildKey, szLinePtr2, szLineEnd2, true); + if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX")) + LoadInfoVariable(&hs->CdnConfigKey, szLinePtr2, szLineEnd2, true); + if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING")) + LoadInfoVariable(&CdnHost, szLinePtr2, szLineEnd2, false); + if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING")) + LoadInfoVariable(&CdnPath, szLinePtr2, szLineEnd2, false); + if(IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING")) + LoadInfoVariable(&TagString, szLinePtr2, szLineEnd2, false); + + // Move both line pointers + szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1); + if(szLinePtr1 == NULL) + break; + + szLinePtr2 = SkipInfoVariable(szLinePtr2, szLineEnd2); + if(szLinePtr2 == NULL) + break; + } + + // Stop parsing if found active config + if(Active.pbData != NULL && *Active.pbData == '1') + break; + + // Free the blobs + FreeCascBlob(&Active); + FreeCascBlob(&hs->CdnBuildKey); + FreeCascBlob(&hs->CdnConfigKey); + FreeCascBlob(&CdnHost); + FreeCascBlob(&CdnPath); + FreeCascBlob(&TagString); + } + + // All four must be present + if(hs->CdnBuildKey.pbData != NULL && + hs->CdnConfigKey.pbData != NULL && + CdnHost.pbData != NULL && + CdnPath.pbData != NULL) + { + // Merge the CDN host and CDN path + hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1); + if(hs->szUrlPath != NULL) + { + CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData); + CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData); + nError = ERROR_SUCCESS; + } + } + + // If we found tags, we can extract language build from it + if(TagString.pbData != NULL) + GetDefaultLocaleMask(hs, &TagString); + + FreeCascBlob(&CdnHost); + FreeCascBlob(&CdnPath); + FreeCascBlob(&TagString); + FreeCascBlob(&Active); + return nError; +} + +static int ParseFile_BuildDb(TCascStorage * hs, void * pvListFile) +{ + const char * szLinePtr; + const char * szLineEnd; + char szOneLine[0x200]; + size_t nLength; + int nError; + + // Load the single line from the text file + nLength = ListFile_GetNextLine(pvListFile, szOneLine, _maxchars(szOneLine)); + if(nLength == 0) + return ERROR_BAD_FORMAT; + + // Set the line range + szLinePtr = szOneLine; + szLineEnd = szOneLine + nLength; + + // Extract the CDN build key + nError = LoadInfoVariable(&hs->CdnBuildKey, szLinePtr, szLineEnd, true); + if(nError == ERROR_SUCCESS) + { + // Skip the variable + szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); + + // Load the CDN config hash + nError = LoadInfoVariable(&hs->CdnConfigKey, szLinePtr, szLineEnd, true); + if(nError == ERROR_SUCCESS) + { + // Skip the variable + szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); + + // Skip the Locale/OS/code variable + szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); + + // Load the URL + hs->szUrlPath = CascNewStrFromAnsi(szLinePtr, szLineEnd); + if(hs->szUrlPath == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + } + + // Verify all variables + if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL) + nError = ERROR_BAD_FORMAT; + return nError; +} + +static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile) +{ + const char * szLineBegin; + const char * szVarBegin; + const char * szLineEnd; + int nError = ERROR_SUCCESS; + + // Keep parsing the listfile while there is something in there + for(;;) + { + // Get the next line + if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) + break; + + // Archive group + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archive-group"); + if(szVarBegin != NULL) + { + nError = LoadSingleBlob(&hs->ArchivesGroup, szVarBegin, szLineEnd); + continue; + } + + // Archives + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archives"); + if(szVarBegin != NULL) + { + nError = LoadMultipleBlobs(&hs->ArchivesKey, szVarBegin, szLineEnd); + continue; + } + + // Patch archive group + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archive-group"); + if(szVarBegin != NULL) + { + LoadSingleBlob(&hs->PatchArchivesKey, szVarBegin, szLineEnd); + continue; + } + + // Patch archives + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archives"); + if(szVarBegin != NULL) + { + nError = LoadMultipleBlobs(&hs->PatchArchivesKey, szVarBegin, szLineEnd); + continue; + } + } + + // Check if all required fields are present + if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0) + return ERROR_BAD_FORMAT; + + return nError; +} + +static int LoadCdnBuildFile(TCascStorage * hs, void * pvListFile) +{ + const char * szLineBegin; + const char * szVarBegin; + const char * szLineEnd = NULL; + int nError = ERROR_SUCCESS; + + for(;;) + { + // Get the next line + if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) + break; + + // Game name + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-product"); + if(szVarBegin != NULL) + { + GetGameType(hs, szVarBegin, szLineEnd); + continue; + } + + // Game build number + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-name"); + if(szVarBegin != NULL) + { + GetBuildNumber(hs, szVarBegin, szLineEnd); + continue; + } + + // Root + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "root"); + if(szVarBegin != NULL) + { + LoadSingleBlob(&hs->RootKey, szVarBegin, szLineEnd); + continue; + } + + // Patch + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch"); + if(szVarBegin != NULL) + { + LoadSingleBlob(&hs->PatchKey, szVarBegin, szLineEnd); + continue; + } + + // Download + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "download"); + if(szVarBegin != NULL) + { + LoadSingleBlob(&hs->DownloadKey, szVarBegin, szLineEnd); + continue; + } + + // Install + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "install"); + if(szVarBegin != NULL) + { + LoadSingleBlob(&hs->InstallKey, szVarBegin, szLineEnd); + continue; + } + + // Encoding keys + szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "encoding"); + if(szVarBegin != NULL) + { + nError = LoadMultipleBlobs(&hs->EncodingKey, szVarBegin, szLineEnd, 2); + continue; + } + } + + // Check the encoding keys + if(hs->EncodingKey.pbData == NULL || hs->EncodingKey.cbData != MD5_HASH_SIZE * 2) + return ERROR_BAD_FORMAT; + return nError; +} + +static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory) +{ + TCHAR * szDataPath; + int nError = ERROR_FILE_NOT_FOUND; + + // Try all known subdirectories + for(size_t i = 0; DataDirs[i] != NULL; i++) + { + // Create the eventual data path + szDataPath = CombinePath(szDirectory, DataDirs[i]); + if(szDataPath != NULL) + { + // Does that directory exist? + if(DirectoryExists(szDataPath)) + { + hs->szDataPath = szDataPath; + return ERROR_SUCCESS; + } + + // Free the data path + CASC_FREE(szDataPath); + } + } + + return nError; +} + + +//----------------------------------------------------------------------------- +// Public functions + +int LoadBuildInfo(TCascStorage * hs) +{ + PARSEINFOFILE PfnParseProc = NULL; + void * pvListFile; + int nError = ERROR_SUCCESS; + + switch(hs->BuildFileType) + { + case CascBuildInfo: + PfnParseProc = ParseFile_BuildInfo; + break; + + case CascBuildDb: + PfnParseProc = ParseFile_BuildDb; + break; + + default: + nError = ERROR_NOT_SUPPORTED; + break; + } + + // Parse the appropriate build file + if(nError == ERROR_SUCCESS) + { + pvListFile = ListFile_OpenExternal(hs->szBuildFile); + if(pvListFile != NULL) + { + // Parse the info file + nError = PfnParseProc(hs, pvListFile); + ListFile_Free(pvListFile); + } + else + nError = ERROR_FILE_NOT_FOUND; + } + + // If the .build.info OR .build.db file has been loaded, + // proceed with loading the CDN config file and CDN build file + if(nError == ERROR_SUCCESS) + { + // Load the configuration file + pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey); + if(pvListFile != NULL) + { + nError = LoadCdnConfigFile(hs, pvListFile); + ListFile_Free(pvListFile); + } + else + nError = ERROR_FILE_NOT_FOUND; + } + + // Load the build file + if(nError == ERROR_SUCCESS) + { + pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey); + if(pvListFile != NULL) + { + nError = LoadCdnBuildFile(hs, pvListFile); + ListFile_Free(pvListFile); + } + else + nError = ERROR_FILE_NOT_FOUND; + } + + // Fill the index directory + if(nError == ERROR_SUCCESS) + { + // First, check for more common "data" subdirectory + if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) + return ERROR_SUCCESS; + + // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) + if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) + return ERROR_SUCCESS; + + nError = ERROR_FILE_NOT_FOUND; + } + + return nError; +} + +// Checks whether there is a ".agent.db". If yes, the function +// sets "szRootPath" and "szDataPath" in the storage structure +// and returns ERROR_SUCCESS +int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory) +{ + TFileStream * pStream; + TCHAR * szBuildFile; + int nError = ERROR_FILE_NOT_FOUND; + + // Try to find any of the root files used in the history + for(size_t i = 0; BuildTypes[i].szFileName != NULL; i++) + { + // Create the full name of the .agent.db file + szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName); + if(szBuildFile != NULL) + { + // Attempt to open the file + pStream = FileStream_OpenFile(szBuildFile, 0); + if(pStream != NULL) + { + // Free the stream + FileStream_Close(pStream); + + // Check for the data directory + nError = CheckDataDirectory(hs, szDirectory); + if(nError == ERROR_SUCCESS) + { + hs->szBuildFile = szBuildFile; + hs->BuildFileType = BuildTypes[i].BuildFileType; + return ERROR_SUCCESS; + } + } + + CASC_FREE(szBuildFile); + } + } + + return nError; +} + +// Parses single line from Overwatch. +// The line structure is: "#MD5|CHUNK_ID|FILENAME|INSTALLPATH" +// The line has all preceding spaces removed +int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, PQUERY_KEY PtrEncodingKey, char * szFileName, size_t nMaxChars) +{ + size_t nLength; + int nError; + + // Check the MD5 (aka encoding key) + if(szLinePtr[MD5_STRING_SIZE] != '|') + return ERROR_BAD_FORMAT; + + // Convert the encoding key to binary + PtrEncodingKey->cbData = MD5_HASH_SIZE; + nError = ConvertStringToBinary(szLinePtr, MD5_STRING_SIZE, PtrEncodingKey->pbData); + if(nError != ERROR_SUCCESS) + return nError; + + // Skip the MD5 + szLinePtr += MD5_STRING_SIZE+1; + + // Skip the chunk ID + szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); + + // Get the archived file name + szLineEnd = SkipInfoVariable(szLinePtr, szLineEnd); + nLength = (size_t)(szLineEnd - szLinePtr - 1); + + // Get the file name + if(nLength > nMaxChars) + return ERROR_INSUFFICIENT_BUFFER; + + memcpy(szFileName, szLinePtr, nLength); + szFileName[nLength] = 0; + return ERROR_SUCCESS; +} diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp index 0bfe16cae1d..bea2e308747 100644 --- a/dep/CascLib/src/CascFindFile.cpp +++ b/dep/CascLib/src/CascFindFile.cpp @@ -11,7 +11,6 @@ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" -#include "CascMndxRoot.h" //----------------------------------------------------------------------------- // Local functions @@ -30,16 +29,22 @@ static void FreeSearchHandle(TCascSearch * pSearch) // Close (dereference) the archive handle if(pSearch->hs != NULL) + { + // Give root handler chance to free their stuff + RootHandler_EndSearch(pSearch->hs->pRootHandler, pSearch); + + // Dereference the storage handle CascCloseStorage((HANDLE)pSearch->hs); - pSearch->hs = NULL; + pSearch->hs = NULL; + } // Free the file cache and frame array if(pSearch->szMask != NULL) CASC_FREE(pSearch->szMask); if(pSearch->szListFile != NULL) CASC_FREE(pSearch->szListFile); - if(pSearch->pStruct1C != NULL) - delete pSearch->pStruct1C; +// if(pSearch->pStruct1C != NULL) +// delete pSearch->pStruct1C; if(pSearch->pCache != NULL) ListFile_Free(pSearch->pCache); @@ -47,58 +52,6 @@ static void FreeSearchHandle(TCascSearch * pSearch) pSearch->szClassName = NULL; CASC_FREE(pSearch); } -/* -DWORD dwRootEntries = 0; -DWORD dwEncoEntries = 0; -DWORD dwIndexEntries = 0; -*/ -static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry, PCASC_FIND_DATA pFindData, size_t nRootIndex) -{ - PCASC_ENCODING_ENTRY pEncodingEntry; - PCASC_INDEX_ENTRY pIndexEntry; - TCascStorage * hs = pSearch->hs; - QUERY_KEY QueryKey; - DWORD dwByteIndex = (DWORD)(nRootIndex / 0x08); - DWORD dwBitIndex = (DWORD)(nRootIndex & 0x07); - - // First of all, check if that entry has been reported before - // If the bit is set, then the file has already been reported - // by a previous search iteration - if(pSearch->BitArray[dwByteIndex] & (1 << dwBitIndex)) - return false; - pSearch->BitArray[dwByteIndex] |= (1 << dwBitIndex); - - // Increment the number of root entries -// dwRootEntries++; - - // Now try to find that encoding key in the array of encoding keys - QueryKey.pbData = (LPBYTE)pRootEntry->EncodingKey; - QueryKey.cbData = MD5_HASH_SIZE; - pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL); - if(pEncodingEntry == NULL) - return false; - -// dwEncoEntries++; - - // Now try to find the index entry. Note that we take the first key - QueryKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; - QueryKey.cbData = MD5_HASH_SIZE; - pIndexEntry = FindIndexEntry(hs, &QueryKey); - if(pIndexEntry == NULL) - return false; - -// dwIndexEntries++; - - // Fill the name hash and the MD5 - memcpy(pFindData->EncodingKey, pRootEntry->EncodingKey, MD5_HASH_SIZE); - pFindData->FileNameHash = pRootEntry->FileNameHash; - pFindData->dwPackageIndex = 0; - pFindData->dwLocaleFlags = pRootEntry->Locales; - - // Fill-in the file size - pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE); - return true; -} static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask) { @@ -106,7 +59,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis size_t cbToAllocate; // When using the MNDX info, do not allocate the extra bit array - cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->RootTable.TableSize / 8) : 0); + cbToAllocate = sizeof(TCascSearch) + ((hs->pEncodingMap->TableSize + 7) / 8); pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate); if(pSearch != NULL) { @@ -125,7 +78,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis // Save the other variables if(szListFile != NULL) { - pSearch->szListFile = NewStr(szListFile, 0); + pSearch->szListFile = CascNewStr(szListFile, 0); if(pSearch->szListFile == NULL) { FreeSearchHandle(pSearch); @@ -134,7 +87,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis } // Allocate the search mask - pSearch->szMask = NewStr(szMask, 0); + pSearch->szMask = CascNewStr(szMask, 0); if(pSearch->szMask == NULL) { FreeSearchHandle(pSearch); @@ -145,64 +98,106 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis return pSearch; } -static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +// Perform searching using root-specific provider. +// The provider may need the listfile +static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { - PCASC_ROOT_ENTRY pRootEntry; - TCascStorage * hs = pSearch->hs; - char szFileName2[MAX_PATH + 1]; - DWORD TableIndex = 0; - - // Get next file from the listfile - while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH)) + PCASC_ENCODING_ENTRY pEncodingEntry; + PCASC_INDEX_ENTRY pIndexEntry; + QUERY_KEY EncodingKey; + QUERY_KEY IndexKey; + LPBYTE pbEncodingKey; + DWORD EncodingIndex = 0; + DWORD LocaleFlags = 0; + DWORD FileSize = CASC_INVALID_SIZE; + DWORD ByteIndex; + DWORD BitMask; + + for(;;) { -#ifdef _DEBUG -// if(!_stricmp(pSearch->szFileName, "Character\\BloodElf\\Female\\DeathKnightEyeGlow.blp")) -// DebugBreak(); -#endif - - // Normalize the file name found in the list file - NormalizeFileName_UpperBkSlash(szFileName2, pSearch->szFileName, MAX_PATH); - - // Find the root entry - pRootEntry = FindRootEntry(hs, szFileName2, &TableIndex); - if(pRootEntry != NULL) + // Attempt to find (the next) file from the root entry + pbEncodingKey = RootHandler_Search(pSearch->hs->pRootHandler, pSearch, &FileSize, &LocaleFlags); + if(pbEncodingKey == NULL) + return false; + + // Verify whether the encoding key exists in the encoding table + EncodingKey.pbData = pbEncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + pEncodingEntry = FindEncodingEntry(pSearch->hs, &EncodingKey, &EncodingIndex); + if(pEncodingEntry != NULL) { - // Verify whether the file exists in the storage - if(VerifyRootEntry(pSearch, pRootEntry, pFindData, TableIndex)) - { - strcpy(pFindData->szFileName, pSearch->szFileName); - pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName); - return true; - } + // Mark the item as already found + // Note: Duplicate items are allowed while we are searching using file names + // Do not exclude items from search if they were found before + ByteIndex = (DWORD)(EncodingIndex / 8); + BitMask = 1 << (EncodingIndex & 0x07); + pSearch->BitArray[ByteIndex] |= BitMask; + + // Locate the index entry + IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry); + IndexKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey); + if(pIndexEntry == NULL) + continue; + + // If we retrieved the file size directly from the root provider, use it + // Otherwise, we need to retrieve it from the encoding entry + if(FileSize == CASC_INVALID_SIZE) + FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE); + + // Fill-in the found file + strcpy(pFindData->szFileName, pSearch->szFileName); + memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE); + pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName); + pFindData->dwLocaleFlags = LocaleFlags; + pFindData->dwFileSize = FileSize; + return true; } } - - // Listfile search ended - return false; } -static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +static bool DoStorageSearch_EncodingKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { - PCASC_ROOT_ENTRY pRootEntry; + PCASC_ENCODING_ENTRY pEncodingEntry; + PCASC_INDEX_ENTRY pIndexEntry; TCascStorage * hs = pSearch->hs; + QUERY_KEY IndexKey; + DWORD ByteIndex; + DWORD BitMask; - // Check if there is more files with the same name hash - while(pSearch->RootIndex < hs->RootTable.TableSize) + // Check for encoding keys that haven't been found yet + while(pSearch->IndexLevel1 < hs->pEncodingMap->TableSize) { - // Get the pointer to the root entry - pRootEntry = hs->RootTable.TablePtr + pSearch->RootIndex; - - // Verify if that root entry exists in the CASC storage - // and was not found before - if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex)) + // Check if that entry has been reported before + ByteIndex = (DWORD)(pSearch->IndexLevel1 / 8); + BitMask = 1 << (pSearch->IndexLevel1 & 0x07); + if((pSearch->BitArray[ByteIndex] & BitMask) == 0) { - pFindData->szFileName[0] = 0; - pFindData->szPlainName = NULL; - return true; + // Locate the index entry + pEncodingEntry = (PCASC_ENCODING_ENTRY)hs->pEncodingMap->HashTable[pSearch->IndexLevel1]; + if(pEncodingEntry != NULL) + { + IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry); + IndexKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey); + if(pIndexEntry != NULL) + { + // Fill-in the found file + memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE); + pFindData->szFileName[0] = 0; + pFindData->szPlainName = NULL; + pFindData->dwLocaleFlags = CASC_LOCALE_NONE; + pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE); + + // Mark the entry as already-found + pSearch->BitArray[ByteIndex] |= BitMask; + return true; + } + } } - // Move to the next entry - pSearch->RootIndex++; + // Go to the next encoding entry + pSearch->IndexLevel1++; } // Nameless search ended @@ -211,10 +206,6 @@ static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindDat static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) { - // Are we searching using the MNDX ? - if(pSearch->hs->pMndxInfo != NULL) - return DoStorageSearch_MNDX(pSearch, pFindData); - // State 0: No search done yet if(pSearch->dwState == 0) { @@ -223,30 +214,25 @@ static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile); // Move the search phase to the listfile searching - pSearch->RootIndex = 0; + pSearch->IndexLevel1 = 0; pSearch->dwState++; - - // If either file stream or listfile cache are invalid, - // move to the next phase - if(pSearch->pCache == NULL) - pSearch->dwState++; } // State 1: Searching the list file if(pSearch->dwState == 1) { - if(DoStorageSearch_ListFile(pSearch, pFindData)) + if(DoStorageSearch_RootFile(pSearch, pFindData)) return true; // Move to the nameless search state - assert(pSearch->RootIndex == 0); + pSearch->IndexLevel1 = 0; pSearch->dwState++; } // State 2: Searching the remaining entries if(pSearch->dwState == 2) { - if(DoStorageSearch_Hash(pSearch, pFindData)) + if(DoStorageSearch_EncodingKey(pSearch, pFindData)) return true; // Move to the final search state @@ -275,19 +261,12 @@ HANDLE WINAPI CascFindFirstFile( if(szMask == NULL || pFindData == NULL) nError = ERROR_INVALID_PARAMETER; - // Allocate the structure for archive search + // Init the search structure and search handle if(nError == ERROR_SUCCESS) { // Clear the entire search structure memset(pFindData, 0, sizeof(CASC_FIND_DATA)); - // We must have listfile for non-MNDX storages - if(hs->pMndxInfo == NULL && szListFile == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - // Allocate the search handle pSearch = AllocateSearchHandle(hs, szListFile, szMask); if(pSearch == NULL) diff --git a/dep/CascLib/src/CascLib.def b/dep/CascLib/src/CascLib.def new file mode 100644 index 00000000000..cb5f9166e49 --- /dev/null +++ b/dep/CascLib/src/CascLib.def @@ -0,0 +1,29 @@ +; +; Export file for Windows +; Copyright (c) 2015 Ladislav Zezula +; ladik@zezula.net +; + +LIBRARY CascLib.dll + +EXPORTS + + CascOpenStorage + CascGetStorageInfo + CascCloseStorage + + CascOpenFileByIndexKey + CascOpenFileByEncodingKey + CascOpenFile + CascGetFileSize + CascSetFilePointer + CascReadFile + CascCloseFile + + CascFindFirstFile + CascFindNextFile + CascFindClose + + GetLastError=Kernel32.GetLastError + SetLastError=Kernel32.SetLastError +
\ No newline at end of file diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h index 330a4b2bb49..bad32eb2dba 100644 --- a/dep/CascLib/src/CascLib.h +++ b/dep/CascLib/src/CascLib.h @@ -39,7 +39,7 @@ extern "C" { #define CASC_STOR_XXXXX 0x00000001 // Not used // Values for CascOpenFile -#define CASC_FILE_XXXXX 0x00000001 // Not used +#define CASC_OPEN_BY_ENCODING_KEY 0x00000001 // The name is just the encoding key; skip ROOT file processing // Flags for file stream #define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file @@ -103,7 +103,7 @@ extern "C" { #ifndef MD5_HASH_SIZE #define MD5_HASH_SIZE 0x10 -#define MD5_STRING_SIZE 0x21 +#define MD5_STRING_SIZE 0x20 #endif #ifndef SHA1_DIGEST_SIZE @@ -146,9 +146,7 @@ typedef struct _CASC_FIND_DATA { char szFileName[MAX_PATH]; // Full name of the found file char * szPlainName; // Plain name of the found file - ULONGLONG FileNameHash; // File name hash BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key - DWORD dwPackageIndex; // File package index (HOTS only) DWORD dwLocaleFlags; // Locale flags (WoW only) DWORD dwFileSize; // Size of the file @@ -184,6 +182,16 @@ HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData); bool WINAPI CascFindClose(HANDLE hFind); +//----------------------------------------------------------------------------- +// GetLastError/SetLastError support for non-Windows platform + +#ifndef PLATFORM_WINDOWS + +int GetLastError(); +void SetLastError(int nError); + +#endif // PLATFORM_WINDOWS + #ifdef __cplusplus } // extern "C" #endif diff --git a/dep/CascLib/src/CascMndxRoot.h b/dep/CascLib/src/CascMndx.h index bd93f230845..d1b6653d4fe 100644 --- a/dep/CascLib/src/CascMndxRoot.h +++ b/dep/CascLib/src/CascMndx.h @@ -13,6 +13,9 @@ class TFileNameDatabase; +#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported +#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX' + #define CASC_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type)) #define CASC_SEARCH_INITIALIZING 0 @@ -353,13 +356,4 @@ inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex) return ((Table.NameFragArray[ItemIndex].FragOffs & 0xFFFFFF00) == 0xFFFFFF00); } -//----------------------------------------------------------------------------- -// CASC functions related to MNDX - -int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName); -int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppFoundInfo); -bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData); -void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo); - #endif // __CASC_MNDX_ROOT__ diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp index 2b8c3d3c4ad..c4c27a3f6a6 100644 --- a/dep/CascLib/src/CascOpenFile.cpp +++ b/dep/CascLib/src/CascOpenFile.cpp @@ -11,10 +11,6 @@ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" -#include "CascMndxRoot.h" - -//----------------------------------------------------------------------------- -// Local structures //----------------------------------------------------------------------------- // Local functions @@ -31,79 +27,19 @@ PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey) PCASC_INDEX_ENTRY pIndexEntry = NULL; if(hs->pIndexEntryMap != NULL) - pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData); + pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData, NULL); return pIndexEntry; } -PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex) +PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex) { - PCASC_ENCODING_ENTRY pEncodingEntry; - size_t StartEntry = 0; - size_t MidlEntry; - size_t EndEntry = hs->nEncodingEntries; - int nResult; - - // Perform binary search - while(StartEntry < EndEntry) - { - // Calculate the middle of the interval - MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2); - pEncodingEntry = hs->ppEncodingEntries[MidlEntry]; - - // Did we find it? - nResult = memcmp(pEncodingKey->pbData, pEncodingEntry->EncodingKey, MD5_HASH_SIZE); - if(nResult == 0) - { - if(PtrIndex != NULL) - PtrIndex[0] = MidlEntry; - return pEncodingEntry; - } - - // Move the interval to the left or right - (nResult < 0) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1; - } - - // Not found, sorry - return NULL; -} - -// Also used in CascSearchFile -PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex) -{ - PCASC_ROOT_ENTRY pRootEntry; - ULONGLONG FileNameHash; - DWORD TableIndex; - uint32_t dwHashHigh = 0; - uint32_t dwHashLow = 0; - - // Calculate the HASH value of the normalized file name - hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow); - FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; - - // Get the first table index - TableIndex = (DWORD)(FileNameHash & (hs->RootTable.TableSize - 1)); - assert(hs->RootTable.ItemCount < hs->RootTable.TableSize); - - // Search the proper entry - for(;;) - { - // Does the has match? - pRootEntry = hs->RootTable.TablePtr + TableIndex; - if(pRootEntry->FileNameHash == FileNameHash) - { - if(PtrTableIndex != NULL) - PtrTableIndex[0] = TableIndex; - return pRootEntry; - } + PCASC_ENCODING_ENTRY pEncodingEntry = NULL; - // If the entry is free, the file is not there - if(pRootEntry->FileNameHash == 0 && pRootEntry->SumValue == 0) - return NULL; + if(hs->pEncodingMap != NULL) + pEncodingEntry = (PCASC_ENCODING_ENTRY)Map_FindObject(hs->pEncodingMap, pEncodingKey->pbData, PtrIndex); - // Move to the next entry - TableIndex = (DWORD)((TableIndex + 1) & (hs->RootTable.TableSize - 1)); - } + return pEncodingEntry; } static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry) @@ -187,7 +123,7 @@ static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DW // Prepare the file index and open the file by index // Note: We don't know what to do if there is more than just one index key // We always take the first file present. Is that correct? - IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry); IndexKey.cbData = MD5_HASH_SIZE; if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, ppCascFile)) { @@ -259,13 +195,10 @@ bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile) { - PCASC_ROOT_ENTRY_MNDX pRootEntryMndx = NULL; - PCASC_ROOT_ENTRY pRootEntry; - PCASC_PACKAGE pPackage; TCascStorage * hs; QUERY_KEY EncodingKey; - char * szStrippedName; - char szFileName2[MAX_PATH+1]; + LPBYTE pbEncodingKey; + BYTE KeyBuffer[MD5_HASH_SIZE]; int nError = ERROR_SUCCESS; CASCLIB_UNUSED(dwLocale); @@ -285,55 +218,37 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal return false; } - // If the storage has a MNDX root directory, use it to search the entry - if(hs->pMndxInfo != NULL) + // If the user is opening the file via encoding key, skip the ROOT file processing + if((dwFlags & CASC_OPEN_BY_ENCODING_KEY) == 0) { - // Convert the file name to lowercase + slashes - NormalizeFileName_LowerSlash(szFileName2, szFileName, MAX_PATH); - - // Find the package number - pPackage = FindMndxPackage(hs, szFileName2); - if(pPackage != NULL) + // Let the root directory provider get us the encoding key + pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName); + if(pbEncodingKey == NULL) { - // Cut the package name off the full path - szStrippedName = szFileName2 + pPackage->nLength; - while(szStrippedName[0] == '/') - szStrippedName++; - - nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntryMndx); - if(nError == ERROR_SUCCESS) - { - // Prepare the encoding key - EncodingKey.pbData = pRootEntryMndx->EncodingKey; - EncodingKey.cbData = MD5_HASH_SIZE; - } - } - else - { - nError = ERROR_FILE_NOT_FOUND; + SetLastError(ERROR_FILE_NOT_FOUND); + return false; } + + // Setup the encoding key + EncodingKey.pbData = pbEncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; } else { - // Convert the file name to lowercase + slashes - NormalizeFileName_UpperBkSlash(szFileName2, szFileName, MAX_PATH); - - // Check the root directory for that hash - pRootEntry = FindRootEntry(hs, szFileName2, NULL); - if(pRootEntry != NULL) - { - // Prepare the root key - EncodingKey.pbData = (LPBYTE)pRootEntry->EncodingKey; - EncodingKey.cbData = MD5_HASH_SIZE; - nError = ERROR_SUCCESS; - } - else + // Check the length of the file name + if(strlen(szFileName) < MD5_STRING_SIZE) { - nError = ERROR_FILE_NOT_FOUND; + SetLastError(ERROR_INVALID_PARAMETER); + return false; } + + // Convert the file name to binary blob + EncodingKey.pbData = KeyBuffer; + EncodingKey.cbData = MD5_HASH_SIZE; + nError = ConvertStringToBinary(szFileName, MD5_STRING_SIZE, KeyBuffer); } - // Use the root key to find the file in the encoding table entry + // Use the encoding key to find the file in the encoding table entry if(nError == ERROR_SUCCESS) { if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, (TCascFile **)phFile)) @@ -344,10 +259,10 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal } #ifdef CASCLIB_TEST - if(phFile[0] != NULL && pRootEntryMndx != NULL) - { - ((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize; - } +// if(phFile[0] != NULL && pRootEntryMndx != NULL) +// { +// ((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize; +// } #endif if(nError != ERROR_SUCCESS) diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp index 6a8d83ee903..c3d623df9f0 100644 --- a/dep/CascLib/src/CascOpenStorage.cpp +++ b/dep/CascLib/src/CascOpenStorage.cpp @@ -13,19 +13,12 @@ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" -#include "CascMndxRoot.h" - -//----------------------------------------------------------------------------- -// Dumping options - -#ifdef _DEBUG -#define CASC_DUMP_ROOT_FILE 2 // The root file will be dumped (level 2) -#endif //----------------------------------------------------------------------------- // Local structures -#define CASC_INITIAL_ROOT_TABLE_SIZE 0x00100000 +// Size of one segment in the ENCODING table +// The segment is filled by entries of type #define CASC_ENCODING_SEGMENT_SIZE 0x1000 typedef struct _BLOCK_SIZE_AND_HASH @@ -67,25 +60,10 @@ typedef struct _FILE_INDEX_HEADER_V2 } FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2; -typedef struct _FILE_ENCODING_HEADER -{ - BYTE Magic[2]; // "EN" - BYTE field_2; - BYTE field_3; - BYTE field_4; - BYTE field_5[2]; - BYTE field_7[2]; - BYTE NumSegments[4]; // Number of entries (big endian) - BYTE field_D[4]; - BYTE field_11; - BYTE SegmentsPos[4]; // Offset of encoding segments - -} FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER; - typedef struct _FILE_ENCODING_SEGMENT { - BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment - BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment + BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment + BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment } FILE_ENCODING_SEGMENT, *PFILE_ENCODING_SEGMENT; @@ -143,28 +121,6 @@ static bool IsIndexFileName_V2(const TCHAR * szFileName) _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0); } -static void QUERY_KEY_Free(PQUERY_KEY pBlob) -{ - if(pBlob != NULL) - { - if(pBlob->pbData != NULL) - CASC_FREE(pBlob->pbData); - - pBlob->pbData = NULL; - pBlob->cbData = 0; - } -} - -static void QUERY_KEY_FreeArray(PQUERY_KEY pBlobArray) -{ - // Free the buffer in the first blob - // (will also free all buffers in the array) - QUERY_KEY_Free(pBlobArray); - - // Free the array itself - CASC_FREE(pBlobArray); -} - static bool IsCascIndexHeader_V1(LPBYTE pbFileData, DWORD cbFileData) { PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData; @@ -206,52 +162,99 @@ static bool IsCascIndexHeader_V2(LPBYTE pbFileData, DWORD cbFileData) return (HashHigh == pSizeAndHash->dwBlockHash); } -LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd) +static bool CutLastPathPart(TCHAR * szWorkPath) { - // Validate the file locale block - pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer; - pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1); - if(pbFilePointer > pbFileEnd) - return NULL; - - // Validate the array of 32-bit integers - pBlockInfo->pInt32Array = (PDWORD)pbFilePointer; - pbFilePointer = (LPBYTE)(pBlockInfo->pInt32Array + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); - if(pbFilePointer > pbFileEnd) - return NULL; - - // Validate the array of root entries - pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer; - pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); - if(pbFilePointer > pbFileEnd) - return NULL; - - // Return the position of the next block - return pbFilePointer; + size_t nLength = _tcslen(szWorkPath); + + for(nLength = _tcslen(szWorkPath); nLength > 0; nLength--) + { + if(szWorkPath[nLength] == '\\' || szWorkPath[nLength] == '/') + { + szWorkPath[nLength] = 0; + return true; + } + } + + return false; } -static int InitializeCascDirectories(TCascStorage * hs, const TCHAR * szDataPath) +static int InsertExtraFile( + TCascStorage * hs, + const char * szFileName, + PQUERY_KEY pQueryKey) { - TCHAR * szLastPathPart; + // If the given key is not encoding key (aka, it's an index key), + // we need to create a fake encoding entry + if(pQueryKey->cbData == MD5_HASH_SIZE * 2) + { + PCASC_ENCODING_ENTRY pNewEntry; + PCASC_INDEX_ENTRY pIndexEntry; + QUERY_KEY IndexKey; + + // Find the entry in the index table in order to get the file size + IndexKey.pbData = pQueryKey->pbData + MD5_HASH_SIZE; + IndexKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(hs, &IndexKey); + if(pIndexEntry == NULL) + return ERROR_FILE_NOT_FOUND; + + // Create a fake entry in the encoding map + pNewEntry = (PCASC_ENCODING_ENTRY)Array_Insert(&hs->ExtraEntries, NULL, 1); + if(pNewEntry == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill the encoding entry + pNewEntry->KeyCount = 1; + pNewEntry->FileSizeBE[0] = pIndexEntry->FileSizeLE[3]; + pNewEntry->FileSizeBE[1] = pIndexEntry->FileSizeLE[2]; + pNewEntry->FileSizeBE[2] = pIndexEntry->FileSizeLE[1]; + pNewEntry->FileSizeBE[3] = pIndexEntry->FileSizeLE[0]; + memcpy(pNewEntry->EncodingKey, pQueryKey->pbData, MD5_HASH_SIZE); + memcpy(pNewEntry + 1, pQueryKey->pbData + MD5_HASH_SIZE, MD5_HASH_SIZE); + + // Insert the entry to the map of encoding keys + Map_InsertObject(hs->pEncodingMap, pNewEntry, pNewEntry->EncodingKey); + } - // Save the game data directory - hs->szDataPath = NewStr(szDataPath, 0); - - // Save the root game directory - hs->szRootPath = NewStr(szDataPath, 0); + // Now we need to insert the entry to the root handler in order + // to be able to translate file name to encoding key + return RootHandler_Insert(hs->pRootHandler, szFileName, pQueryKey->pbData); +} + +static int InitializeCascDirectories(TCascStorage * hs, const TCHAR * szDataPath) +{ + TCHAR * szWorkPath; + int nError = ERROR_NOT_ENOUGH_MEMORY; - // Find the last part - szLastPathPart = hs->szRootPath; - for(size_t i = 0; hs->szRootPath[i] != 0; i++) + // Find the root directory of the storage. The root directory + // is the one where ".build.info" is. + szWorkPath = CascNewStr(szDataPath, 0); + if(szWorkPath != NULL) { - if(hs->szRootPath[i] == '\\' || hs->szRootPath[i] == '/') - szLastPathPart = hs->szRootPath + i; - } - - // Cut the last part - if(szLastPathPart != NULL) - szLastPathPart[0] = 0; - return (hs->szRootPath && hs->szDataPath) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; + // Get the length and go up until we find the ".build.info" or ".build.db" + for(;;) + { + // Is this a game directory? + nError = CheckGameDirectory(hs, szWorkPath); + if(nError == ERROR_SUCCESS) + { + nError = ERROR_SUCCESS; + break; + } + + // Cut one path part + if(!CutLastPathPart(szWorkPath)) + { + nError = ERROR_FILE_NOT_FOUND; + break; + } + } + + // Free the work path buffer + CASC_FREE(szWorkPath); + } + + return nError; } static bool IndexDirectory_OnFileFound( @@ -566,7 +569,7 @@ static int CreateArrayOfIndexEntries(TCascStorage * hs) // 9e dc a7 8f e2 09 ad d8 b7 (encoding file) // f3 5e bb fb d1 2b 3f ef 8b // c8 69 9f 18 a2 5e df 7e 52 - Map_InsertObject(pMap, pIndexEntry->IndexKey); + Map_InsertObject(pMap, pIndexEntry, pIndexEntry->IndexKey); // Move to the next entry pIndexEntry++; @@ -581,28 +584,28 @@ static int CreateArrayOfIndexEntries(TCascStorage * hs) return nError; } -static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumberOfSegments) +static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumSegments) { PCASC_ENCODING_ENTRY pEncodingEntry; - size_t nMaxEntries; - size_t nEntries = 0; + DWORD dwMaxEntries; int nError = ERROR_SUCCESS; // Sanity check - assert(hs->ppEncodingEntries == NULL); assert(hs->pIndexEntryMap != NULL); + assert(hs->pEncodingMap == NULL); - // Calculate the largest eventual number of encodign entries - nMaxEntries = (dwNumberOfSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE); + // Calculate the largest eventual number of encoding entries + // Add space for extra entries + dwMaxEntries = (dwNumSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE); - // Allocate the array of pointers to encoding entries - hs->ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, nMaxEntries); - if(hs->ppEncodingEntries != NULL) + // Create the map of the encoding entries + hs->pEncodingMap = Map_Create(dwMaxEntries + CASC_EXTRA_FILES, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ENCODING_ENTRY, EncodingKey)); + if(hs->pEncodingMap != NULL) { - LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments); + LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments); // Parse all segments - for(DWORD i = 0; i < dwNumberOfSegments; i++) + for(DWORD i = 0; i < dwNumSegments; i++) { LPBYTE pbEncodingEntry = pbStartOfSegment; LPBYTE pbEndOfSegment = pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE - sizeof(CASC_ENCODING_ENTRY) - MD5_HASH_SIZE; @@ -616,7 +619,7 @@ static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEn break; // Insert the pointer the array - hs->ppEncodingEntries[nEntries++] = pEncodingEntry; + Map_InsertObject(hs->pEncodingMap, pEncodingEntry, pEncodingEntry->EncodingKey); // Move to the next encoding entry pbEncodingEntry += sizeof(CASC_ENCODING_ENTRY) + (pEncodingEntry->KeyCount * MD5_HASH_SIZE); @@ -625,9 +628,6 @@ static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEn // Move to the next segment pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE; } - - // Remember the total number of encoding entries - hs->nEncodingEntries = nEntries; } else nError = ERROR_NOT_ENOUGH_MEMORY; @@ -684,8 +684,16 @@ static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile) CascReadFile(hFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER), &dwBytesRead); if(dwBytesRead == sizeof(CASC_ENCODING_HEADER)) { - dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.NumSegments); - dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.SegmentsPos); + // Check the version and sizes + if(EncodingHeader.Version != 0x01 || EncodingHeader.ChecksumSizeA != MD5_HASH_SIZE || EncodingHeader.ChecksumSizeB != MD5_HASH_SIZE) + { + assert(false); + return NULL; + } + + // Get the number of segments + dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.Entries_TableA); + dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.Size_StringTable1); if(EncodingHeader.Magic[0] == 'E' && EncodingHeader.Magic[1] == 'N' && dwSegmentPos != 0 && dwNumSegments != 0) nError = ERROR_SUCCESS; } @@ -721,36 +729,16 @@ static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile) static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile) { - TCascFile * hf; LPBYTE pbRootFile = NULL; DWORD cbRootFile = 0; DWORD dwBytesRead = 0; - BYTE StartOfFile[0x10]; int nError = ERROR_SUCCESS; - // Dummy read the first 16 bytes - CascReadFile(hFile, &StartOfFile, sizeof(StartOfFile), &dwBytesRead); - if(dwBytesRead != sizeof(StartOfFile)) + // Retrieve the size of the ROOT file + cbRootFile = CascGetFileSize(hFile, NULL); + if(cbRootFile == 0) nError = ERROR_BAD_FORMAT; - // Calculate and allocate space for the entire file - if(nError == ERROR_SUCCESS) - { - // Convert the file handle to pointer to TCascFile - hf = IsValidFileHandle(hFile); - if(hf != NULL) - { - // Parse the frames to get the file size - for(DWORD i = 0; i < hf->FrameCount; i++) - { - cbRootFile += hf->pFrames[i].FrameSize; - } - } - - // Evaluate the error - nError = (cbRootFile != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; - } - // Allocate space for the entire file if(nError == ERROR_SUCCESS) { @@ -762,12 +750,9 @@ static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile) // If all went OK, we load the entire file to memory if(nError == ERROR_SUCCESS) { - // Copy the header itself - memcpy(pbRootFile, StartOfFile, sizeof(StartOfFile)); - - // Read the rest of the data - CascReadFile(hFile, pbRootFile + sizeof(StartOfFile), cbRootFile - sizeof(StartOfFile), &dwBytesRead); - if(dwBytesRead != (cbRootFile - sizeof(StartOfFile))) + // Read the entire file to memory + CascReadFile(hFile, pbRootFile, cbRootFile, &dwBytesRead); + if(dwBytesRead != cbRootFile) nError = ERROR_FILE_CORRUPT; } @@ -780,17 +765,19 @@ static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile) static int LoadEncodingFile(TCascStorage * hs) { PFILE_ENCODING_SEGMENT pEncodingSegment; - PCASC_ENCODING_ENTRY pEncodingEntry; + QUERY_KEY EncodingKey; LPBYTE pbStartOfSegment; LPBYTE pbEncodingFile = NULL; HANDLE hFile = NULL; DWORD cbEncodingFile = 0; - DWORD dwNumberOfSegments = 0; + DWORD dwNumSegments = 0; DWORD dwSegmentsPos = 0; int nError = ERROR_SUCCESS; // Open the encoding file - if(!CascOpenFileByIndexKey((HANDLE)hs, &hs->EncodingEKey, 0, &hFile)) + EncodingKey.pbData = hs->EncodingKey.pbData + MD5_HASH_SIZE; + EncodingKey.cbData = MD5_HASH_SIZE; + if(!CascOpenFileByIndexKey((HANDLE)hs, &EncodingKey, 0, &hFile)) nError = GetLastError(); // Load the entire ENCODING file to memory @@ -808,20 +795,25 @@ static int LoadEncodingFile(TCascStorage * hs) // Verify all encoding segments if(nError == ERROR_SUCCESS) { - // Save the encoding header - hs->pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile; + PCASC_ENCODING_HEADER pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile; // Convert size and offset - dwNumberOfSegments = ConvertBytesToInteger_4(hs->pEncodingHeader->NumSegments); - dwSegmentsPos = ConvertBytesToInteger_4(hs->pEncodingHeader->SegmentsPos); + dwNumSegments = ConvertBytesToInteger_4(pEncodingHeader->Entries_TableA); + dwSegmentsPos = ConvertBytesToInteger_4(pEncodingHeader->Size_StringTable1); + + // Store the encoding file to the CASC storage + hs->EncodingFile.pbData = pbEncodingFile; + hs->EncodingFile.cbData = cbEncodingFile; // Allocate the array of encoding segments pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos); - pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments); + pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments); // Go through all encoding segments and verify them - for(DWORD i = 0; i < dwNumberOfSegments; i++) + for(DWORD i = 0; i < dwNumSegments; i++) { + PCASC_ENCODING_ENTRY pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment; + // Check if there is enough space in the buffer if((pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE) > (pbEncodingFile + cbEncodingFile)) { @@ -837,8 +829,7 @@ static int LoadEncodingFile(TCascStorage * hs) // break; // } - // Check if the encoding key matches - pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment; + // Check if the encoding key matches with the expected first value if(memcmp(pEncodingEntry->EncodingKey, pEncodingSegment->FirstEncodingKey, MD5_HASH_SIZE)) { nError = ERROR_FILE_CORRUPT; @@ -856,239 +847,10 @@ static int LoadEncodingFile(TCascStorage * hs) if(nError == ERROR_SUCCESS) { pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos); - nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumberOfSegments); - } - return nError; -} - -typedef struct _CHECK_ROOT_ENTRY_INPUT -{ - ULONGLONG FileNameHash; - DWORD SumValue; - DWORD EncodingKey[4]; - -} CHECK_ROOT_ENTRY_INPUT, *PCHECK_ROOT_ENTRY_INPUT; - -typedef struct _CHECK_ROOT_ENTRY_OUTPUT -{ - DWORD field_0; - DWORD field_4; - DWORD field_8; - bool field_C; - -} CHECK_ROOT_ENTRY_OUTPUT, *PCHECK_ROOT_ENTRY_OUTPUT; - - -// WoW6: 00413F61 -static bool EnlargeHashTableIfMoreThan75PercentUsed(PCASC_ROOT_HASH_TABLE pRootTable, DWORD NewItemCount) -{ - // Don't relocate anything, just check - assert((double)NewItemCount / (double)pRootTable->TableSize < .75); - return true; -} - -// WOW6: 00414402 -// Finds an existing root table entry or a free one -PCASC_ROOT_ENTRY CascRootTable_FindFreeEntryWithEnlarge( - PCASC_ROOT_HASH_TABLE pRootTable, - PCASC_ROOT_ENTRY pNewEntry) -{ - PCASC_ROOT_ENTRY pEntry; - DWORD TableIndex; - - // The table size must be a power of two - assert((pRootTable->TableSize & (pRootTable->TableSize - 1)) == 0); - - // Make sure that number of occupied items is never bigger - // than 75% of the table size - if(!EnlargeHashTableIfMoreThan75PercentUsed(pRootTable, pRootTable->ItemCount + 1)) - return NULL; - - // Get the start index of the table - TableIndex = (DWORD)(pNewEntry->FileNameHash) & (pRootTable->TableSize - 1); - - // If that entry is already occupied, move to a next entry - for(;;) - { - // Check that entry if it's free or not - pEntry = pRootTable->TablePtr + TableIndex; - if(pEntry->SumValue == 0) - break; - - // Is the found entry equal to the existing one? - if(pEntry->FileNameHash == pNewEntry->FileNameHash) - break; - - // Move to the next entry - TableIndex = (TableIndex + 1) & (pRootTable->TableSize - 1); - } - - // Either return a free entry or an existing one - return pEntry; -} - -// WOW6: 004145D1 -static void CascRootTable_InsertTableEntry( - PCASC_ROOT_HASH_TABLE pRootTable, - PCASC_ROOT_ENTRY pNewEntry) -{ - PCASC_ROOT_ENTRY pEntry; - - // Find an existing entry or an empty one - pEntry = CascRootTable_FindFreeEntryWithEnlarge(pRootTable, pNewEntry); - assert(pEntry != NULL); - - // If that entry is not used yet, fill it in - if(pEntry->FileNameHash == 0) - { - *pEntry = *pNewEntry; - pRootTable->ItemCount++; + nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumSegments); } -} - -static int LoadWowRootFileLocales( - TCascStorage * hs, - LPBYTE pbRootFile, - DWORD cbRootFile, - DWORD dwLocaleMask, - bool bLoadBlocksWithFlags80, - BYTE HighestBitValue) -{ - CASC_ROOT_ENTRY NewRootEntry; - ROOT_BLOCK_INFO BlockInfo; - LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; - LPBYTE pbFilePointer; - - // Now parse the root file - for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; ) - { - // Validate the file locale block - pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd); - if(pbFilePointer == NULL) - break; - - // WoW.exe (build 19116): Entries with flag 0x100 set are skipped - if(BlockInfo.pLocaleBlockHdr->Flags & 0x100) - continue; - - // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if arg_4 is set to FALSE (which is by default) - if(bLoadBlocksWithFlags80 == 0 && (BlockInfo.pLocaleBlockHdr->Flags & 0x80)) - continue; - // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to arg_8 are skipped - if((BYTE)(BlockInfo.pLocaleBlockHdr->Flags >> 0x1F) != HighestBitValue) - continue; - - // WoW.exe (build 19116): Locales other than defined mask are skipped too - if((BlockInfo.pLocaleBlockHdr->Locales & dwLocaleMask) == 0) - continue; - - // Reset the sum value - NewRootEntry.SumValue = 0; - - // WoW.exe (build 19116): Blocks with zero files are skipped - for(DWORD i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++) - { - // (004147A3) Prepare the CASC_ROOT_ENTRY structure - NewRootEntry.FileNameHash = BlockInfo.pRootEntries[i].FileNameHash; - NewRootEntry.SumValue = NewRootEntry.SumValue + BlockInfo.pInt32Array[i]; - NewRootEntry.Locales = BlockInfo.pLocaleBlockHdr->Locales; - NewRootEntry.EncodingKey[0] = BlockInfo.pRootEntries[i].EncodingKey[0]; - NewRootEntry.EncodingKey[1] = BlockInfo.pRootEntries[i].EncodingKey[1]; - NewRootEntry.EncodingKey[2] = BlockInfo.pRootEntries[i].EncodingKey[2]; - NewRootEntry.EncodingKey[3] = BlockInfo.pRootEntries[i].EncodingKey[3]; - - // Insert the root table item to the hash table - CascRootTable_InsertTableEntry(&hs->RootTable, &NewRootEntry); - NewRootEntry.SumValue++; - } - } - - return 1; -} - -// WoW.exe: 004146C7 (BuildManifest::Load) -static int LoadWowRootFileWithParams( - TCascStorage * hs, - LPBYTE pbRootFile, - DWORD cbRootFile, - DWORD dwLocaleBits, - BYTE HighestBitValue) -{ - // Load the locale as-is - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, dwLocaleBits, false, HighestBitValue); - - // If we wanted enGB, we also load enUS for the missing files - if(dwLocaleBits == CASC_LOCALE_ENGB) - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue); - - if(dwLocaleBits == CASC_LOCALE_PTPT) - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue); - - return ERROR_SUCCESS; -} - -/* - // Code from WoW.exe - if(dwLocaleBits == CASC_LOCALE_DUAL_LANG) - { - // Is this english version of WoW? - if(arg_4 == CASC_LOCALE_BIT_ENUS) - { - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, false, HighestBitValue); - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue); - return ERROR_SUCCESS; - } - - // Is this portuguese version of WoW? - if(arg_4 == CASC_LOCALE_BIT_PTBR) - { - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, false, HighestBitValue); - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue); - } - } - - LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << arg_4), false, HighestBitValue); -*/ - -static int LoadWowRootFile( - TCascStorage * hs, - LPBYTE pbRootFile, - DWORD cbRootFile, - DWORD dwLocaleMask) -{ - int nError; - - // Dump the root file, if needed -#ifdef CASC_DUMP_ROOT_FILE - //CascDumpRootFile(hs, - // pbRootFile, - // cbRootFile, - // "\\casc_root_%build%.txt", - // _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"), - // CASC_DUMP_ROOT_FILE); -#endif - - // Allocate root table entries. Note that the initial size - // of the root table is set to 0x00200000 by World of Warcraft 6.x - hs->RootTable.TablePtr = CASC_ALLOC(CASC_ROOT_ENTRY, CASC_INITIAL_ROOT_TABLE_SIZE); - hs->RootTable.TableSize = CASC_INITIAL_ROOT_TABLE_SIZE; - if(hs->RootTable.TablePtr == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Clear the entire table - memset(hs->RootTable.TablePtr, 0, CASC_INITIAL_ROOT_TABLE_SIZE * sizeof(CASC_ROOT_ENTRY)); - - // Load the root file - nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 0); - if(nError != ERROR_SUCCESS) - return nError; - - nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 1); - if(nError != ERROR_SUCCESS) - return nError; - - return ERROR_SUCCESS; + return nError; } static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask) @@ -1100,49 +862,70 @@ static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask) int nError = ERROR_SUCCESS; // Sanity checks - assert(hs->RootTable.TablePtr == NULL); - assert(hs->RootTable.ItemCount == 0); - assert(hs->ppEncodingEntries != NULL); + assert(hs->pEncodingMap != NULL); + assert(hs->pRootHandler == NULL); // Locale: The default parameter is 0 - in that case, // we assign the default locale, loaded from the .build.info file if(dwLocaleMask == 0) dwLocaleMask = hs->dwDefaultLocale; - // The root file is either MNDX file (Heroes of the Storm) - // or a file containing an array of root entries (World of Warcraft 6.0+) - // Note: The "root" key file's MD5 hash is equal to its name - // in the configuration + // Load the entire ROOT file to memory if(!CascOpenFileByEncodingKey((HANDLE)hs, &hs->RootKey, 0, &hFile)) nError = GetLastError(); - // Load the entire ROOT file to memory + // Load the entire file to memory if(nError == ERROR_SUCCESS) { - // Load the necessary part of the ENCODING file to memory pbRootFile = LoadRootFileToMemory(hFile, &cbRootFile); - if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK)) - nError = ERROR_FILE_CORRUPT; - - // Close the encoding file CascCloseFile(hFile); } - // Check if the file is a MNDX file - if(nError == ERROR_SUCCESS) + // Check if the version of the ROOT file + if(nError == ERROR_SUCCESS && pbRootFile != NULL) { FileSignature = (PDWORD)pbRootFile; - if(FileSignature[0] == CASC_MNDX_SIGNATURE) + switch(FileSignature[0]) { - nError = LoadMndxRootFile(hs, pbRootFile, cbRootFile); - } - else - { - // WOW6: 00415000 - nError = LoadWowRootFile(hs, pbRootFile, cbRootFile, dwLocaleMask); + case CASC_MNDX_ROOT_SIGNATURE: + nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile); + break; + + case CASC_DIABLO3_ROOT_SIGNATURE: + nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile); + break; + + case CASC_OVERWATCH_ROOT_SIGNATURE: + nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile); + break; + + default: + nError = RootHandler_CreateWoW6(hs, pbRootFile, cbRootFile, dwLocaleMask); + break; } } + // Insert entry for the + if(nError == ERROR_SUCCESS) + { + InsertExtraFile(hs, "ENCODING", &hs->EncodingKey); + InsertExtraFile(hs, "ROOT", &hs->RootKey); + InsertExtraFile(hs, "DOWNLOAD", &hs->DownloadKey); + InsertExtraFile(hs, "INSTALL", &hs->InstallKey); + } + +#ifdef _DEBUG + if(nError == ERROR_SUCCESS) + { + //RootFile_Dump(hs, + // pbRootFile, + // cbRootFile, + // _T("\\casc_root_%build%.txt"), + // _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"), + // DUMP_LEVEL_INDEX_ENTRIES); + } +#endif + // Free the root file CASC_FREE(pbRootFile); return nError; @@ -1154,19 +937,19 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs) if(hs != NULL) { - // Free the MNDX info - if(hs->pPackages != NULL) - CASC_FREE(hs->pPackages); - if(hs->pMndxInfo != NULL) - FreeMndxInfo(hs->pMndxInfo); + // Free the root handler + if(hs->pRootHandler != NULL) + RootHandler_Close(hs->pRootHandler); + hs->pRootHandler = NULL; + + // Free the extra encoding entries + Array_Free(&hs->ExtraEntries); // Free the pointers to file entries - if(hs->RootTable.TablePtr != NULL) - CASC_FREE(hs->RootTable.TablePtr); - if(hs->ppEncodingEntries != NULL) - CASC_FREE(hs->ppEncodingEntries); - if(hs->pEncodingHeader != NULL) - CASC_FREE(hs->pEncodingHeader); + if(hs->pEncodingMap != NULL) + Map_Free(hs->pEncodingMap); + if(hs->EncodingFile.pbData != NULL) + CASC_FREE(hs->EncodingFile.pbData); if(hs->pIndexEntryMap != NULL) Map_Free(hs->pIndexEntryMap); @@ -1195,25 +978,24 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs) CASC_FREE(hs->szRootPath); if(hs->szDataPath != NULL) CASC_FREE(hs->szDataPath); + if(hs->szBuildFile != NULL) + CASC_FREE(hs->szBuildFile); if(hs->szIndexPath != NULL) CASC_FREE(hs->szIndexPath); if(hs->szUrlPath != NULL) CASC_FREE(hs->szUrlPath); - // Fre the blob arrays - QUERY_KEY_FreeArray(hs->pArchiveArray); - QUERY_KEY_FreeArray(hs->pPatchArchiveArray); - QUERY_KEY_FreeArray(hs->pEncodingKeys); - // Free the blobs - QUERY_KEY_Free(&hs->CdnConfigKey); - QUERY_KEY_Free(&hs->CdnBuildKey); - QUERY_KEY_Free(&hs->ArchiveGroup); - QUERY_KEY_Free(&hs->PatchArchiveGroup); - QUERY_KEY_Free(&hs->RootKey); - QUERY_KEY_Free(&hs->PatchKey); - QUERY_KEY_Free(&hs->DownloadKey); - QUERY_KEY_Free(&hs->InstallKey); + FreeCascBlob(&hs->CdnConfigKey); + FreeCascBlob(&hs->CdnBuildKey); + FreeCascBlob(&hs->ArchivesGroup); + FreeCascBlob(&hs->ArchivesKey); + FreeCascBlob(&hs->PatchArchivesKey); + FreeCascBlob(&hs->RootKey); + FreeCascBlob(&hs->PatchKey); + FreeCascBlob(&hs->DownloadKey); + FreeCascBlob(&hs->InstallKey); + FreeCascBlob(&hs->EncodingKey); // Free the storage structure hs->szClassName = NULL; @@ -1266,6 +1048,13 @@ bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE nError = LoadEncodingFile(hs); } + // Initialize the dynamic array for extra files + // Reserve space for 0x20 encoding entries + if(nError == ERROR_SUCCESS) + { + nError = Array_Create(&hs->ExtraEntries, CASC_ENCODING_ENTRY_1, CASC_EXTRA_FILES); + } + // Load the index files if(nError == ERROR_SUCCESS) { @@ -1309,8 +1098,7 @@ bool WINAPI CascGetStorageInfo( break; case CascStorageFeatures: - if(hs->pMndxInfo != NULL) - dwInfoValue |= CASC_FEATURE_LISTFILE; + dwInfoValue |= (hs->pRootHandler->dwRootFlags & ROOT_FLAG_HAS_NAMES) ? CASC_FEATURE_LISTFILE : 0; break; case CascStorageGameInfo: @@ -1342,8 +1130,6 @@ bool WINAPI CascGetStorageInfo( return true; } - - bool WINAPI CascCloseStorage(HANDLE hStorage) { TCascStorage * hs; diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h index 3bf1efde4b8..5d0190e07cc 100644 --- a/dep/CascLib/src/CascPort.h +++ b/dep/CascLib/src/CascPort.h @@ -176,6 +176,7 @@ #define _tcsrchr strrchr #define _tcsstr strstr #define _tcsspn strspn + #define _tcsncmp strncmp #define _tprintf printf #define _stprintf sprintf #define _tremove remove diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp index 83fdd0d2096..72eb2c4b647 100644 --- a/dep/CascLib/src/CascReadFile.cpp +++ b/dep/CascLib/src/CascReadFile.cpp @@ -98,9 +98,10 @@ static int LoadFileFrames(TCascFile * hf) else nError = GetLastError(); - // Note: Do not take the FileSize from the sum of frames. - // This value is invalid when loading the ENCODING file. -// hf->FileSize = FileSize; + // Note: on ENCODING file, this value is almost always bigger + // then the real size of ENCODING. We handle this problem + // by calculating size of the ENCODIG file from its header. + hf->FileSize = FileSize; #ifdef CASCLIB_TEST hf->FileSize_FrameSum = FileSize; @@ -264,6 +265,85 @@ static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer) return NULL; } +static int ProcessFileFrame( + LPBYTE pbOutBuffer, + DWORD cbOutBuffer, + LPBYTE pbInBuffer, + DWORD cbInBuffer, + DWORD dwFrameIndex) +{ + LPBYTE pbTempBuffer; + LPBYTE pbWorkBuffer; + DWORD cbTempBuffer = CASCLIB_MAX(cbInBuffer, cbOutBuffer); + DWORD cbWorkBuffer = cbOutBuffer + 1; + DWORD dwStepCount = 0; + bool bWorkComplete = false; + int nError = ERROR_SUCCESS; + + // Allocate the temporary buffer that will serve as output + pbWorkBuffer = pbTempBuffer = CASC_ALLOC(BYTE, cbTempBuffer); + if(pbWorkBuffer == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Perform the loop + for(;;) + { + // Set the output buffer. + // Even operations: extract to temporary buffer + // Odd operations: extract to output buffer + pbWorkBuffer = (dwStepCount & 0x01) ? pbOutBuffer : pbTempBuffer; + cbWorkBuffer = (dwStepCount & 0x01) ? cbOutBuffer : cbTempBuffer; + + // Perform the operation specific to the operation ID + switch(pbInBuffer[0]) + { + case 'E': // Encrypted files + nError = CascDecrypt(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex); + bWorkComplete = (nError != ERROR_SUCCESS); + break; + + case 'Z': // ZLIB compressed files + nError = CascDecompress(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1); + bWorkComplete = true; + break; + + case 'N': // Normal stored files + nError = CascDirectCopy(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1); + bWorkComplete = true; + break; + + case 'F': // Recursive frames - not supported + default: // Unrecognized - if we unpacked something, we consider it done + nError = ERROR_NOT_SUPPORTED; + bWorkComplete = true; + assert(false); + break; + } + + // Are we done? + if(bWorkComplete) + break; + + // Set the input buffer to the work buffer + pbInBuffer = pbWorkBuffer; + cbInBuffer = cbWorkBuffer; + dwStepCount++; + } + + // If the data are currently in the temporary buffer, + // we need to copy them to output buffer + if(nError == ERROR_SUCCESS && pbWorkBuffer != pbOutBuffer) + { + if(cbWorkBuffer != cbOutBuffer) + nError = ERROR_INSUFFICIENT_BUFFER; + memcpy(pbOutBuffer, pbWorkBuffer, cbOutBuffer); + } + + // Free the temporary buffer + CASC_FREE(pbTempBuffer); + return nError; +} + //----------------------------------------------------------------------------- // Public functions @@ -299,7 +379,7 @@ DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh) } // Make sure that the file header area is loaded - nError = EnsureHeaderAreaIsLoaded(hf); + nError = EnsureFrameHeadersLoaded(hf); if(nError != ERROR_SUCCESS) { SetLastError(nError); @@ -387,7 +467,6 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW DWORD dwFilePointer = 0; DWORD dwEndPointer = 0; DWORD dwFrameSize; - DWORD cbOutBuffer; bool bReadResult; int nError = ERROR_SUCCESS; @@ -423,7 +502,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW { // Get the frame pFrame = FindFileFrame(hf, hf->FilePointer); - if(pFrame == NULL) + if(pFrame == NULL || pFrame->CompressedSize < 1) nError = ERROR_FILE_CORRUPT; } @@ -439,7 +518,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW // Perform block read from each file frame while(dwFilePointer < dwEndPointer) { - LPBYTE pbRawData = NULL; + LPBYTE pbFrameData = NULL; DWORD dwFrameStart = pFrame->FrameFileOffset; DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize; @@ -457,8 +536,8 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW } // We also need to allocate buffer for the raw data - pbRawData = CASC_ALLOC(BYTE, pFrame->CompressedSize); - if(pbRawData == NULL) + pbFrameData = CASC_ALLOC(BYTE, pFrame->CompressedSize); + if(pbFrameData == NULL) { nError = ERROR_NOT_ENOUGH_MEMORY; break; @@ -466,7 +545,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW // Load the raw file data to memory FileOffset = pFrame->FrameArchiveOffset; - bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbRawData, pFrame->CompressedSize); + bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbFrameData, pFrame->CompressedSize); // Note: The raw file data size could be less than expected // Happened in WoW build 19342 with the ROOT file. MD5 in the frame header @@ -484,43 +563,34 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW // If the frame offset is before EOF and frame end is beyond EOF, correct it if(FileOffset < StreamSize && dwFrameSize < pFrame->CompressedSize) { - memset(pbRawData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize)); + memset(pbFrameData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize)); bReadResult = true; } } // If the read result failed, we cannot finish reading it - if(bReadResult == false) - { - CASC_FREE(pbRawData); - nError = GetLastError(); - break; - } - - // Verify the block MD5 - if(!VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5)) + if(bReadResult && VerifyDataBlockHash(pbFrameData, pFrame->CompressedSize, pFrame->md5)) { - CASC_FREE(pbRawData); - nError = ERROR_FILE_CORRUPT; - break; + // Convert the source frame to the file cache + nError = ProcessFileFrame(hf->pbFileCache, + pFrame->FrameSize, + pbFrameData, + pFrame->CompressedSize, + (DWORD)(pFrame - hf->pFrames)); + if(nError == ERROR_SUCCESS) + { + // Set the start and end of the cache + hf->CacheStart = dwFrameStart; + hf->CacheEnd = dwFrameEnd; + } } - - // Decompress the file frame - cbOutBuffer = pFrame->FrameSize; - nError = CascDecompress(hf->pbFileCache, &cbOutBuffer, pbRawData, pFrame->CompressedSize); - if(nError != ERROR_SUCCESS || cbOutBuffer != pFrame->FrameSize) + else { - CASC_FREE(pbRawData); nError = ERROR_FILE_CORRUPT; - break; } - // Set the start and end of the cache - hf->CacheStart = dwFrameStart; - hf->CacheEnd = dwFrameEnd; - - // Free the decompress buffer, if needed - CASC_FREE(pbRawData); + // Free the raw frame data + CASC_FREE(pbFrameData); } // Copy the decompressed data diff --git a/dep/CascLib/src/CascRootFile_Diablo3.cpp b/dep/CascLib/src/CascRootFile_Diablo3.cpp new file mode 100644 index 00000000000..98a42cc3226 --- /dev/null +++ b/dep/CascLib/src/CascRootFile_Diablo3.cpp @@ -0,0 +1,1189 @@ +/*****************************************************************************/ +/* CascRootFile_Diablo3.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Support for loading Diablo 3 ROOT file */ +/* Note: D3 offsets refer to Diablo III.exe 2.2.0.30013 (32-bit) */ +/* SHA1: e4f17eca8aad8dde70870bf932ac3f5b85f17a1f */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 04.03.15 1.00 Lad The first version of CascRootFile_Diablo3.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +#define DIABLO3_SUBDIR_SIGNATURE 0xEAF1FE87 +#define DIABLO3_PACKAGES_SIGNATURE 0xAABB0002 +#define DIABLO3_MAX_SUBDIRS 0x20 + +#define DIABLO3_INVALID_INDEX 0xFFFFFFFF +#define DIABLO3_INVALID_FILE 0xFFFFFFFF +#define DIABLO3_MAX_ASSETS 70 // Maximum possible number of assets +#define DIABLO3_MAX_LEVEL0_LENGTH 0x10 // Maximum length of the level-0 directory name + +#define INVALID_FILE_INDEX 0xFFFFFFFF +#define INVALID_ASSET_INDEX 0xFF + +#define ENTRY_FLAG_DIRECTORY_ENTRY 0x80 // The file is actually a directory entry +#define ENTRY_FLAG_PLAIN_NAME 0x01 // If set, the file entry contains offset of the plain file name +#define ENTRY_FLAG_FULL_NAME 0x02 // If set, the file entry contains offset of the full name +#define ENTRY_FLAG_FLAGS_MASK 0xF0 // Mask for the entry flags +#define ENTRY_FLAG_NAME_MASK 0x0F // Mask for the entry file name type + +// Values for CASC_FILE_ENTRY::dwFlags +#define CASC_ENTRY_SHORT_NAME 0x000000001 // If set, the name is in format XXYYplain-name[\sub-index].ext +#define CASC_ENTRY_HAS_SUBINDEX 0x000000002 // If set, the subitem is present in the file name (i.e. XXYYplain-name\sub-index.ext) + +#define SEARCH_PHASE_NAMES 0 // Searching named entry +#define SEARCH_PHASE_FILE_IDS 1 // Searching filed by ID + +// Macro for constructing 64-bit integer from root-index, file-index and sub-index +// The result value is RRAAAAAAAASSSSSS +#define MAKE_INDEX64(ri, fi, si) (((ULONGLONG)ri << 0x38) | ((ULONGLONG)fi << 0x18) | ((ULONGLONG)si)) +#define INDEX64_ROOT_INDEX(hash) (DWORD)((hash >> 0x38) & 0x000000FF) +#define INDEX64_FILE_INDEX(hash) (DWORD)((hash >> 0x18) & 0xFFFFFFFF) +#define INDEX64_SUB_INDEX(hash) (DWORD)((hash >> 0x00) & 0x00FFFFFF) + +// On-disk structure for a file given by file number +typedef struct _DIABLO3_FILEID1_ENTRY +{ + ENCODING_KEY EncodingKey; // Encoding key for the file + DWORD FileIndex; // File index +} DIABLO3_FILEID1_ENTRY, *PDIABLO3_FILEID1_ENTRY; + +// On-disk structure for a file given by file number and suffix +typedef struct _DIABLO3_FILEID2_ENTRY +{ + ENCODING_KEY EncodingKey; // Encoding key for the file + DWORD FileIndex; // File index + DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp" +} DIABLO3_FILEID2_ENTRY, *PDIABLO3_FILEID2_ENTRY; + +// On-disk structure of the named entry +typedef struct _DIABLO3_NAMED_ENTRY +{ + ENCODING_KEY EncodingKey; // Encoding key for the file + BYTE szFileName[1]; // ASCIIZ file name (variable length) +} DIABLO3_NAMED_ENTRY, *PDIABLO3_NAMED_ENTRY; + +// On-disk structure of CoreToc.dat header +typedef struct _DIABLO3_CORE_TOC_HEADER +{ + DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset (level-1 directory) + DWORD EntryOffsets[DIABLO3_MAX_ASSETS]; // Array of offsets of each DIABLO3_CORE_TOC_ENTRY, relative to data after header + DWORD Unknowns[DIABLO3_MAX_ASSETS]; // Unknown + DWORD Alignment; +} DIABLO3_CORE_TOC_HEADER, *PDIABLO3_CORE_TOC_HEADER; + +// On-disk structure of the entry in CoreToc.dat +typedef struct _DIABLO3_CORE_TOC_ENTRY +{ + DWORD AssetIndex; // Index of the Diablo3 asset (aka directory) + DWORD FileIndex; // File index + DWORD NameOffset; // Offset of the plain file name + +} DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY; + +// In-memory structure of parsed directory header +typedef struct _DIABLO3_DIR_HEADER +{ + LPBYTE pbEntries1; + LPBYTE pbEntries2; + LPBYTE pbEntries3; + DWORD dwEntries1; + DWORD dwEntries2; + DWORD dwEntries3; +} DIABLO3_DIR_HEADER, *PDIABLO3_DIR_HEADER; + +// In-memory structure of loaded CoreTOC.dat +typedef struct _DIABLO3_CORE_TOC +{ + DIABLO3_CORE_TOC_HEADER Hdr; // Header of CoreTOC.dat + + LPBYTE pbCoreToc; // Content of the CoreTOC.dat file + DIABLO3_CORE_TOC_ENTRY Entries[1]; // Buffer for storing the entries (variable length) + +} DIABLO3_CORE_TOC, *PDIABLO3_CORE_TOC; + +// On-disk structure of Packages.dat header +typedef struct _DIABLO3_PACKAGES_DAT_HEADER +{ + DWORD Signature; + DWORD NumberOfNames; +} DIABLO3_PACKAGES_DAT_HEADER, *PDIABLO3_PACKAGES_DAT_HEADER; + +// Structure for conversion DirectoryID -> Directory name +typedef struct _DIABLO3_ASSET_INFO +{ + const char * szDirectoryName; // Directory name + const char * szExtension; + +} DIABLO3_ASSET_INFO; +typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO; + +// In-memory structure of a file entry in the linear file list +typedef struct _CASC_FILE_ENTRY +{ + ENCODING_KEY EncodingKey; // Encoding key + ULONGLONG FileNameHash; // Hash of the full file name + DWORD dwFileName; // Offset of the name (in name's dynamic array) + DWORD dwFlags; // Entry flags (see CASC_ENTRY_XXXX) + + DWORD NameOffset; // Offset of the name (in name's dynamic array) + USHORT SubIndex; // File\SubFile index + BYTE AssetIndex; // Asset index (aka directory index) + BYTE EntryFlags; // Entry flags +} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY; + +//----------------------------------------------------------------------------- +// Structure definitions for Diablo3 root file + +struct TRootHandler_Diablo3 : public TRootHandler +{ + // Linear global list of all files + DYNAMIC_ARRAY FileTable; + + // Linear global list of names + DYNAMIC_ARRAY FileNames; + + // Global map of FileName -> FileEntry + PCASC_MAP pRootMap; +}; + +//----------------------------------------------------------------------------- +// Local variables + +static const DIABLO3_ASSET_INFO Assets[] = +{ +// DIR-NAME EXTENSION +// ========== ========= + {NULL, NULL}, // 0x00 + {"Actor", "acr"}, // 0x01 + {"Adventure", "adv"}, // 0x02 + {NULL, NULL}, // 0x03 + {NULL, NULL}, // 0x04 + {"AmbientSound", "ams"}, // 0x05 + {"Anim", "ani"}, // 0x06 + {"Anim2D", "an2"}, // 0x07 + {"AnimSet", "ans"}, // 0x08 + {"Appearance", "app"}, // 0x09 + {NULL, NULL}, // 0x0A + {"Cloth", "clt"}, // 0x0B + {"Conversation", "cnv"}, // 0x0C + {NULL, NULL}, // 0x0D + {"EffectGroup", "efg"}, // 0x0E + {"Encounter", "enc"}, // 0x0F + {NULL, NULL}, // 0x10 + {"Explosion", "xpl"}, // 0x11 + {NULL, NULL}, // 0x12 + {"Font", "fnt"}, // 0x13 + {"GameBalance", "gam"}, // 0x14 + {"Globals", "glo"}, // 0x15 + {"LevelArea", "lvl"}, // 0x16 + {"Light", "lit"}, // 0x17 + {"MarkerSet", "mrk"}, // 0x18 + {"Monster", "mon"}, // 0x19 + {"Observer", "obs"}, // 0x1A + {"Particle", "prt"}, // 0x1B + {"Physics", "phy"}, // 0x1C + {"Power", "pow"}, // 0x1D + {NULL, NULL}, // 0x1E + {"Quest", "qst"}, // 0x1F + {"Rope", "rop"}, // 0x20 + {"Scene", "scn"}, // 0x21 + {"SceneGroup", "scg"}, // 0x22 + {NULL, NULL}, // 0x23 + {"ShaderMap", "shm"}, // 0x24 + {"Shaders", "shd"}, // 0x25 + {"Shakes", "shk"}, // 0x26 + {"SkillKit", "skl"}, // 0x27 + {"Sound", "snd"}, // 0x28 + {"SoundBank", "sbk"}, // 0x29 + {"StringList", "stl"}, // 0x2A + {"Surface", "srf"}, // 0x2B + {"Textures", "tex"}, // 0x2C + {"Trail", "trl"}, // 0x2D + {"UI", "ui"}, // 0x2E + {"Weather", "wth"}, // 0x2F + {"Worlds", "wrl"}, // 0x30 + {"Recipe", "rcp"}, // 0x31 + {NULL, NULL}, // 0x32 + {"Condition", "cnd"}, // 0x33 + {NULL, NULL}, // 0x34 + {NULL, NULL}, // 0x35 + {NULL, NULL}, // 0x36 + {NULL, NULL}, // 0x37 + {"Act", "act"}, // 0x38 + {"Material", "mat"}, // 0x39 + {"QuestRange", "qsr"}, // 0x3A + {"Lore", "lor"}, // 0x3B + {"Reverb", "rev"}, // 0x3C + {"PhysMesh", "phm"}, // 0x3D + {"Music", "mus"}, // 0x3E + {"Tutorial", "tut"}, // 0x3F + {"BossEncounter", "bos"}, // 0x40 + {NULL, NULL}, // 0x41 + {"Accolade", "aco"}, // 0x42 +}; + +static const DIABLO3_ASSET_INFO UnknownAsset = {"Unknown", "xxx"}; + +#define DIABLO3_ASSET_COUNT (sizeof(Assets) / sizeof(Assets[0])) + +//----------------------------------------------------------------------------- +// Local functions + +static PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex) +{ + if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL) + return &Assets[dwAssetIndex]; + return &UnknownAsset; +} + +static DWORD VerifyNamedFileEntry(LPBYTE pbNamedEntry, LPBYTE pbFileEnd) +{ + LPBYTE pbFileName = ((PDIABLO3_NAMED_ENTRY)pbNamedEntry)->szFileName; + + // Find the end of the name + while(pbFileName < pbFileEnd && pbFileName[0] != 0) + pbFileName++; + + // Did we get past the end of the root file? + if(pbFileName >= pbFileEnd) + return 0; + pbFileName++; + + // Return the length of the structure + return (DWORD)(pbFileName - pbNamedEntry); +} + +static char * FindPackageName( + PCASC_MAP pPackageMap, + const char * szAssetName, + const char * szPlainName) +{ + char szFileName[MAX_PATH+1]; + size_t nLength; + + // Construct the name without extension and find it in the map + nLength = sprintf(szFileName, "%s\\%s", szAssetName, szPlainName); + return (char *)Map_FindString(pPackageMap, szFileName, szFileName + nLength); +} + +static size_t CreateShortName( + PCASC_MAP pPackageMap, + DWORD dwRootIndex, // Level-0-dir: Index of the root subdirectory + DWORD dwAssetIndex, // Level-1-dir: Index of the asset name + const char * szPlainName, // Plain name of the file, without extension + DWORD dwSubIndex, + char * szBuffer) +{ + PDIABLO3_ASSET_INFO pAssetInfo = GetAssetInfo(dwAssetIndex); + const char * szPackageName = NULL; + const char * szFormat; + size_t nLength; + + // Write the level-0 directory index as 2-digit hexa number + assert(dwRootIndex < 0x100); + *szBuffer++ = IntToHexChar[dwRootIndex >> 0x04]; + *szBuffer++ = IntToHexChar[dwRootIndex & 0x0F]; + + // Write the level-1 directory index as 2-digit hexa number + assert(dwAssetIndex < 0x100); + *szBuffer++ = IntToHexChar[dwAssetIndex >> 0x04]; + *szBuffer++ = IntToHexChar[dwAssetIndex & 0x0F]; + + // Construct the file name with ending "." for extension + szFormat = (dwSubIndex != DIABLO3_INVALID_INDEX) ? "%s\\%04u." : "%s."; + nLength = sprintf(szBuffer, szFormat, szPlainName, dwSubIndex); + + // Try to fixup the file extension from the package name. + // File extensions are not predictable because for subitems, + // they are not always equal to the main items: + // + // SoundBank\3D Ambience.sbk + // SoundBank\3D Ambience\0000.smp + // SoundBank\3D Ambience\0002.smp + // ... + // SoundBank\Angel.sbk + // SoundBank\Angel\0000.fsb + // SoundBank\Angel\0002.fsb + // + // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible + // + if(pPackageMap != NULL) + { + // Retrieve the asset name + szPackageName = FindPackageName(pPackageMap, pAssetInfo->szDirectoryName, szBuffer); + if(szPackageName != NULL) + { + strcpy(szBuffer, szPackageName + strlen(pAssetInfo->szDirectoryName) + 1); + nLength = strlen(szBuffer); + } + } + + // If we havent't found the package, we either use the default asset extension or "xxx" + if(szPackageName == NULL) + { + if(dwSubIndex == DIABLO3_INVALID_INDEX) + { + strcpy(szBuffer + nLength, pAssetInfo->szExtension); + nLength += strlen(pAssetInfo->szExtension); + } + else + { + strcpy(szBuffer + nLength, "xxx"); + nLength += 3; + } + } + + // Return the length of the short file name + return nLength + 4; +} + +static size_t CreateFileName( + TRootHandler_Diablo3 * pRootHandler, + const char * szShortName, // Short file name of the file + char * szBuffer) +{ + PCASC_FILE_ENTRY pRootEntry; + const char * szNameLevel0; + const char * szNameLevel1 = NULL; + DWORD dwRootIndex0 = 0; + DWORD dwAssetIndex = 0; + + // Retrieve the level-0 and level-1 directory indexes + ConvertStringToInt08(szShortName+0, &dwRootIndex0); + ConvertStringToInt08(szShortName+2, &dwAssetIndex); + + // Retrieve the name of the level-0 directory (aka root subdirectory) + pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootIndex0); + szNameLevel0 = (char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName); + + // Retrieve the name of the level-1 directory (aka asset name) + if(dwAssetIndex < DIABLO3_ASSET_COUNT) + szNameLevel1 = Assets[dwAssetIndex].szDirectoryName; + if(szNameLevel1 == NULL) + szNameLevel1 = UnknownAsset.szDirectoryName; + + // Copy the rest of the name as-is + return sprintf(szBuffer, "%s\\%s\\%s", szNameLevel0, szNameLevel1, szShortName + 4); +} + + +// Creates a map of String -> Pointer +static PCASC_MAP CreatePackageMap( + LPBYTE pbPackagesDat, + LPBYTE pbPackagesEnd) +{ + PDIABLO3_PACKAGES_DAT_HEADER pDatHeader = (PDIABLO3_PACKAGES_DAT_HEADER)pbPackagesDat; + PCASC_MAP pPackageMap; + + // Get the header + if((pbPackagesDat + sizeof(DIABLO3_PACKAGES_DAT_HEADER)) >= pbPackagesEnd) + return NULL; + pbPackagesDat += sizeof(DIABLO3_PACKAGES_DAT_HEADER); + + // Check the signature and name count + if(pDatHeader->Signature != DIABLO3_PACKAGES_SIGNATURE) + return NULL; + + // Create the map for fast search of the file name + pPackageMap = Map_Create(pDatHeader->NumberOfNames, KEY_LENGTH_STRING, 0); + if(pPackageMap != NULL) + { + char * szFileName = (char *)pbPackagesDat; + + // Go as long as there is something + for(DWORD i = 0; i < pDatHeader->NumberOfNames; i++) + { + // Get the file extension + if((LPBYTE)szFileName >= pbPackagesEnd) + break; + + // Insert the file name to the map. The file extension is not included + Map_InsertString(pPackageMap, szFileName, true); + szFileName = szFileName + strlen(szFileName) + 1; + } + } + + return pPackageMap; +} + +// Insert an entry with file name as-is +static int InsertFileEntry( + TRootHandler_Diablo3 * pRootHandler, + ENCODING_KEY & EncodingKey, + const char * szFileName, + size_t cchFileName) +{ + PCASC_FILE_ENTRY pFileEntry; + + // We must not allow the file name array to be reallocated. + // Reallocating the array would cause pointers in TRootHandler_Diablo3::pRootMap + // become invalid + if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax) + { + assert(false); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Insert the plain name to the root handler's global name list + szFileName = (const char *)Array_Insert(&pRootHandler->FileNames, szFileName, cchFileName); + if(szFileName == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Make sure that we don't exceed the file limit at this phase + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + assert(pFileEntry != NULL); + + // Store the info into the file entry + pFileEntry->EncodingKey = EncodingKey; + pFileEntry->FileNameHash = CalcFileNameHash(szFileName); + pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szFileName); + pFileEntry->dwFlags = 0; + + // Verify collisions (debug version only) + assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL); + + // Calculate the file name hash + Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash); + + // Success + return ERROR_SUCCESS; +} + +static int ParseDirEntries_FileId1( + TRootHandler_Diablo3 * pRootHandler, + LPBYTE pbFileEntries, + DWORD dwFileEntries, + DWORD dwRootDirIndex) +{ + PDIABLO3_FILEID1_ENTRY pEntry = (PDIABLO3_FILEID1_ENTRY)pbFileEntries; + PCASC_FILE_ENTRY pFileEntry; + + // Overflow test + if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax) + { + assert(false); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Parse the all ID1 entries in the file + for(DWORD i = 0; i < dwFileEntries; i++, pEntry++) + { + // Insert the file entry to the global list + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + assert(pFileEntry != NULL); + + // Fill the index entry + pFileEntry->EncodingKey = pEntry->EncodingKey; + pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, 0); + pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME; + } + + return ERROR_SUCCESS; +} + +static int ParseDirEntries_FileId2( + TRootHandler_Diablo3 * pRootHandler, + LPBYTE pbFileEntries, + DWORD dwFileEntries, + DWORD dwRootDirIndex) +{ + PDIABLO3_FILEID2_ENTRY pEntry = (PDIABLO3_FILEID2_ENTRY)pbFileEntries; + PCASC_FILE_ENTRY pFileEntry; + + // Overflow test + if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax) + { + assert(false); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Parse the all ID1 entries in the file + for(DWORD i = 0; i < dwFileEntries; i++, pEntry++) + { + // Insert the file entry to the global list + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + assert(pFileEntry != NULL); + + // Fill the index entry + pFileEntry->EncodingKey = pEntry->EncodingKey; + pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, pEntry->SubIndex); + pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME | CASC_ENTRY_HAS_SUBINDEX; + } + + return ERROR_SUCCESS; +} + +static int ParseDirEntries_Named( + TRootHandler_Diablo3 * pRootHandler, + LPBYTE pbFileEntries, + LPBYTE pbFileEnd, + DWORD dwFileEntries, + DWORD dwRootDirIndex) +{ + char szFileName[MAX_PATH+1]; + char * szNamePtr = szFileName; + DWORD cbFileEntry; + int nError = ERROR_SUCCESS; + + // Overflow test + if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax) + { + assert(false); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // If we the file is not in the root directory itself, + // prepare the prefix for the root directory. + if(dwRootDirIndex != DIABLO3_INVALID_INDEX) + { + PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootDirIndex); + const char * szRootName = (const char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName); + + // Copy the root directory name + while(szRootName[0] != 0) + *szNamePtr++ = *szRootName++; + + // Append the backslash + *szNamePtr++ = '\\'; + } + + // Parse the file entry + while(pbFileEntries < pbFileEnd) + { + PDIABLO3_NAMED_ENTRY pNamedEntry = (PDIABLO3_NAMED_ENTRY)pbFileEntries; + DWORD cchFileName; + + // Verify the named entry whether it does not go beyond the EOF + cbFileEntry = VerifyNamedFileEntry(pbFileEntries, pbFileEnd); + if(cbFileEntry == 0) + return ERROR_FILE_CORRUPT; + + // Append the file name to the prepared file name + // This way we obtain the full name and the name lookup + // will be fully operational + memcpy(szNamePtr, pNamedEntry->szFileName, (cbFileEntry - sizeof(ENCODING_KEY))); + cchFileName = (DWORD)((szNamePtr - szFileName) + (cbFileEntry - sizeof(ENCODING_KEY))); + + // Insert the named entry to the global file table + nError = InsertFileEntry(pRootHandler, + pNamedEntry->EncodingKey, + szFileName, + cchFileName); + if(nError != ERROR_SUCCESS) + return nError; + + // Move the pointer to the next entry + pbFileEntries += cbFileEntry; + } + + return ERROR_SUCCESS; +} + +static void ResolveFullFileNames( + TRootHandler_Diablo3 * pRootHandler, + PDIABLO3_CORE_TOC_ENTRY pCoreTocEntries, + PCASC_MAP pPackageMap, + LPBYTE pbCoreTocFile, + DWORD dwFileIndexes) +{ + PCASC_FILE_ENTRY pFileEntry; + char * szPlainName; + char * szNamePtr; + size_t nLength; + DWORD dwRootIndex; + DWORD dwFileIndex; + DWORD dwSubIndex; + char szShortName[MAX_PATH+1]; + char szFullName[MAX_PATH+1]; + + // Parse the entire file table + for(size_t i = 0; i < pRootHandler->FileTable.ItemCount; i++) + { + // Retrieve the file entry at n-th position + pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i); + + // Skip the items that already have full name + if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME) + { + // Retrieve the file index of that file + dwRootIndex = INDEX64_ROOT_INDEX(pFileEntry->FileNameHash); + dwFileIndex = INDEX64_FILE_INDEX(pFileEntry->FileNameHash); + dwSubIndex = (pFileEntry->dwFlags & CASC_ENTRY_HAS_SUBINDEX) ? INDEX64_SUB_INDEX(pFileEntry->FileNameHash) : DIABLO3_INVALID_INDEX; + assert(dwFileIndex < dwFileIndexes); + + // Get the plain name of the file + szPlainName = (char *)(pbCoreTocFile + pCoreTocEntries[dwFileIndex].NameOffset); + + // Create the short file name + nLength = CreateShortName(pPackageMap, + dwRootIndex, + pCoreTocEntries[dwFileIndex].AssetIndex, + szPlainName, + dwSubIndex, + szShortName); + + // Insert the short name to the list of the names + szNamePtr = (char *)Array_Insert(&pRootHandler->FileNames, szShortName, nLength + 1); + pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szNamePtr); + + // Create the full file name + nLength = CreateFileName(pRootHandler, szShortName, szFullName); + pFileEntry->FileNameHash = CalcFileNameHash(szFullName); + + // Insert the entry to the name map. Use the mapping of FullName -> FileHash + Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash); + } + } +} + +static LPBYTE LoadFileToMemory(TCascStorage * hs, LPBYTE pbEncodingKey, DWORD * pcbFileData) +{ + QUERY_KEY EncodingKey; + LPBYTE pbFileData = NULL; + HANDLE hFile; + DWORD cbBytesRead = 0; + DWORD cbFileData = 0; + + // Open the file by encoding key + EncodingKey.pbData = pbEncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + if(CascOpenFileByEncodingKey((HANDLE)hs, &EncodingKey, 0, &hFile)) + { + // Retrieve the file size + cbFileData = CascGetFileSize(hFile, NULL); + if(cbFileData > 0) + { + pbFileData = CASC_ALLOC(BYTE, cbFileData); + if(pbFileData != NULL) + { + CascReadFile(hFile, pbFileData, cbFileData, &cbBytesRead); + } + } + + // Close the file + CascCloseFile(hFile); + } + + // Give the file to the caller + if(pcbFileData != NULL) + pcbFileData[0] = cbBytesRead; + return pbFileData; +} + +static LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData) +{ + LPBYTE pbEncodingKey = NULL; + LPBYTE pbFileData = NULL; + + // Try to find encoding key for the file + pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName); + if(pbEncodingKey != NULL) + pbFileData = LoadFileToMemory(hs, pbEncodingKey, pcbFileData); + + return pbFileData; +} + +static int ParseDirectoryHeader( + PDIABLO3_DIR_HEADER pDirHeader, + LPBYTE pbDirFile, + LPBYTE pbFileEnd) +{ + DWORD dwSignature = 0; + + // + // Structure of a Diablo3 directory file + // 1) Signature (4 bytes) + // 2) Number of DIABLO3_FILEID1_ENTRY entries (4 bytes) + // 3) Array of DIABLO3_FILEID1_ENTRY entries + // 4) Number of DIABLO3_FILEID2_ENTRY entries (4 bytes) + // 5) Array of DIABLO3_FILEID2_ENTRY entries + // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes) + // 7) Array of DIABLO3_NAMED_ENTRY entries + // + + // Prepare the header signature + memset(pDirHeader, 0, sizeof(DIABLO3_DIR_HEADER)); + + // Get the signature + if((pbDirFile + sizeof(DWORD)) >= pbFileEnd) + return ERROR_BAD_FORMAT; + dwSignature = *(PDWORD)pbDirFile; + + // Check the signature + if(dwSignature != CASC_DIABLO3_ROOT_SIGNATURE && dwSignature != DIABLO3_SUBDIR_SIGNATURE) + return ERROR_BAD_FORMAT; + pbDirFile += sizeof(DWORD); + + // Subdirectories have extra two arrays + if(dwSignature == DIABLO3_SUBDIR_SIGNATURE) + { + // Get the number of DIABLO3_FILEID1_ENTRY items + if((pbDirFile + sizeof(DWORD)) >= pbFileEnd) + return ERROR_BAD_FORMAT; + pDirHeader->dwEntries1 = *(PDWORD)pbDirFile; + + // Get the array of DIABLO3_FILEID1_ENTRY + pDirHeader->pbEntries1 = (pbDirFile + sizeof(DWORD)); + pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries1 * sizeof(DIABLO3_FILEID1_ENTRY); + + // Get the number of DIABLO3_FILEID2_ENTRY items + if((pbDirFile + sizeof(DWORD)) >= pbFileEnd) + return ERROR_BAD_FORMAT; + pDirHeader->dwEntries2 = *(PDWORD)pbDirFile; + + // Get the array of DIABLO3_FILEID2_ENTRY + pDirHeader->pbEntries2 = (pbDirFile + sizeof(DWORD)); + pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries2 * sizeof(DIABLO3_FILEID2_ENTRY); + } + + // Get the pointer and length DIABLO3_NAMED_ENTRY array + if((pbDirFile + sizeof(DWORD)) >= pbFileEnd) + return ERROR_BAD_FORMAT; + pDirHeader->dwEntries3 = *(PDWORD)pbDirFile; + pDirHeader->pbEntries3 = (pbDirFile + sizeof(DWORD)); + return ERROR_SUCCESS; +} + +static DWORD ScanDirectoryFile( + TCascStorage * hs, + LPBYTE pbRootFile, + LPBYTE pbFileEnd) +{ + PDIABLO3_NAMED_ENTRY pNamedEntry; + DIABLO3_DIR_HEADER RootHeader; + DIABLO3_DIR_HEADER DirHeader; + LPBYTE pbSubDir; + DWORD dwTotalFileCount; + DWORD cbNamedEntry; + DWORD cbSubDir; + int nError; + + // Parse the directory header in order to retrieve the items + nError = ParseDirectoryHeader(&RootHeader, pbRootFile, pbFileEnd); + if(nError != ERROR_SUCCESS) + return 0; + + // Add the root directory's entries + dwTotalFileCount = RootHeader.dwEntries1 + RootHeader.dwEntries2 + RootHeader.dwEntries3; + + // Parse the named entries + for(DWORD i = 0; i < RootHeader.dwEntries3; i++) + { + // Get the this named entry + if((cbNamedEntry = VerifyNamedFileEntry(RootHeader.pbEntries3, pbFileEnd)) == 0) + return 0; + pNamedEntry = (PDIABLO3_NAMED_ENTRY)RootHeader.pbEntries3; + RootHeader.pbEntries3 += cbNamedEntry; + + // Load the subdirectory to memory + pbSubDir = LoadFileToMemory(hs, pNamedEntry->EncodingKey.Value, &cbSubDir); + if(pbSubDir != NULL) + { + // Count the files in the subdirectory + if(ParseDirectoryHeader(&DirHeader, pbSubDir, pbSubDir + cbSubDir) == ERROR_SUCCESS) + { + dwTotalFileCount += DirHeader.dwEntries1 + DirHeader.dwEntries2 + DirHeader.dwEntries3; + } + + // Free the subdirectory + CASC_FREE(pbSubDir); + } + } + + // Return the total number of entries + return dwTotalFileCount; +} + +static int ParseDirectoryFile( + TRootHandler_Diablo3 * pRootHandler, + LPBYTE pbDirFile, + LPBYTE pbFileEnd, + DWORD dwRootDirIndex) +{ + DIABLO3_DIR_HEADER DirHeader; + int nError; + + // Sanity checks + assert(pRootHandler->FileTable.ItemArray != NULL); + assert(pRootHandler->FileTable.ItemCount < pRootHandler->FileTable.ItemCountMax); + + // Parse the directory header in order to retrieve the items + nError = ParseDirectoryHeader(&DirHeader, pbDirFile, pbFileEnd); + if(nError != ERROR_SUCCESS) + return nError; + + // Process all DIABLO3_FILEID1_ENTRY entries. These are for files + // belonging to an asset group, without subitem number. + // Example: "SoundBank\SoundFile.smp" + // We skip inserting them to the name map, because the names are not known yet + if(DirHeader.pbEntries1 && DirHeader.dwEntries1) + { + assert(dwRootDirIndex != DIABLO3_INVALID_INDEX); + nError = ParseDirEntries_FileId1(pRootHandler, DirHeader.pbEntries1, DirHeader.dwEntries1, dwRootDirIndex); + if(nError != ERROR_SUCCESS) + return nError; + } + + // Parse all DIABLO3_FILEID2_ENTRY entries. These are for files + // belonging to an asset group, with a subitem number. + // Example: "SoundBank\SoundFile\0001.smp" + // We skip inserting them to the name map, because the names are not known yet + if(DirHeader.pbEntries2 && DirHeader.dwEntries2) + { + assert(dwRootDirIndex != DIABLO3_INVALID_INDEX); + nError = ParseDirEntries_FileId2(pRootHandler, DirHeader.pbEntries2, DirHeader.dwEntries2, dwRootDirIndex); + if(nError != ERROR_SUCCESS) + return nError; + } + + + // Parse all named entries. These are for files with arbitrary names, + // and they do not belong to an asset. + if(DirHeader.pbEntries3 && DirHeader.dwEntries3) + { + nError = ParseDirEntries_Named(pRootHandler, DirHeader.pbEntries3, pbFileEnd, DirHeader.dwEntries3, dwRootDirIndex); + if(nError != ERROR_SUCCESS) + return nError; + } + + // Give the directory to the caller + return nError; +} + +static int ParseCoreTOC( + TRootHandler_Diablo3 * pRootHandler, + PCASC_MAP pPackageMap, + LPBYTE pbCoreTocFile, + LPBYTE pbCoreTocEnd) +{ + PDIABLO3_CORE_TOC_HEADER pTocHeader; + PDIABLO3_CORE_TOC_ENTRY pSortedEntries; + PDIABLO3_CORE_TOC_ENTRY pTocEntry; + LPBYTE pbCoreTocNames; + DWORD dwFileIndexes = 0; + DWORD i; + + // Check the space for header + if((pbCoreTocFile + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbCoreTocEnd) + return ERROR_FILE_CORRUPT; + pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbCoreTocFile; + pbCoreTocFile += sizeof(DIABLO3_CORE_TOC_HEADER); + + // Calculate space needed for allocation + for(i = 0; i < DIABLO3_MAX_ASSETS; i++) + { + // Get the first entry + pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]); + + // Find out the entry with the maximum index + for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++) + { + if(pTocEntry->FileIndex > dwFileIndexes) + dwFileIndexes = pTocEntry->FileIndex + 1; + pTocEntry++; + } + } + + // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs + pSortedEntries = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwFileIndexes); + if(pSortedEntries != NULL) + { + // Initialize all entries to invalid + memset(pSortedEntries, 0xFF, dwFileIndexes * sizeof(DIABLO3_CORE_TOC_ENTRY)); + + // Populate the linear array with the entries + for(i = 0; i < DIABLO3_MAX_ASSETS; i++) + { + // Set the pointers + pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]); + pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]); + + // Setup the entries + for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++) + { + pSortedEntries[pTocEntry->FileIndex].AssetIndex = pTocEntry->AssetIndex; + pSortedEntries[pTocEntry->FileIndex].FileIndex = pTocEntry->FileIndex; + pSortedEntries[pTocEntry->FileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocFile) + pTocEntry->NameOffset; + pTocEntry++; + } + } + + // Now use the linear array to resolve the asset indexes and plain names + ResolveFullFileNames(pRootHandler, pSortedEntries, pPackageMap, pbCoreTocFile, dwFileIndexes); + CASC_FREE(pSortedEntries); + } + + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Implementation of Diablo III root file + +static int D3Handler_Insert(TRootHandler_Diablo3 * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey) +{ + ENCODING_KEY EncodingKey; + DWORD dwFileIndex; + + // Don't let the number of items to overflow + if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax) + return ERROR_NOT_ENOUGH_MEMORY; + + // Insert the item + EncodingKey = *(PENCODING_KEY)pbEncodingKey; + dwFileIndex = InsertFileEntry(pRootHandler, + EncodingKey, + szFileName, + strlen(szFileName) + 1); + return (dwFileIndex != INVALID_FILE_INDEX) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; +} + +static LPBYTE D3Handler_Search(TRootHandler_Diablo3 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */) +{ + PCASC_FILE_ENTRY pFileEntry; + const char * szSrcName = NULL; + + // Are we still inside the root directory range? + while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount) + { + // Get the n-th directory and the file name + pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1); + szSrcName = (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName); + + // This is either a full file name or an abbreviated name + if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME) + { + CreateFileName(pRootHandler, szSrcName, pSearch->szFileName); + } + else + { + strcpy(pSearch->szFileName, szSrcName); + } + + // Prepare for the next search + pSearch->IndexLevel1++; + return pFileEntry->EncodingKey.Value; + } + + // No more entries + return NULL; +} + +static void D3Handler_EndSearch(TRootHandler_Diablo3 * /* pRootHandler */, TCascSearch * /* pSearch */) +{ + // Do nothing +} + +static LPBYTE D3Handler_GetKey(TRootHandler_Diablo3 * pRootHandler, const char * szFileName) +{ + PCASC_FILE_ENTRY pFileEntry; + ULONGLONG FileNameHash = CalcFileNameHash(szFileName); + + // Find the file in the name table + pFileEntry = (PCASC_FILE_ENTRY)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL); + return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL; +} + +static void D3Handler_Close(TRootHandler_Diablo3 * pRootHandler) +{ + if(pRootHandler != NULL) + { + // Free the file map + Map_Free(pRootHandler->pRootMap); + + // Free the array of the file entries and file names + Array_Free(&pRootHandler->FileTable); + Array_Free(&pRootHandler->FileNames); + + // Free the root file itself + CASC_FREE(pRootHandler); + } +} + +/* +static void DumpRootFile(TDumpContext * dc, LPBYTE pbFileData, LPBYTE pbFileDataEnd) +{ + char szMD5Buffer[MD5_STRING_SIZE+1]; + DWORD dwSignature; + DWORD dwItemCount; + DWORD i; + + dwSignature = *(PDWORD)pbFileData; + if(dwSignature != CASC_DIABLO3_SUBDIR_SIGNATURE) + return; + pbFileData += sizeof(DWORD); + + // Dump items that contain EncodingKey + AssetId + dwItemCount = *(PDWORD)pbFileData; + pbFileData += sizeof(DWORD); + for(i = 0; i < dwItemCount; i++) + { + PCASC_DIABLO3_ASSET_ENTRY pEntry = (PCASC_DIABLO3_ASSET_ENTRY)pbFileData; + + if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd) + return; + pbFileData += sizeof(*pEntry); + + dump_print(dc, "%s %08X\n", StringFromMD5(pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId); + } + + // Terminate with two newlines + dump_print(dc, "\n"); + + // Dump items that contain EncodingKey + AssetId + FileNumber + dwItemCount = *(PDWORD)pbFileData; + pbFileData += sizeof(DWORD); + for(i = 0; i < dwItemCount; i++) + { + PCASC_DIABLO3_ASSET_ENTRY2 pEntry = (PCASC_DIABLO3_ASSET_ENTRY2)pbFileData; + + if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd) + return; + pbFileData += sizeof(*pEntry); + + dump_print(dc, "%s %08X %08X\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId, pEntry->FileNumber); + } + + // Terminate with two newlines + dump_print(dc, "\n"); + + // Dump items that contain EncodingKey + FileName + dwItemCount = *(PDWORD)pbFileData; + pbFileData += sizeof(DWORD); + for(i = 0; i < dwItemCount; i++) + { + PDIABLO3_NAMED_ENTRY pEntry = (PDIABLO3_NAMED_ENTRY)pbFileData; + DWORD dwEntrySize = VerifyNamedFileEntry(pbFileData, pbFileDataEnd); + + if((pbFileData + dwEntrySize) > pbFileDataEnd) + return; + pbFileData += dwEntrySize; + + dump_print(dc, "%s %s\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->szFileName); + } + + dump_print(dc, "\n\n"); +} +*/ +//----------------------------------------------------------------------------- +// Public functions + +int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +{ + TRootHandler_Diablo3 * pRootHandler; + PCASC_MAP pPackageMap = NULL; + LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; + LPBYTE pbPackagesDat = NULL; + DWORD dwTotalFileCount; + DWORD cbPackagesDat = 0; + int nError; + + // Allocate the root handler object + hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Diablo3, 1); + if(pRootHandler == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill-in the handler functions + memset(pRootHandler, 0, sizeof(TRootHandler_Diablo3)); + pRootHandler->Insert = (ROOT_INSERT)D3Handler_Insert; + pRootHandler->Search = (ROOT_SEARCH)D3Handler_Search; + pRootHandler->EndSearch = (ROOT_ENDSEARCH)D3Handler_EndSearch; + pRootHandler->GetKey = (ROOT_GETKEY)D3Handler_GetKey; + pRootHandler->Close = (ROOT_CLOSE)D3Handler_Close; + + // Fill-in the flags + pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES; + + // Scan the total number of files in the root directories + // Reserve space for extra files + dwTotalFileCount = ScanDirectoryFile(hs, pbRootFile, pbRootFileEnd); + if(dwTotalFileCount == 0) + return ERROR_FILE_CORRUPT; + dwTotalFileCount += CASC_EXTRA_FILES; + + // Allocate the global linear file table + // Note: This is about 18 MB of memory for Diablo III PTR build 30013 + nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, dwTotalFileCount); + if(nError != ERROR_SUCCESS) + return nError; + + // Allocate global buffer for file names. + // The size of the buffer was taken from Diablo III build 30013 + nError = Array_Create(&pRootHandler->FileNames, char, 0x01000000); + if(nError != ERROR_SUCCESS) + return nError; + + // Create map of ROOT_ENTRY -> FileEntry + pRootHandler->pRootMap = Map_Create(dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash)); + if(pRootHandler->pRootMap == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Parse the ROOT file and insert all entries in the file table + nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFileEnd, DIABLO3_INVALID_INDEX); + if(nError == ERROR_SUCCESS) + { + size_t dwRootEntries = pRootHandler->FileTable.ItemCount; + + // We expect the number of level-0 to be less than maximum + assert(dwRootEntries < DIABLO3_MAX_SUBDIRS); + + // Now parse the all root items and load them + for(size_t i = 0; i < dwRootEntries; i++) + { + PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i); + + // Load the entire file to memory + pbRootFile = LoadFileToMemory(hs, pRootEntry->EncodingKey.Value, &cbRootFile); + if(pbRootFile != NULL) + { + nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFile + cbRootFile, i); + CASC_FREE(pbRootFile); + } + } + } + + // Note: The file "Base\Data_D3\PC\Misc\Packages.dat" contains the names + // of the files (without level-0 and level-1 directory). We can use these + // names for supplying the missing extensions + if(nError == ERROR_SUCCESS) + { + // Load the entire file to memory + pbPackagesDat = LoadFileToMemory(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat", &cbPackagesDat); + if(pbPackagesDat != NULL) + { + pPackageMap = CreatePackageMap(pbPackagesDat, pbPackagesDat + cbPackagesDat); + } + } + + // Vast majorify of files at this moment don't have names. + // We can load the Base\CoreTOC.dat file in order + // to get directory asset indexes, file names and extensions + if(nError == ERROR_SUCCESS) + { + LPBYTE pbCoreTOC; + DWORD cbCoreTOC = 0; + + // Load the entire file to memory + pbCoreTOC = LoadFileToMemory(hs, "Base\\CoreTOC.dat", &cbCoreTOC); + if(pbCoreTOC != NULL) + { + ParseCoreTOC(pRootHandler, pPackageMap, pbCoreTOC, pbCoreTOC + cbCoreTOC); + CASC_FREE(pbCoreTOC); + } + } + + // Free the packages map + if(pPackageMap != NULL) + Map_Free(pPackageMap); + if(pbPackagesDat != NULL) + CASC_FREE(pbPackagesDat); + return nError; +} diff --git a/dep/CascLib/src/CascMndxRoot.cpp b/dep/CascLib/src/CascRootFile_Mndx.cpp index 328afee8ba5..bf17290a0af 100644 --- a/dep/CascLib/src/CascMndxRoot.cpp +++ b/dep/CascLib/src/CascRootFile_Mndx.cpp @@ -12,20 +12,20 @@ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" -#include "CascMndxRoot.h" +#include "CascMndx.h" //----------------------------------------------------------------------------- // Local defines -#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0' +#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0' //----------------------------------------------------------------------------- // Local structures typedef struct _FILE_MNDX_HEADER { - DWORD Signature; // 'MNDX' - DWORD HeaderVersion; // Must be <= 2 + DWORD Signature; // 'MNDX' + DWORD HeaderVersion; // Must be <= 2 DWORD FormatVersion; } FILE_MNDX_HEADER, *PFILE_MNDX_HEADER; @@ -39,6 +39,57 @@ typedef struct _FILE_MAR_INFO DWORD MarDataOffsetHi; } FILE_MAR_INFO, *PFILE_MAR_INFO; +typedef struct _CASC_MNDX_INFO +{ + BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file + DWORD HeaderVersion; // Must be <= 2 + DWORD FormatVersion; + DWORD field_1C; + DWORD field_20; + DWORD MarInfoOffset; // Offset of the first MAR entry info + DWORD MarInfoCount; // Number of the MAR info entries + DWORD MarInfoSize; // Size of the MAR info entry + DWORD MndxEntriesOffset; + DWORD MndxEntriesTotal; // Total number of MNDX root entries + DWORD MndxEntriesValid; // Number of valid MNDX root entries + DWORD MndxEntrySize; // Size of one MNDX root entry + struct _MAR_FILE * pMarFile1; // File name list for the packages + struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names + struct _MAR_FILE * pMarFile3; // File name list for complete names +// PCASC_ROOT_ENTRY_MNDX pMndxEntries; +// PCASC_ROOT_ENTRY_MNDX * ppValidEntries; + bool bRootFileLoaded; // true if the root info file was properly loaded + +} CASC_MNDX_INFO, *PCASC_MNDX_INFO; + +typedef struct _CASC_MNDX_PACKAGE +{ + char * szFileName; // Pointer to file name + size_t nLength; // Length of the file name + +} CASC_MNDX_PACKAGE, *PCASC_MNDX_PACKAGE; + +typedef struct _CASC_MNDX_PACKAGES +{ + char * szNameBuffer; // Pointer to the buffer for file names + size_t NameEntries; // Number of name entries in Names + size_t NameBufferUsed; // Number of bytes used in the name buffer + size_t NameBufferMax; // Total size of the name buffer + + CASC_MNDX_PACKAGE Packages[1]; // List of packages + +} CASC_MNDX_PACKAGES, *PCASC_MNDX_PACKAGES; + +// Root file entry for CASC storages with MNDX root file (Heroes of the Storm) +// Corresponds to the in-file structure +typedef struct _CASC_ROOT_ENTRY_MNDX +{ + DWORD Flags; // High 8 bits: Flags, low 24 bits: package index + BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file + DWORD FileSize; // Uncompressed file size, in bytes + +} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX; + //----------------------------------------------------------------------------- // Testing functions prototypes @@ -2734,14 +2785,14 @@ static void MAR_FILE_Destructor(PMAR_FILE pMarFile) #define CASC_PACKAGES_INIT 0x10 #define CASC_PACKAGES_DELTA 0x10 -static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax) +static PCASC_MNDX_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax) { - PCASC_PACKAGES pPackages; + PCASC_MNDX_PACKAGES pPackages; size_t cbToAllocate; // Allocate space - cbToAllocate = sizeof(CASC_PACKAGES) + (nNameEntries * sizeof(CASC_PACKAGE)) + nNameBufferMax; - pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); + cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNameBufferMax; + pPackages = (PCASC_MNDX_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); if(pPackages != NULL) { // Fill the structure @@ -2757,8 +2808,8 @@ static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMa return pPackages; } -static PCASC_PACKAGES InsertToPackageList( - PCASC_PACKAGES pPackages, +static PCASC_MNDX_PACKAGES InsertToPackageList( + PCASC_MNDX_PACKAGES pPackages, const char * szFileName, size_t cchFileName, size_t nPackageIndex) @@ -2777,11 +2828,11 @@ static PCASC_PACKAGES InsertToPackageList( // If any of the two variables overflowed, we need to reallocate the name list if(nNewNameEntries > pPackages->NameEntries || nNewNameBufferMax > pPackages->NameBufferMax) { - PCASC_PACKAGES pOldPackages = pPackages; + PCASC_MNDX_PACKAGES pOldPackages = pPackages; // Allocate new name list - cbToAllocate = sizeof(CASC_PACKAGES) + (nNewNameEntries * sizeof(CASC_PACKAGE)) + nNewNameBufferMax; - pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); + cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNewNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNewNameBufferMax; + pPackages = (PCASC_MNDX_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); if(pPackages == NULL) return NULL; @@ -2822,17 +2873,17 @@ static PCASC_PACKAGES InsertToPackageList( return pPackages; } -static int LoadPackageNames(TCascStorage * hs) +static int LoadPackageNames(PCASC_MNDX_INFO pMndxInfo, PCASC_MNDX_PACKAGES * ppPackages) { TMndxFindResult Struct1C; - PCASC_PACKAGES pPackages = NULL; + PCASC_MNDX_PACKAGES pPackages = NULL; PMAR_FILE pMarFile; // Sanity checks - assert(hs->pMndxInfo != NULL); + assert(pMndxInfo != NULL); // Prepare the file name search in the top level directory - pMarFile = hs->pMndxInfo->pMarFile1; + pMarFile = pMndxInfo->pMarFile1; Struct1C.SetSearchPath("", 0); // Allocate initial name list structure @@ -2856,21 +2907,34 @@ static int LoadPackageNames(TCascStorage * hs) return ERROR_NOT_ENOUGH_MEMORY; } - // Set the name list to the CASC storage structure - hs->pPackages = pPackages; + // Give the packages to the caller + if(ppPackages != NULL) + ppPackages[0] = pPackages; return ERROR_SUCCESS; } -PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName) +//----------------------------------------------------------------------------- +// Implementation of root file functions + +struct TRootHandler_MNDX : public TRootHandler { - PCASC_PACKAGE pMatching = NULL; - PCASC_PACKAGE pPackage; + CASC_MNDX_INFO MndxInfo; + + PCASC_ROOT_ENTRY_MNDX * ppValidEntries; + PCASC_ROOT_ENTRY_MNDX pMndxEntries; + PCASC_MNDX_PACKAGES pPackages; // Linear list of present packages +}; + +PCASC_MNDX_PACKAGE FindMndxPackage(TRootHandler_MNDX * pRootHandler, const char * szFileName) +{ + PCASC_MNDX_PACKAGE pMatching = NULL; + PCASC_MNDX_PACKAGE pPackage; size_t nMaxLength = 0; size_t nLength = strlen(szFileName); // Packages must be loaded - assert(hs->pPackages != NULL); - pPackage = hs->pPackages->Packages; + assert(pRootHandler->pPackages != NULL); + pPackage = pRootHandler->pPackages->Packages; //FILE * fp = fopen("E:\\packages.txt", "wt"); //for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++) @@ -2881,7 +2945,7 @@ PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName) //fclose(fp); // Find the longest matching name - for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++) + for(size_t i = 0; i < pRootHandler->pPackages->NameEntries; i++, pPackage++) { if(pPackage->szFileName != NULL && pPackage->nLength < nLength && pPackage->nLength > nMaxLength) { @@ -2898,11 +2962,49 @@ PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName) return pMatching; } -static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndxFindResult * pStruct1C) +int SearchMndxInfo(TRootHandler_MNDX * pRootHandler, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry) +{ + PCASC_ROOT_ENTRY_MNDX pRootEntry; + PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo; + TMndxFindResult Struct1C; + + // Search the database for the file name + if(pMndxInfo->bRootFileLoaded) + { + Struct1C.SetSearchPath(szFileName, strlen(szFileName)); + + // Search the file name in the second MAR info (the one with stripped package names) + if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS) + return ERROR_FILE_NOT_FOUND; + + // The found MNDX index must fall into range of valid MNDX entries + if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid) + { + // HOTS: E945F4 + pRootEntry = pRootHandler->ppValidEntries[Struct1C.FileNameIndex]; + while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage) + { + // The highest bit serves as a terminator if set + if(pRootEntry->Flags & 0x80000000) + return ERROR_FILE_NOT_FOUND; + + pRootEntry++; + } + + // Give the root entry pointer to the caller + if(ppRootEntry != NULL) + ppRootEntry[0] = pRootEntry; + return ERROR_SUCCESS; + } + } + + return ERROR_FILE_NOT_FOUND; +} + +static LPBYTE FillFindData(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, TMndxFindResult * pStruct1C, PDWORD PtrFileSize) { PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL; - TCascStorage * hs = pSearch->hs; - PCASC_PACKAGE pPackage; + PCASC_MNDX_PACKAGE pPackage; char * szStrippedPtr; char szStrippedName[MAX_PATH+1]; int nError; @@ -2911,40 +3013,134 @@ static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndx assert(pStruct1C->cchFoundPath < MAX_PATH); // Fill the file name - memcpy(pFindData->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath); - pFindData->szFileName[pStruct1C->cchFoundPath] = 0; - pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName); - pFindData->dwFileSize = CASC_INVALID_SIZE; + memcpy(pSearch->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath); + pSearch->szFileName[pStruct1C->cchFoundPath] = 0; // Fill the file size - pPackage = FindMndxPackage(hs, pFindData->szFileName); - if(pPackage != NULL) - { - // Cut the package name off the full path - szStrippedPtr = pFindData->szFileName + pPackage->nLength; - while(szStrippedPtr[0] == '/') - szStrippedPtr++; + pPackage = FindMndxPackage(pRootHandler, pSearch->szFileName); + if(pPackage == NULL) + return NULL; - // We need to convert the stripped name to lowercase, replacing backslashes with slashes - NormalizeFileName_LowerSlash(szStrippedName, szStrippedPtr, MAX_PATH); + // Cut the package name off the full path + szStrippedPtr = pSearch->szFileName + pPackage->nLength; + while(szStrippedPtr[0] == '/') + szStrippedPtr++; - // Search the package - nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntry); - if(nError == ERROR_SUCCESS) - { - pFindData->dwFileSize = pRootEntry->FileSize; - } + // We need to convert the stripped name to lowercase, replacing backslashes with slashes + NormalizeFileName_LowerSlash(szStrippedName, szStrippedPtr, MAX_PATH); + + // Search the package + nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry); + if(nError != ERROR_SUCCESS) + return NULL; + + // Give the file size + if(PtrFileSize != NULL) + PtrFileSize[0] = pRootEntry->FileSize; + return pRootEntry->EncodingKey; +} + +static int MndxHandler_Insert(TRootHandler_MNDX *, const char *, LPBYTE) +{ + return ERROR_NOT_SUPPORTED; +} + +static LPBYTE MndxHandler_Search(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD /* PtrLocaleFlags */) +{ + TMndxFindResult * pStruct1C = NULL; + PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo; + PMAR_FILE pMarFile = pMndxInfo->pMarFile3; + bool bFindResult = false; + + // If the first time, allocate the structure for the search result + if(pSearch->pRootContext == NULL) + { + // Create the new search structure + pStruct1C = new TMndxFindResult; + if(pStruct1C == NULL) + return NULL; + + // Setup the search mask + pStruct1C->SetSearchPath("", 0); + pSearch->pRootContext = pStruct1C; } - return true; + + // Make shortcut for the search structure + assert(pSearch->pRootContext != NULL); + pStruct1C = (TMndxFindResult *)pSearch->pRootContext; + + // Search the next file name (our code) + pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult); + if(bFindResult == false) + return NULL; + + // Give the file size and encoding key + return FillFindData(pRootHandler, pSearch, pStruct1C, PtrFileSize); +} + +static void MndxHandler_EndSearch(TRootHandler_MNDX * /* pRootHandler */, TCascSearch * pSearch) +{ + if(pSearch != NULL) + delete (TMndxFindResult *)pSearch->pRootContext; + pSearch->pRootContext = NULL; +} + +static LPBYTE MndxHandler_GetKey(TRootHandler_MNDX * pRootHandler, const char * szFileName) +{ + PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL; + PCASC_MNDX_PACKAGE pPackage; + char * szStrippedName; + char szNormName[MAX_PATH+1]; + int nError; + + // Convert the file name to lowercase + slashes + NormalizeFileName_LowerSlash(szNormName, szFileName, MAX_PATH); + + // Find the package number + pPackage = FindMndxPackage(pRootHandler, szNormName); + if(pPackage == NULL) + return NULL; + + // Cut the package name off the full path + szStrippedName = szNormName + pPackage->nLength; + while(szStrippedName[0] == '/') + szStrippedName++; + + // Find the root entry + nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry); + if(nError != ERROR_SUCCESS || pRootEntry == NULL) + return NULL; + + // Return the encoding key + return pRootEntry->EncodingKey; +} + +static void MndxHandler_Close(TRootHandler_MNDX * pRootHandler) +{ + if(pRootHandler->MndxInfo.pMarFile1 != NULL) + MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile1); + if(pRootHandler->MndxInfo.pMarFile2 != NULL) + MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile2); + if(pRootHandler->MndxInfo.pMarFile3 != NULL) + MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile3); + if(pRootHandler->ppValidEntries != NULL) + CASC_FREE(pRootHandler->ppValidEntries); + if(pRootHandler->pMndxEntries != NULL) + CASC_FREE(pRootHandler->pMndxEntries); + if(pRootHandler->pPackages != NULL) + CASC_FREE(pRootHandler->pPackages); + + CASC_FREE(pRootHandler); } //----------------------------------------------------------------------------- // Public functions - MNDX info -int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { PFILE_MNDX_HEADER pMndxHeader = (PFILE_MNDX_HEADER)pbRootFile; PCASC_MNDX_INFO pMndxInfo; + TRootHandler_MNDX * pRootHandler; FILE_MAR_INFO MarInfo; PMAR_FILE pMarFile; LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; @@ -2957,13 +3153,24 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) if(pMndxHeader->Signature != CASC_MNDX_SIGNATURE || pMndxHeader->FormatVersion > 2 || pMndxHeader->FormatVersion < 1) return ERROR_BAD_FORMAT; - // Allocate space for the CASC_MNDX_INFO structure - pMndxInfo = CASC_ALLOC(CASC_MNDX_INFO, 1); - if(pMndxInfo == NULL) + // Allocate the structure for the MNDX root file + hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_MNDX, 1); + if(pRootHandler == NULL) return ERROR_NOT_ENOUGH_MEMORY; + // Fill-in the handler functions + memset(pRootHandler, 0, sizeof(TRootHandler_MNDX)); + pRootHandler->Insert = (ROOT_INSERT)MndxHandler_Insert; + pRootHandler->Search = (ROOT_SEARCH)MndxHandler_Search; + pRootHandler->EndSearch = (ROOT_ENDSEARCH)MndxHandler_EndSearch; + pRootHandler->GetKey = (ROOT_GETKEY)MndxHandler_GetKey; + pRootHandler->Close = (ROOT_CLOSE) MndxHandler_Close; + pMndxInfo = &pRootHandler->MndxInfo; + + // Fill-in the flags + pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES; + // Copy the header into the MNDX info - memset(pMndxInfo, 0, sizeof(CASC_MNDX_INFO)); pMndxInfo->HeaderVersion = pMndxHeader->HeaderVersion; pMndxInfo->FormatVersion = pMndxHeader->FormatVersion; dwFilePointer += sizeof(FILE_MNDX_HEADER); @@ -3047,10 +3254,10 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid) { cbToAllocate = pMndxInfo->MndxEntriesTotal * pMndxInfo->MndxEntrySize; - pMndxInfo->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate); - if(pMndxInfo->pMndxEntries != NULL) + pRootHandler->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate); + if(pRootHandler->pMndxEntries != NULL) { - if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pMndxInfo->pMndxEntries, cbToAllocate)) + if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pRootHandler->pMndxEntries, cbToAllocate)) nError = ERROR_FILE_CORRUPT; } else @@ -3064,15 +3271,15 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) if(nError == ERROR_SUCCESS) { assert(pMndxInfo->MndxEntriesValid <= pMndxInfo->MndxEntriesTotal); - pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1); - if(pMndxInfo->ppValidEntries != NULL) + pRootHandler->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1); + if(pRootHandler->ppValidEntries != NULL) { - PCASC_ROOT_ENTRY_MNDX pRootEntry = pMndxInfo->pMndxEntries; + PCASC_ROOT_ENTRY_MNDX pRootEntry = pRootHandler->pMndxEntries; DWORD ValidEntryCount = 1; // edx DWORD nIndex1 = 0; // The first entry is always valid - pMndxInfo->ppValidEntries[nIndex1++] = pMndxInfo->pMndxEntries; + pRootHandler->ppValidEntries[nIndex1++] = pRootHandler->pMndxEntries; // Put the remaining entries for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pRootEntry++) @@ -3082,7 +3289,7 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) if(pRootEntry->Flags & 0x80000000) { - pMndxInfo->ppValidEntries[nIndex1++] = pRootEntry + 1; + pRootHandler->ppValidEntries[nIndex1++] = pRootEntry + 1; ValidEntryCount++; } } @@ -3090,131 +3297,28 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) // Verify the final number of valid entries if((ValidEntryCount - 1) != pMndxInfo->MndxEntriesValid) nError = ERROR_BAD_FORMAT; - - // Mark the MNDX info as fully loaded - pMndxInfo->bRootFileLoaded = true; } else nError = ERROR_NOT_ENOUGH_MEMORY; } - // Save the MNDX info to the archive storage + // Load the MNDX packages if(nError == ERROR_SUCCESS) { - // Store the MNDX database into the archive - hs->pMndxInfo = pMndxInfo; - pMndxInfo = NULL; + nError = LoadPackageNames(pMndxInfo, &pRootHandler->pPackages); + pMndxInfo->bRootFileLoaded = (nError == ERROR_SUCCESS); + } #if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST) -// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", hs->pMndxInfo->pMarFile1); -// CascDumpFileNames("E:\\casc-listfile.txt", hs->pMndxInfo->pMarFile1); - TestMndxRootFile(hs->pMndxInfo); +// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", pMndxInfo->pMarFile1); +// CascDumpFileNames("E:\\casc-listfile.txt", pMndxInfo->pMarFile1); +// TestMndxRootFile(pRootHandler); #endif - // Load the top level entries - nError = LoadPackageNames(hs); - } - - // If anything failed, free the memory remaining allocated - if(nError != ERROR_SUCCESS) - { - if(pMndxInfo != NULL) - FreeMndxInfo(pMndxInfo); - pMndxInfo = NULL; - } + // Return the result return nError; } -int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry) -{ - PCASC_ROOT_ENTRY_MNDX pRootEntry; - TMndxFindResult Struct1C; - - // Search the database for the file name - if(pMndxInfo->bRootFileLoaded) - { - Struct1C.SetSearchPath(szFileName, strlen(szFileName)); - - // Search the file name in the second MAR info (the one with stripped package names) - if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS) - return ERROR_FILE_NOT_FOUND; - - // The found MNDX index must fall into range of valid MNDX entries - if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid) - { - // HOTS: E945F4 - pRootEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex]; - while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage) - { - // The highest bit serves as a terminator if set - if(pRootEntry->Flags & 0x80000000) - return ERROR_FILE_NOT_FOUND; - - pRootEntry++; - } - - // Give the root entry pointer to the caller - if(ppRootEntry != NULL) - ppRootEntry[0] = pRootEntry; - return ERROR_SUCCESS; - } - } - - return ERROR_FILE_NOT_FOUND; -} - -bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) -{ - TMndxFindResult * pStruct1C = NULL; - PCASC_MNDX_INFO pMndxInfo = pSearch->hs->pMndxInfo; - PMAR_FILE pMarFile = pMndxInfo->pMarFile3; - bool bFindResult = false; - - // Sanity checks - assert(pMndxInfo != NULL); - - // If the first time, allocate the structure for the search result - if(pSearch->pStruct1C == NULL) - { - // Create the new search structure - pSearch->pStruct1C = pStruct1C = new TMndxFindResult; - if(pSearch->pStruct1C == NULL) - return false; - - // Setup the search mask - pStruct1C->SetSearchPath("", 0); - } - - // Make shortcut for the search structure - assert(pSearch->pStruct1C != NULL); - pStruct1C = (TMndxFindResult *)pSearch->pStruct1C; - - // Search the next file name (our code) - pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult); - if(bFindResult) - return FillFindData(pSearch, pFindData, pStruct1C); - - return false; -} - -void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo) -{ - if(pMndxInfo != NULL) - { - if(pMndxInfo->pMarFile1 != NULL) - MAR_FILE_Destructor(pMndxInfo->pMarFile1); - if(pMndxInfo->pMarFile2 != NULL) - MAR_FILE_Destructor(pMndxInfo->pMarFile2); - if(pMndxInfo->pMarFile3 != NULL) - MAR_FILE_Destructor(pMndxInfo->pMarFile3); - if(pMndxInfo->ppValidEntries != NULL) - CASC_FREE(pMndxInfo->ppValidEntries); - if(pMndxInfo->pMndxEntries != NULL) - CASC_FREE(pMndxInfo->pMndxEntries); - CASC_FREE(pMndxInfo); - } -} - //---------------------------------------------------------------------------- // Unit tests diff --git a/dep/CascLib/src/CascMndxRoot_x86.asm b/dep/CascLib/src/CascRootFile_Mndx_x86.asm index c0e787d0872..bf6d6d0b489 100644 --- a/dep/CascLib/src/CascMndxRoot_x86.asm +++ b/dep/CascLib/src/CascRootFile_Mndx_x86.asm @@ -6,9 +6,9 @@ ASSUME FS: NOTHING TMndxFindResult struc ; (sizeof=0x1C) ; XREF: GAME_OBJECT_03F7E848::sub_E94500_r szSearchMask dd ? ; XREF: TMndxFindResult__SetPath+2D_w - ; TMndxFindResult__Constructor+4_w ... + ; TMndxFindResult__Constructor+4_w cchSearchMask dd ? ; XREF: TMndxFindResult__SetPath:loc_1956E9A_w - ; TMndxFindResult__Constructor+6_w ... + ; TMndxFindResult__Constructor+6_w field_8 dd ? ; XREF: TMndxFindResult__Constructor+9_w szFoundPath dd ? ; XREF: TMndxFindResult__Constructor+C_w ; TFileNameDatabase__FindFileInDatabase+55_w @@ -17,7 +17,7 @@ cchFoundPath dd ? ; XREF: TMndxFindResult__Constructor+F_w FileNameIndex dd ? ; XREF: TMndxFindResult__Constructor+12_w ; TFileNameDatabase__FindFileInDatabase+6B_w pStruct40 dd ? ; XREF: MAR_FILE__FindFileInDatabase+19_r - ; TMndxFindResult__SetPath:loc_1956E8C_r ... + ; TMndxFindResult__SetPath:loc_1956E8C_r TMndxFindResult ends ; --------------------------------------------------------------------------- @@ -25,9 +25,9 @@ TMndxFindResult ends TRIPLET struc ; (sizeof=0xC) BitIndex dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+39_r NextKey dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+8C_r - ; TSparseArray__GetItemValue:loc_1959B8F_r ... + ; TSparseArray__GetItemValue:loc_1959B8F_r Distance dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+3E_r - ; TSparseArray__GetItemValue:loc_1959BBE_r ... + ; TSparseArray__GetItemValue:loc_1959BBE_r TRIPLET ends ; --------------------------------------------------------------------------- @@ -44,33 +44,33 @@ NAME_ENTRY ends TStruct14 struc ; (sizeof=0x14) ; XREF: TFileNameDatabase::sub_1959460r HashValue dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+44r - ; TGenericArray__InsertItem_STRUCT14+46w ... + ; TGenericArray__InsertItem_STRUCT14+46w field_4 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+48r - ; TGenericArray__InsertItem_STRUCT14+4Bw ... + ; TGenericArray__InsertItem_STRUCT14+4Bw field_8 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+4Er - ; TGenericArray__InsertItem_STRUCT14+51w ... + ; TGenericArray__InsertItem_STRUCT14+51w field_C dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+54r - ; TGenericArray__InsertItem_STRUCT14+57w ... + ; TGenericArray__InsertItem_STRUCT14+57w field_10 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+5Ar - ; TGenericArray__InsertItem_STRUCT14+5Dw ... + ; TGenericArray__InsertItem_STRUCT14+5Dw TStruct14 ends ; --------------------------------------------------------------------------- TGenericArray struc ; (sizeof=0x15) ; XREF: TGenericArray::LoadDwordsArrayWithCopyr - ; TFileNameDatabase::LoadBytesr ... + ; TFileNameDatabase::LoadBytesr DataBuffer dd ? ; XREF: TMndxFindResult__CreateStruct40+24w - ; TMndxFindResult__CreateStruct40+35w ... + ; TMndxFindResult__CreateStruct40+35w field_4 dd ? ; XREF: TMndxFindResult__CreateStruct40+26w - ; TMndxFindResult__CreateStruct40+38w ... + ; TMndxFindResult__CreateStruct40+38w ItemArray dd ? ; XREF: TMndxFindResult__CreateStruct40+29w - ; TMndxFindResult__CreateStruct40+3Bw ... + ; TMndxFindResult__CreateStruct40+3Bw ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+2Cw - ; TMndxFindResult__CreateStruct40+3Ew ... + ; TMndxFindResult__CreateStruct40+3Ew MaxItemCount dd ? ; XREF: TFileNameDatabasePtr__CreateDatabase+27o - ; TMndxFindResult__CreateStruct40+2Fw ... + ; TMndxFindResult__CreateStruct40+2Fw bIsValidArray db ? ; XREF: LoadAndVerifyIndexFileHeader+31w - ; TMndxFindResult__CreateStruct40+32w ... + ; TMndxFindResult__CreateStruct40+32w TGenericArray ends ; --------------------------------------------------------------------------- @@ -81,7 +81,7 @@ DataBuffer dd ? ; XREF: TArchiveDatabase__Destructor+64 ; TArchiveDatabase__Constructor+1A3w field_4 dd ? ; XREF: TArchiveDatabase__Constructor+1A9w ItemArray dd ? ; XREF: sub_1957350+31r - ; sub_19573D0+1Fr ... + ; sub_19573D0+1Fr ItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1B5w MaxItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1BBw bIsValidArray db ? ; XREF: TArchiveDatabase__Constructor+1C1w @@ -89,9 +89,9 @@ bIsValidArray db ? ; XREF: TArchiveDatabase__Constructor+1C db ? ; undefined db ? ; undefined BitsPerEntry dd ? ; XREF: sub_1957350+1Fr - ; sub_19573D0+6r ... + ; sub_19573D0+6r EntryBitMask dd ? ; XREF: sub_1957350:loc_19573B1r - ; sub_19573D0:loc_1957419r ... + ; sub_19573D0:loc_1957419r TotalEntries dd ? ; XREF: TGenericArrayEx__LoadFromStream:loc_195861Bw ; TArchiveDatabase__Constructor+1D3w TBitEntryArray ends @@ -100,50 +100,50 @@ TBitEntryArray ends TStruct40 struc ; (sizeof=0x40) array_00 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+24w - ; TMndxFindResult__CreateStruct40+26w ... + ; TMndxFindResult__CreateStruct40+26w db ? ; undefined db ? ; undefined db ? ; undefined array_18 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+35w - ; TMndxFindResult__CreateStruct40+38w ... + ; TMndxFindResult__CreateStruct40+38w db ? ; undefined db ? ; undefined db ? ; undefined HashValue dd ? ; XREF: TMndxFindResult__CreateStruct40+47w - ; TFileNameDatabase__CheckNextPathFragment+1Ar ... + ; TFileNameDatabase__CheckNextPathFragment+1Ar CharIndex dd ? ; XREF: TMndxFindResult__CreateStruct40+4Aw - ; TFileNameDatabase__CheckNextPathFragment+11r ... + ; TFileNameDatabase__CheckNextPathFragment+11r ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+4Dw - ; TStruct40__InitSearchBuffers+6Bw ... + ; TStruct40__InitSearchBuffers+6Bw SearchPhase dd ? ; XREF: TMndxFindResult__CreateStruct40+50w - ; TFileNameDatabase__FindFileInDatabase+17w ... + ; TFileNameDatabase__FindFileInDatabase+17w TStruct40 ends ; --------------------------------------------------------------------------- TSparseArray struc ; (sizeof=0x65) ; XREF: TSparseArray::LoadFromStream_Exchange_r - ; TNameIndexStruct_r ... + ; TNameIndexStruct_r ItemIsPresent TGenericArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w - ; TFileNameDatabase__Destructor+4C_r ... + ; TFileNameDatabase__Destructor+4C_r db ? ; undefined db ? ; undefined db ? ; undefined TotalItemCount dd ? ; XREF: TSparseArray__ExchangeWith+4D_r - ; TSparseArray__ExchangeWith+56_w ... + ; TSparseArray__ExchangeWith+56_w ValidItemCount dd ? ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r - ; TSparseArray__ExchangeWith+59_r ... + ; TSparseArray__ExchangeWith+59_r ArrayTriplets_20 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+40_r - ; TFileNameDatabase__Destructor+94_r ... + ; TFileNameDatabase__Destructor+94_r db ? ; undefined db ? ; undefined db ? ; undefined ArrayDwords_38 TGenericArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o - ; TFileNameDatabase__Destructor+34_r ... + ; TFileNameDatabase__Destructor+34_r db ? ; undefined db ? ; undefined db ? ; undefined ArrayDwords_50 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+28_r - ; TFileNameDatabase__Destructor+7C_r ... + ; TFileNameDatabase__Destructor+7C_r TSparseArray ends ; --------------------------------------------------------------------------- @@ -151,12 +151,12 @@ TSparseArray ends TNameIndexStruct struc ; (sizeof=0x7D) ; XREF: TNameIndexStruct::LoadFromStream_Exchange_r ; TFileNameDatabaser NameFragments TGenericArray <> ; XREF: TFileNameDatabase__Destructor+58_r - ; TFileNameDatabase__LoadFromStream+82_r ... + ; TFileNameDatabase__LoadFromStream+82_r db ? ; undefined db ? ; undefined db ? ; undefined Struct68 TSparseArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w - ; TFileNameDatabase__Destructor+28_r ... + ; TFileNameDatabase__Destructor+28_r TNameIndexStruct ends ; --------------------------------------------------------------------------- @@ -164,11 +164,11 @@ TNameIndexStruct ends TByteStream struc ; (sizeof=0x18) ; XREF: TFileNameDatabasePtr::CreateDatabase_r ; TFileNameDatabase_r pbData dd ? ; XREF: TByteStream__Constructor+4_w - ; TByteStream__IsMarDataValid+2_r ... + ; TByteStream__IsMarDataValid+2_r pvMappedFile dd ? ; XREF: TByteStream__Constructor+6_w ; TByteStream__Destructor+3_r cbData dd ? ; XREF: TByteStream__Constructor+9_w - ; TByteStream__GetBytes:loc_1959A05_r ... + ; TByteStream__GetBytes:loc_1959A05_r field_C dd ? ; XREF: TByteStream__Constructor+C_w hFile dd ? ; XREF: TByteStream__Constructor+F_w ; TByteStream__Destructor:loc_19599D2_r @@ -183,11 +183,11 @@ TStruct10 struc ; (sizeof=0x10) ; XREF: TStruct10::sub_1957800_r field_0 dd ? ; XREF: sub_19572E0+24_w ; TFileNameDatabase__Constructor+21A_w field_4 dd ? ; XREF: sub_1956FD0+28_w - ; sub_1956FD0:loc_1957001_w ... + ; sub_1956FD0:loc_1957001_w field_8 dd ? ; XREF: sub_19572E0+4A_w - ; sub_19572E0+5B_w ... + ; sub_19572E0+5B_w field_C dd ? ; XREF: sub_1957050:loc_1957074_w - ; sub_1957050:loc_1957081_w ... + ; sub_1957050:loc_1957081_w TStruct10 ends ; --------------------------------------------------------------------------- @@ -195,52 +195,52 @@ TStruct10 ends TFileNameDatabasePtr struc ; (sizeof=0x4) ; XREF: TFileNameDatabase_r ; MAR_FILE_r pDatabase dd ? ; XREF: MAR_FILE__Constructor+1D_w - ; MAR_FILE__CreateDatabase+22_w ... + ; MAR_FILE__CreateDatabase+22_w TFileNameDatabasePtr ends ; --------------------------------------------------------------------------- TFileNameDatabase struc ; (sizeof=0x240) ; XREF: TFileNameDatabase::LoadFromStream_Exchange_r Struct68_00 TSparseArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o - ; TFileNameDatabase__Destructor+D9_r ... + ; TFileNameDatabase__Destructor+D9_r db ? ; undefined db ? ; undefined db ? ; undefined FileNameIndexes TSparseArray <> ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r - ; TFileNameDatabase__Destructor+AC_r ... + ; TFileNameDatabase__Destructor+AC_r db ? ; undefined db ? ; undefined db ? ; undefined Struct68_D0 TSparseArray <> ; XREF: TFileNameDatabase__Destructor+7C_r - ; TFileNameDatabase__Destructor+88_r ... + ; TFileNameDatabase__Destructor+88_r db ? ; undefined db ? ; undefined db ? ; undefined FrgmDist_LoBits TGenericArray <> ; XREF: TFileNameDatabase__Destructor+70_r - ; TFileNameDatabase__CheckNextPathFragment:loc_1957AC9_r ... + ; TFileNameDatabase__CheckNextPathFragment:loc_1957AC9_r db ? ; undefined db ? ; undefined db ? ; undefined FrgmDist_HiBits TBitEntryArray <> ; XREF: TFileNameDatabase__Destructor+64_r - ; TFileNameDatabase__CheckNextPathFragment+119_r ... + ; TFileNameDatabase__CheckNextPathFragment+119_r IndexStruct_174 TNameIndexStruct <> ; XREF: TFileNameDatabase__Destructor+28_r - ; TFileNameDatabase__Destructor+34_r ... + ; TFileNameDatabase__Destructor+34_r db ? ; undefined db ? ; undefined db ? ; undefined NextDB TFileNameDatabasePtr <> ; XREF: TFileNameDatabase__Destructor+1D_o - ; TFileNameDatabase__CheckNextPathFragment+52_r ... + ; TFileNameDatabase__CheckNextPathFragment+52_r NameFragTable TGenericArray <> ; XREF: TFileNameDatabase__Destructor+E_r - ; TFileNameDatabase__CheckNextPathFragment+2F_r ... + ; TFileNameDatabase__CheckNextPathFragment+2F_r db ? ; undefined db ? ; undefined db ? ; undefined NameFragIndexMask dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+26_r - ; sub_1957B80:loc_1957B95_r ... + ; sub_1957B80:loc_1957B95_r field_214 dd ? ; XREF: TFileNameDatabase__Constructor+20E_w ; TFileNameDatabase__LoadFromStream+110_w Struct10 TStruct10 <> ; XREF: TFileNameDatabase__Constructor+21A_w - ; TFileNameDatabase__Constructor+224_w ... + ; TFileNameDatabase__Constructor+224_w MarStream TByteStream <> ; XREF: TFileNameDatabase__Destructor+3_o ; TFileNameDatabase__Constructor+214_o TFileNameDatabase ends @@ -249,11 +249,11 @@ TFileNameDatabase ends MAR_FILE struc ; (sizeof=0xC) pDatabasePtr TFileNameDatabasePtr <> ; XREF: MAR_FILE__Constructor+1D_w - ; MAR_FILE__CreateDatabase+22_w ... + ; MAR_FILE__CreateDatabase+22_w pbMarFileData dd ? ; XREF: MAR_FILE__Constructor+1A_w - ; MAR_FILE__CreateDatabase+1B_r ... + ; MAR_FILE__CreateDatabase+1B_r cbMarFileData dd ? ; XREF: MAR_FILE__Constructor+12_w - ; MAR_FILE__CreateDatabase+18_r ... + ; MAR_FILE__CreateDatabase+18_r MAR_FILE ends .DATA @@ -368,7 +368,7 @@ operator_delete ENDP TSparseArray__GetItemValue proc near ; CODE XREF: sub_1957350+1A_p - ; TFileNameDatabase__CheckNextPathFragment+103_p ... + ; TFileNameDatabase__CheckNextPathFragment+103_p arg_0 = dword ptr 8 @@ -451,7 +451,7 @@ loc_1959BDE: ; CODE XREF: TSparseArray__GetItemValue+ add esi, eax loc_1959BE0: ; CODE XREF: TSparseArray__GetItemValue+26_j - ; TSparseArray__GetItemValue+45_j ... + ; TSparseArray__GetItemValue+45_j mov ebx, edi ; jumptable 01959B88 default case shr ebx, 5 test bl, 1 @@ -525,7 +525,7 @@ TSparseArray__GetItemValue endp TArchiveDatabase__sub_1959CB0 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1_p - ; sub_1958B00+E8_p ... + ; sub_1958B00+E8_p pThis = dword ptr -4 dwKey = dword ptr 8 @@ -614,7 +614,7 @@ loc_1959D55: ; CODE XREF: TFileNameDatabase__FindNext mov ecx, [ebp+pThis] loc_1959D5F: ; CODE XREF: TFileNameDatabase__FindNextMatch+62_j - ; TFileNameDatabase__FindNextMatch+7C_j ... + ; TFileNameDatabase__FindNextMatch+7C_j mov ecx, [ecx+28h] lea esi, [eax+eax*2] mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax] @@ -709,7 +709,7 @@ loc_1959E49: ; CODE XREF: TFileNameDatabase__FindNext lea edx, [edx+esi-1C0h] loc_1959E53: ; CODE XREF: TFileNameDatabase__FindNextMatch+FE_j - ; TFileNameDatabase__FindNextMatch+10B_j ... + ; TFileNameDatabase__FindNextMatch+10B_j mov eax, [ebp+pThis] mov ebx, [eax+8] mov ecx, [ebx+edi*4] @@ -809,7 +809,7 @@ TArchiveDatabase__sub_1959CB0 endp ; Attributes: bp-based frame TNameIndexStruct__CheckNameFragment proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+6B_p - ; TFileNameDatabase__CheckNextPathFragment+191_p ... + ; TFileNameDatabase__CheckNextPathFragment+191_p pThis = dword ptr -4 pUnknownStruct1C= dword ptr 8 @@ -845,7 +845,7 @@ loc_195A1A1: ; CODE XREF: TNameIndexStruct__CheckName jb short loc_195A1A1 loc_195A1BD: ; CODE XREF: TNameIndexStruct__CheckNameFragment+2C_j - ; TNameIndexStruct__CheckNameFragment+5Ej ... + ; TNameIndexStruct__CheckNameFragment+5Ej pop edi pop esi xor al, al @@ -907,7 +907,7 @@ TNameIndexStruct__CheckNameFragment endp ; Attributes: bp-based frame sub_1957350 proc near ; CODE XREF: sub_1957B80+D8p - ; sub_1957B80:loc_1957C73p ... + ; sub_1957B80:loc_1957C73p arg_0 = dword ptr 8 @@ -971,7 +971,7 @@ sub_1957350 endp ; Attributes: bp-based frame sub_1959F50 proc near ; CODE XREF: sub_1957B80+14C_p - ; sub_1958980+62_p ... + ; sub_1958980+62_p var_4 = dword ptr -4 arg_0 = dword ptr 8 @@ -1046,7 +1046,7 @@ loc_1959FCA: ; CODE XREF: sub_1959F50+76_j mov ecx, [ebp+var_4] loc_1959FD4: ; CODE XREF: sub_1959F50+51_j - ; sub_1959F50+5B_j ... + ; sub_1959F50+5B_j mov edi, [ecx+28h] lea esi, [eax+eax*2] sub edx, [edi+esi*4] @@ -1124,7 +1124,7 @@ loc_195A064: ; CODE XREF: sub_1959F50+CD_j sub edx, esi loc_195A066: ; CODE XREF: sub_1959F50+B5_j - ; sub_1959F50+BC_j ... + ; sub_1959F50+BC_j mov ebx, [ecx+8] mov esi, [ebx+edi*4] mov eax, esi @@ -1223,7 +1223,7 @@ sub_1959F50 endp ; Attributes: bp-based frame sub_1957B80 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+5E_p - ; TFileNameDatabase__CheckNextPathFragment+180_p ... + ; TFileNameDatabase__CheckNextPathFragment+180_p pStruct40 = dword ptr -4 arg_0 = dword ptr 8 @@ -1300,7 +1300,7 @@ loc_1957C05: ; CODE XREF: sub_1957B80+6C_j jb loc_1957B95 loc_1957C25: ; CODE XREF: sub_1957B80+67_j - ; sub_1957B80+7C_j ... + ; sub_1957B80+7C_j pop edi pop esi xor al, al @@ -1765,7 +1765,7 @@ TFileNameDatabase__FindFileInDatabase endp ; Attributes: bp-based frame TGenericArray__SetMaxItems_BYTE proc near ; CODE XREF: sub_19582E0+2Cp - ; TStruct40__InitSearchBuffers+2Cp ... + ; TStruct40__InitSearchBuffers+2Cp ByteCount = dword ptr 8 @@ -1820,7 +1820,7 @@ TGenericArray__SetMaxItems_BYTE endp TGenericArray__SetMaxItems_STRUCT14 proc near ; CODE XREF: TGenericArray__InsertItem_STRUCT14+2Cp - ; TGenericArray__sub_19583A0+2Dp ... + ; TGenericArray__sub_19583A0+2Dp var_4 = dword ptr -4 ItemCount = dword ptr 8 @@ -2086,7 +2086,7 @@ TGenericArray__InsertItem_STRUCT14 endp ; Attributes: bp-based frame CopyNameFragment proc near ; CODE XREF: sub_1958980+DAp - ; sub_1958D70+6Dp ... + ; sub_1958D70+6Dp pThis = dword ptr -4 pStruct1C = dword ptr 8 @@ -2284,7 +2284,7 @@ CopyNameFragment endp ; Attributes: bp-based frame sub_1958D70 proc near ; CODE XREF: sub_1958980+C9p - ; sub_1958D70+59p ... + ; sub_1958D70+59p var_C = dword ptr -0Ch var_8 = dword ptr -8 @@ -2582,7 +2582,7 @@ sub_1958D70 endp ; Attributes: bp-based frame TFileNameDatabase__sub_1959CB0 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1p - ; TFileNameDatabase__sub_1958B00+E8p ... + ; TFileNameDatabase__sub_1958B00+E8p pThis = dword ptr -4 dwKey = dword ptr 8 @@ -2671,7 +2671,7 @@ loc_1959D55: ; CODE XREF: TFileNameDatabase__sub_1959 mov ecx, [ebp+pThis] loc_1959D5F: ; CODE XREF: TFileNameDatabase__sub_1959CB0+62j - ; TFileNameDatabase__sub_1959CB0+7Cj ... + ; TFileNameDatabase__sub_1959CB0+7Cj mov ecx, [ecx+TFileNameDatabase.Struct68_00.ArrayTriplets_20.ItemArray] lea esi, [eax+eax*2] mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax] @@ -2766,7 +2766,7 @@ loc_1959E49: ; CODE XREF: TFileNameDatabase__sub_1959 lea edx, [edx+esi-1C0h] loc_1959E53: ; CODE XREF: TFileNameDatabase__sub_1959CB0+FEj - ; TFileNameDatabase__sub_1959CB0+10Bj ... + ; TFileNameDatabase__sub_1959CB0+10Bj mov eax, [ebp+pThis] mov ebx, [eax+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] mov ecx, [ebx+edi*4] @@ -2866,7 +2866,7 @@ TFileNameDatabase__sub_1959CB0 endp ; Attributes: bp-based frame TNameIndexStruct__CheckAndCopyNameFragment proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+74p - ; TArchiveDatabase__sub_1958B00+1E0p ... + ; TArchiveDatabase__sub_1958B00+1E0p var_C = dword ptr -0Ch pThis = dword ptr -8 @@ -3206,7 +3206,7 @@ TNameIndexStruct__CheckAndCopyNameFragment endp ; Attributes: bp-based frame sub_1959010 proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+67p - ; TArchiveDatabase__sub_1958B00+1CFp ... + ; TArchiveDatabase__sub_1958B00+1CFp pFragmentInfo = dword ptr -0Ch var_8 = dword ptr -8 @@ -3262,7 +3262,7 @@ loc_195907F: ; CODE XREF: sub_1959010+5Ej jnz loc_195912E loc_1959087: ; CODE XREF: sub_1959010+90j - ; sub_1959010+1F3j ... + ; sub_1959010+1F3j pop edi pop esi xor al, al @@ -3962,14 +3962,14 @@ loc_19594B0: ; CODE XREF: TFileNameDatabase__sub_1959 ; --------------------------------------------------------------------------- loc_1959522: ; CODE XREF: TFileNameDatabase__sub_1959460+26Bj - ; TFileNameDatabase__sub_1959460+2D9j ... + ; TFileNameDatabase__sub_1959460+2D9j mov ebx, [ebp+pThis] jmp short loc_1959530 ; --------------------------------------------------------------------------- align 10h loc_1959530: ; CODE XREF: TFileNameDatabase__sub_1959460+23j - ; TFileNameDatabase__sub_1959460+97j ... + ; TFileNameDatabase__sub_1959460+97j mov edx, [esi+TStruct40.ItemCount] cmp edx, [esi+TStruct40.array_18.ItemCount] jnz loc_19595BD diff --git a/dep/CascLib/src/CascRootFile_Ovr.cpp b/dep/CascLib/src/CascRootFile_Ovr.cpp new file mode 100644 index 00000000000..205353c0120 --- /dev/null +++ b/dep/CascLib/src/CascRootFile_Ovr.cpp @@ -0,0 +1,199 @@ +/*****************************************************************************/ +/* CascRootFile_Ovr.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Support for loading Overwatch ROOT file */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 28.10.15 1.00 Lad The first version of CascRootFile_Ovr.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Structure definitions for Overwatch root file + +typedef struct _CASC_FILE_ENTRY +{ + ENCODING_KEY EncodingKey; // Encoding key + ULONGLONG FileNameHash; // File name hash + DWORD dwFileName; // Offset of the file name in the name cache +} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY; + +struct TRootHandler_Ovr : public TRootHandler +{ + // Linear global list of file entries + DYNAMIC_ARRAY FileTable; + + // Linear global list of names + DYNAMIC_ARRAY FileNames; + + // Global map of FileName -> FileEntry + PCASC_MAP pRootMap; +}; + +//----------------------------------------------------------------------------- +// Local functions + +static int InsertFileEntry( + TRootHandler_Ovr * pRootHandler, + const char * szFileName, + LPBYTE pbEncodingKey) +{ + PCASC_FILE_ENTRY pFileEntry; + size_t nLength = strlen(szFileName); + + // Attempt to insert the file name to the global buffer + szFileName = (char *)Array_Insert(&pRootHandler->FileNames, szFileName, nLength + 1); + if(szFileName == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Attempt to insert the entry to the array of file entries + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + if(pFileEntry == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill the file entry + pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey; + pFileEntry->FileNameHash = CalcFileNameHash(szFileName); + pFileEntry->dwFileName = Array_IndexOf(&pRootHandler->FileNames, szFileName); + + // Insert the file entry to the map + assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL); + Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Implementation of Overwatch root file + +static int OvrHandler_Insert( + TRootHandler_Ovr * pRootHandler, + const char * szFileName, + LPBYTE pbEncodingKey) +{ + return InsertFileEntry(pRootHandler, szFileName, pbEncodingKey); +} + +static LPBYTE OvrHandler_Search(TRootHandler_Ovr * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */) +{ + PCASC_FILE_ENTRY pFileEntry; + + // Are we still inside the root directory range? + while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount) + { + // Retrieve the file item + pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1); + strcpy(pSearch->szFileName, (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName)); + + // Prepare the pointer to the next search + pSearch->IndexLevel1++; + return pFileEntry->EncodingKey.Value; + } + + // No more entries + return NULL; +} + +static void OvrHandler_EndSearch(TRootHandler_Ovr * /* pRootHandler */, TCascSearch * /* pSearch */) +{ + // Do nothing +} + +static LPBYTE OvrHandler_GetKey(TRootHandler_Ovr * pRootHandler, const char * szFileName) +{ + ULONGLONG FileNameHash = CalcFileNameHash(szFileName); + + return (LPBYTE)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL); +} + +static void OvrHandler_Close(TRootHandler_Ovr * pRootHandler) +{ + if(pRootHandler != NULL) + { + // Free the file map + if(pRootHandler->pRootMap) + Map_Free(pRootHandler->pRootMap); + pRootHandler->pRootMap = NULL; + + // Free the array of the file names and file items + Array_Free(&pRootHandler->FileTable); + Array_Free(&pRootHandler->FileNames); + + // Free the root file itself + CASC_FREE(pRootHandler); + } +} + +//----------------------------------------------------------------------------- +// Public functions + +int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +{ + TRootHandler_Ovr * pRootHandler; + ENCODING_KEY KeyBuffer; + QUERY_KEY EncodingKey = {KeyBuffer.Value, MD5_HASH_SIZE}; + void * pTextFile; + size_t nLength; + char szOneLine[0x200]; + char szFileName[MAX_PATH+1]; + DWORD dwFileCountMax = hs->pEncodingMap->TableSize; + int nError = ERROR_SUCCESS; + + // Allocate the root handler object + hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Ovr, 1); + if(pRootHandler == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill-in the handler functions + memset(pRootHandler, 0, sizeof(TRootHandler_Ovr)); + pRootHandler->Insert = (ROOT_INSERT)OvrHandler_Insert; + pRootHandler->Search = (ROOT_SEARCH)OvrHandler_Search; + pRootHandler->EndSearch = (ROOT_ENDSEARCH)OvrHandler_EndSearch; + pRootHandler->GetKey = (ROOT_GETKEY)OvrHandler_GetKey; + pRootHandler->Close = (ROOT_CLOSE)OvrHandler_Close; + + // Fill-in the flags + pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES; + + // Allocate the linear array of file entries + nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, 0x10000); + if(nError != ERROR_SUCCESS) + return nError; + + // Allocate the buffer for the file names + nError = Array_Create(&pRootHandler->FileNames, char, 0x10000); + if(nError != ERROR_SUCCESS) + return nError; + + // Create map of ROOT_ENTRY -> FileEntry + pRootHandler->pRootMap = Map_Create(dwFileCountMax, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash)); + if(pRootHandler->pRootMap == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Parse the ROOT file + pTextFile = ListFile_FromBuffer(pbRootFile, cbRootFile); + if(pTextFile != NULL) + { + // Skip the first line, containing "#MD5|CHUNK_ID|FILENAME|INSTALLPATH" + ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine)); + + // Parse the next lines + while((nLength = ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine))) > 0) + { + // Parse the line + nError = ParseRootFileLine(szOneLine, szOneLine + nLength, &EncodingKey, szFileName, _maxchars(szFileName)); + if(nError == ERROR_SUCCESS) + { + InsertFileEntry(pRootHandler, szFileName, KeyBuffer.Value); + } + } + + ListFile_Free(pTextFile); + } + + // Succeeded + return nError; +} diff --git a/dep/CascLib/src/CascRootFile_WoW6.cpp b/dep/CascLib/src/CascRootFile_WoW6.cpp new file mode 100644 index 00000000000..0c631f82b00 --- /dev/null +++ b/dep/CascLib/src/CascRootFile_WoW6.cpp @@ -0,0 +1,531 @@ +/*****************************************************************************/ +/* CascOpenStorage.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Storage functions for CASC */ +/* Note: WoW6 offsets refer to WoW.exe 6.0.3.19116 (32-bit) */ +/* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascOpenStorage.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +#define ROOT_SEARCH_PHASE_INITIALIZING 0 +#define ROOT_SEARCH_PHASE_LISTFILE 1 +#define ROOT_SEARCH_PHASE_NAMELESS 2 +#define ROOT_SEARCH_PHASE_FINISHED 2 + +// On-disk version of locale block +typedef struct _FILE_LOCALE_BLOCK +{ + DWORD NumberOfFiles; // Number of entries + DWORD Flags; + DWORD Locales; // File locale mask (CASC_LOCALE_XXX) + + // Followed by a block of 32-bit integers (count: NumberOfFiles) + // Followed by the MD5 and file name hash (count: NumberOfFiles) + +} FILE_LOCALE_BLOCK, *PFILE_LOCALE_BLOCK; + +// On-disk version of root entry +typedef struct _FILE_ROOT_ENTRY +{ + ENCODING_KEY EncodingKey; // MD5 of the file + ULONGLONG FileNameHash; // Jenkins hash of the file name + +} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY; + + +typedef struct _CASC_ROOT_BLOCK +{ + PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block + PDWORD FileDataIds; // Pointer to the array of File Data IDs + PFILE_ROOT_ENTRY pRootEntries; + +} CASC_ROOT_BLOCK, *PCASC_ROOT_BLOCK; + +// Root file entry for CASC storages without MNDX root file (World of Warcraft 6.0+) +// Does not match to the in-file structure of the root entry +typedef struct _CASC_FILE_ENTRY +{ + ENCODING_KEY EncodingKey; // File encoding key (MD5) + ULONGLONG FileNameHash; // Jenkins hash of the file name + DWORD FileDataId; // File Data Index + DWORD Locales; // Locale flags of the file + +} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY; + +struct TRootHandler_WoW6 : public TRootHandler +{ + // Linear global list of file entries + DYNAMIC_ARRAY FileTable; + + // Global map of FileName -> FileEntry + PCASC_MAP pRootMap; + + // For counting files + DWORD dwTotalFileCount; + DWORD FileDataId; +}; + +// Prototype for root file parsing routine +typedef int (*PARSE_ROOT)(TRootHandler_WoW6 * pRootHandler, PCASC_ROOT_BLOCK pBlockInfo); + +//----------------------------------------------------------------------------- +// Local functions + +static bool IsFileDataIdName(const char * szFileName) +{ + BYTE BinaryValue[4]; + + // File name must begin with "File", case insensitive + if(AsciiToUpperTable_BkSlash[szFileName[0]] == 'F' && + AsciiToUpperTable_BkSlash[szFileName[1]] == 'I' && + AsciiToUpperTable_BkSlash[szFileName[2]] == 'L' && + AsciiToUpperTable_BkSlash[szFileName[3]] == 'E') + { + // Then, 8 hexadecimal digits must follow + if(ConvertStringToBinary(szFileName + 4, 8, BinaryValue) == ERROR_SUCCESS) + { + // Must be followed by an extension or end-of-string + return (szFileName[0x0C] == 0 || szFileName[0x0C] == '.'); + } + } + + return false; +} + +// Search by FileDataId +PCASC_FILE_ENTRY FindRootEntry(DYNAMIC_ARRAY & FileTable, DWORD FileDataId) +{ + PCASC_FILE_ENTRY pStartEntry = (PCASC_FILE_ENTRY)FileTable.ItemArray; + PCASC_FILE_ENTRY pMidlEntry; + PCASC_FILE_ENTRY pEndEntry = pStartEntry + FileTable.ItemCount - 1; + int nResult; + + // Perform binary search on the table + while(pStartEntry < pEndEntry) + { + // Calculate the middle of the interval + pMidlEntry = pStartEntry + ((pEndEntry - pStartEntry) / 2); + + // Did we find it? + nResult = (int)FileDataId - (int)pMidlEntry->FileDataId; + if(nResult == 0) + return pMidlEntry; + + // Move the interval to the left or right + (nResult < 0) ? pEndEntry = pMidlEntry : pStartEntry = pMidlEntry + 1; + } + + return NULL; +} + +// Search by file name hash +// Also used in CascSearchFile +PCASC_FILE_ENTRY FindRootEntry(PCASC_MAP pRootMap, const char * szFileName, DWORD * PtrTableIndex) +{ + // Calculate the HASH value of the normalized file name + ULONGLONG FileNameHash = CalcFileNameHash(szFileName); + + // Perform the hash search + return (PCASC_FILE_ENTRY)Map_FindObject(pRootMap, &FileNameHash, PtrTableIndex); +} + +LPBYTE VerifyLocaleBlock(PCASC_ROOT_BLOCK pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd) +{ + // Validate the file locale block + pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer; + pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1); + if(pbFilePointer > pbFileEnd) + return NULL; + + // Validate the array of 32-bit integers + pBlockInfo->FileDataIds = (PDWORD)pbFilePointer; + pbFilePointer = (LPBYTE)(pBlockInfo->FileDataIds + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); + if(pbFilePointer > pbFileEnd) + return NULL; + + // Validate the array of root entries + pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer; + pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); + if(pbFilePointer > pbFileEnd) + return NULL; + + // Return the position of the next block + return pbFilePointer; +} + +static int ParseRoot_CountFiles( + TRootHandler_WoW6 * pRootHandler, + PCASC_ROOT_BLOCK pRootBlock) +{ + // Add the file count to the total file count + pRootHandler->dwTotalFileCount += pRootBlock->pLocaleBlockHdr->NumberOfFiles; + return ERROR_SUCCESS; +} + +static int ParseRoot_AddRootEntries( + TRootHandler_WoW6 * pRootHandler, + PCASC_ROOT_BLOCK pRootBlock) +{ + PCASC_FILE_ENTRY pFileEntry; + + // Sanity checks + assert(pRootHandler->FileTable.ItemArray != NULL); + assert(pRootHandler->FileTable.ItemCountMax != 0); + + // WoW.exe (build 19116): Blocks with zero files are skipped + for(DWORD i = 0; i < pRootBlock->pLocaleBlockHdr->NumberOfFiles; i++) + { + // Create new entry, with overflow check + if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax) + return ERROR_INSUFFICIENT_BUFFER; + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + + // (004147A3) Prepare the CASC_FILE_ENTRY structure + pFileEntry->FileNameHash = pRootBlock->pRootEntries[i].FileNameHash; + pFileEntry->FileDataId = pRootHandler->FileDataId + pRootBlock->FileDataIds[i]; + pFileEntry->Locales = pRootBlock->pLocaleBlockHdr->Locales; + pFileEntry->EncodingKey = pRootBlock->pRootEntries[i].EncodingKey; + + // Also, insert the entry to the map + Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash); + + // Update the local File Data Id + assert((pFileEntry->FileDataId + 1) > pFileEntry->FileDataId); + pRootHandler->FileDataId = pFileEntry->FileDataId + 1; + + // Move to the next root entry + pFileEntry++; + } + + return ERROR_SUCCESS; +} + +static int ParseWowRootFileInternal( + TRootHandler_WoW6 * pRootHandler, + PARSE_ROOT pfnParseRoot, + LPBYTE pbRootFile, + LPBYTE pbRootFileEnd, + DWORD dwLocaleMask, + bool bLoadBlocksWithFlags80, + BYTE HighestBitValue) +{ + CASC_ROOT_BLOCK RootBlock; + + // Now parse the root file + while(pbRootFile < pbRootFileEnd) + { + // Validate the file locale block + pbRootFile = VerifyLocaleBlock(&RootBlock, pbRootFile, pbRootFileEnd); + if(pbRootFile == NULL) + break; + + // WoW.exe (build 19116): Entries with flag 0x100 set are skipped + if(RootBlock.pLocaleBlockHdr->Flags & 0x100) + continue; + + // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if arg_4 is set to FALSE (which is by default) + if((RootBlock.pLocaleBlockHdr->Flags & 0x80) && bLoadBlocksWithFlags80 == 0) + continue; + + // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to arg_8 are skipped + if((RootBlock.pLocaleBlockHdr->Flags >> 0x1F) != HighestBitValue) + continue; + + // WoW.exe (build 19116): Locales other than defined mask are skipped too + if((RootBlock.pLocaleBlockHdr->Locales & dwLocaleMask) == 0) + continue; + + // Now call the custom function + pfnParseRoot(pRootHandler, &RootBlock); + } + + return ERROR_SUCCESS; +} + +/* + // Code from WoW.exe + if(dwLocaleMask == CASC_LOCALE_DUAL_LANG) + { + // Is this english version of WoW? + if(arg_4 == CASC_LOCALE_BIT_ENUS) + { + LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, false, HighestBitValue); + LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue); + return ERROR_SUCCESS; + } + + // Is this portuguese version of WoW? + if(arg_4 == CASC_LOCALE_BIT_PTBR) + { + LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, false, HighestBitValue); + LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue); + } + } + + LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << arg_4), false, HighestBitValue); +*/ + +static int ParseWowRootFile2( + TRootHandler_WoW6 * pRootHandler, + PARSE_ROOT pfnParseRoot, + LPBYTE pbRootFile, + LPBYTE pbRootFileEnd, + DWORD dwLocaleMask, + BYTE HighestBitValue) +{ + // Load the locale as-is + ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, false, HighestBitValue); + + // If we wanted enGB, we also load enUS for the missing files + if(dwLocaleMask == CASC_LOCALE_ENGB) + ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_ENUS, false, HighestBitValue); + + if(dwLocaleMask == CASC_LOCALE_PTPT) + ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_PTBR, false, HighestBitValue); + + return ERROR_SUCCESS; +} + +// WoW.exe: 004146C7 (BuildManifest::Load) +static int ParseWowRootFile( + TRootHandler_WoW6 * pRootHandler, + PARSE_ROOT pfnParseRoot, + LPBYTE pbRootFile, + LPBYTE pbRootFileEnd, + DWORD dwLocaleMask) +{ + ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 0); + ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 1); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Implementation of WoW6 root file + +static int WowHandler_Insert( + TRootHandler_WoW6 * pRootHandler, + const char * szFileName, + LPBYTE pbEncodingKey) +{ + PCASC_FILE_ENTRY pFileEntry; + DWORD FileDataId = 0; + + // Don't let the number of items to overflow + if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax) + return ERROR_NOT_ENOUGH_MEMORY; + + // Insert the item to the linear file list + pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1); + if(pFileEntry != NULL) + { + // Get the file data ID of the previous item (0 if this is the first one) + if(pRootHandler->FileTable.ItemCount > 1) + FileDataId = pFileEntry[-1].FileDataId; + + // Fill-in the new entry + pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey; + pFileEntry->FileNameHash = CalcFileNameHash(szFileName); + pFileEntry->FileDataId = FileDataId + 1; + pFileEntry->Locales = CASC_LOCALE_ALL; + + // Verify collisions (debug version only) + assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL); + + // Insert the entry to the map + Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash); + } + + return ERROR_SUCCESS; +} + +static LPBYTE WowHandler_Search(TRootHandler_WoW6 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD PtrLocaleFlags) +{ + PCASC_FILE_ENTRY pFileEntry; + + // Only if we have a listfile + if(pSearch->pCache != NULL) + { + // Keep going through the listfile + while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH)) + { + // Find the root entry + pFileEntry = FindRootEntry(pRootHandler->pRootMap, pSearch->szFileName, NULL); + if(pFileEntry != NULL) + { + // Give the caller the locale mask + if(PtrLocaleFlags != NULL) + PtrLocaleFlags[0] = pFileEntry->Locales; + return pFileEntry->EncodingKey.Value; + } + } + } + + // No more files + return NULL; +} + +static LPBYTE WowHandler_GetKey(TRootHandler_WoW6 * pRootHandler, const char * szFileName) +{ + PCASC_FILE_ENTRY pFileEntry; + DWORD FileDataId; + BYTE FileDataIdLE[4]; + + // Open by FileDataId. The file name must be as following: + // File########.xxx, where '#' are hexa-decimal numbers (case insensitive). + // Extension is ignored in that case + if(IsFileDataIdName(szFileName)) + { + ConvertStringToBinary(szFileName + 4, 8, FileDataIdLE); + FileDataId = ConvertBytesToInteger_4(FileDataIdLE); + + pFileEntry = FindRootEntry(pRootHandler->FileTable, FileDataId); + } + else + { + // Find by the file name hash + pFileEntry = FindRootEntry(pRootHandler->pRootMap, szFileName, NULL); + } + + return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL; +} + +static void WowHandler_EndSearch(TRootHandler_WoW6 * /* pRootHandler */, TCascSearch * pSearch) +{ + if(pSearch->pRootContext != NULL) + CASC_FREE(pSearch->pRootContext); + pSearch->pRootContext = NULL; +} + +static void WowHandler_Close(TRootHandler_WoW6 * pRootHandler) +{ + if(pRootHandler != NULL) + { + Array_Free(&pRootHandler->FileTable); + Map_Free(pRootHandler->pRootMap); + CASC_FREE(pRootHandler); + } +} + +#ifdef _DEBUG +static void TRootHandlerWoW6_Dump( + TCascStorage * hs, + TDumpContext * dc, // Pointer to an opened file + LPBYTE pbRootFile, + DWORD cbRootFile, + const TCHAR * szListFile, + int nDumpLevel) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + CASC_ROOT_BLOCK BlockInfo; + PLISTFILE_MAP pListMap; + QUERY_KEY EncodingKey; + LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; + LPBYTE pbFilePointer; + char szOneLine[0x100]; + DWORD i; + + // Create the listfile map + pListMap = ListFile_CreateMap(szListFile); + + // Dump the root entries + for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; ) + { + // Validate the root block + pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd); + if(pbFilePointer == NULL) + break; + + // Dump the locale block + dump_print(dc, "Flags: %08X Locales: %08X NumberOfFiles: %u\n" + "=========================================================\n", + BlockInfo.pLocaleBlockHdr->Flags, + BlockInfo.pLocaleBlockHdr->Locales, + BlockInfo.pLocaleBlockHdr->NumberOfFiles); + + // Dump the hashes and encoding keys + for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++) + { + // Dump the entry + dump_print(dc, "%08X %08X-%08X %s %s\n", + (DWORD)(BlockInfo.FileDataIds[i]), + (DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20), + (DWORD)(BlockInfo.pRootEntries[i].FileNameHash), + StringFromMD5(BlockInfo.pRootEntries[i].EncodingKey.Value, szOneLine), + ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash)); + + // Find the encoding entry in the encoding table + if(nDumpLevel >= DUMP_LEVEL_ENCODING_FILE) + { + EncodingKey.pbData = BlockInfo.pRootEntries[i].EncodingKey.Value; + EncodingKey.cbData = MD5_HASH_SIZE; + pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL); + CascDumpEncodingEntry(hs, dc, pEncodingEntry, nDumpLevel); + } + } + + // Put extra newline + dump_print(dc, "\n"); + } + + ListFile_FreeMap(pListMap); +} +#endif + +//----------------------------------------------------------------------------- +// Public functions + +int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask) +{ + TRootHandler_WoW6 * pRootHandler; + LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; + int nError; + + // Verify the size + if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK)) + nError = ERROR_FILE_CORRUPT; + + // Allocate the root handler object + hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_WoW6, 1); + if(pRootHandler == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill-in the handler functions + memset(pRootHandler, 0, sizeof(TRootHandler_WoW6)); + pRootHandler->Insert = (ROOT_INSERT)WowHandler_Insert; + pRootHandler->Search = (ROOT_SEARCH)WowHandler_Search; + pRootHandler->EndSearch = (ROOT_ENDSEARCH)WowHandler_EndSearch; + pRootHandler->GetKey = (ROOT_GETKEY)WowHandler_GetKey; + pRootHandler->Close = (ROOT_CLOSE)WowHandler_Close; + +#ifdef _DEBUG + pRootHandler->Dump = TRootHandlerWoW6_Dump; // Support for ROOT file dump +#endif // _DEBUG + + // Count the files that are going to be loaded + ParseWowRootFile(pRootHandler, ParseRoot_CountFiles, pbRootFile, pbRootFileEnd, dwLocaleMask); + pRootHandler->dwTotalFileCount += CASC_EXTRA_FILES; + + // Create linear table that will contain all root items + nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, pRootHandler->dwTotalFileCount); + if(nError != ERROR_SUCCESS) + return nError; + + // Create the map of FileHash ->FileEntry + pRootHandler->pRootMap = Map_Create(pRootHandler->dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash)); + if(pRootHandler->pRootMap == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Parse the root file again and insert all files to the map + ParseWowRootFile(pRootHandler, ParseRoot_AddRootEntries, pbRootFile, pbRootFileEnd, dwLocaleMask); + return ERROR_SUCCESS; +} diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp index 2dbdd470478..9c0f56dc9ba 100644 --- a/dep/CascLib/src/common/Common.cpp +++ b/dep/CascLib/src/common/Common.cpp @@ -99,7 +99,7 @@ void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength) szTarget[cchLength] = 0; } -char * NewStr(const char * szString, size_t nCharsToReserve) +char * CascNewStr(const char * szString, size_t nCharsToReserve) { char * szNewString = NULL; size_t nLength; @@ -118,7 +118,7 @@ char * NewStr(const char * szString, size_t nCharsToReserve) return szNewString; } -wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve) +wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve) { wchar_t * szNewString = NULL; size_t nLength; @@ -137,22 +137,20 @@ wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve) return szNewString; } -TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd) +TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd) { TCHAR * szNewString = NULL; - TCHAR * szStringPtr = NULL; - size_t nLength = (size_t)(pbStringEnd - pbStringBegin); - if(pbStringEnd > pbStringBegin) + // Only if the entry is valid + if(szBegin != NULL && szEnd > szBegin) { - szNewString = szStringPtr = CASC_ALLOC(TCHAR, nLength + 1); + // Allocate and copy the string + szNewString = CASC_ALLOC(TCHAR, (szEnd - szBegin + 1)); if(szNewString != NULL) - { - CopyString(szStringPtr, (const char *)pbStringBegin, nLength); - szStringPtr[nLength] = 0; - } + CopyString(szNewString, szBegin, (szEnd - szBegin)); } + // Return the string return szNewString; } @@ -218,30 +216,60 @@ TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir) return szFullPath; } -void NormalizeFileName_UpperBkSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars) +TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength) { - char * szTrgFileEnd = szTrgFileName + cchMaxChars; - size_t i; + TCHAR * szFullPath = NULL; + TCHAR * szSubDir; - // Normalize the file name: ToLower + BackSlashToSlash - for(i = 0; szSrcFileName[i] != 0 && szTrgFileName < szTrgFileEnd; i++) - szTrgFileName[i] = AsciiToUpperTable_BkSlash[szSrcFileName[i]]; + // Create the subdir string + szSubDir = CASC_ALLOC(TCHAR, nLength + 1); + if(szSubDir != NULL) + { + CopyString(szSubDir, szString, nLength); + szFullPath = CombinePath(szPath, szSubDir); + CASC_FREE(szSubDir); + } - assert(szSrcFileName[i] == 0); - szTrgFileName[i] = 0; + return szFullPath; } -void NormalizeFileName_LowerSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars) +size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars) { - char * szTrgFileEnd = szTrgFileName + cchMaxChars; + char * szNormNameEnd = szNormName + cchMaxChars; size_t i; // Normalize the file name: ToLower + BackSlashToSlash - for(i = 0; szSrcFileName[i] != 0 && szTrgFileName < szTrgFileEnd; i++) - szTrgFileName[i] = AsciiToLowerTable_Slash[szSrcFileName[i]]; + for(i = 0; szFileName[0] != 0 && szNormName < szNormNameEnd; i++) + *szNormName++ = NormTable[*szFileName++]; - assert(szSrcFileName[i] == 0); - szTrgFileName[i] = 0; + // Terminate the string + szNormName[0] = 0; + return i; +} + +size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars) +{ + return NormalizeFileName(AsciiToUpperTable_BkSlash, szNormName, szFileName, cchMaxChars); +} + +size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars) +{ + return NormalizeFileName(AsciiToLowerTable_Slash, szNormName, szFileName, cchMaxChars); +} + +ULONGLONG CalcFileNameHash(const char * szFileName) +{ + char szNormName[MAX_PATH+1]; + uint32_t dwHashHigh = 0; + uint32_t dwHashLow = 0; + size_t nLength; + + // Normalize the file name - convert to uppercase, slashes to backslashes + nLength = NormalizeFileName_UpperBkSlash(szNormName, szFileName, MAX_PATH); + + // Calculate the HASH value of the normalized file name + hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow); + return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; } int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue) @@ -256,6 +284,22 @@ int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue) return (Digit > 0x0F) ? ERROR_BAD_FORMAT : ERROR_SUCCESS; } +int ConvertStringToInt08(const char * szString, PDWORD PtrValue) +{ + BYTE DigitOne = AsciiToUpperTable_BkSlash[szString[0]] - '0'; + BYTE DigitTwo = AsciiToUpperTable_BkSlash[szString[1]] - '0'; + + // Fix the digits + if(DigitOne > 9) + DigitOne -= 'A' - '9' - 1; + if(DigitTwo > 9) + DigitTwo -= 'A' - '9' - 1; + + // Combine them into a value + PtrValue[0] = (DigitOne << 0x04) | DigitTwo; + return (DigitOne <= 0x0F && DigitTwo <= 0x0F) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; +} + int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue) { // The number of digits must be even @@ -290,6 +334,41 @@ int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrVa return ERROR_SUCCESS; } +// Converts string blob to binary blob. +int ConvertStringToBinary( + const char * szString, + size_t nMaxDigits, + LPBYTE pbBinary) +{ + const char * szStringEnd = szString + nMaxDigits; + DWORD dwCounter = 0; + BYTE DigitValue; + BYTE ByteValue = 0; + + // Convert the string + while(szString < szStringEnd) + { + // Retrieve the digit converted to hexa + DigitValue = (BYTE)(AsciiToUpperTable_BkSlash[szString[0]] - '0'); + if(DigitValue > 9) + DigitValue -= 'A' - '9' - 1; + if(DigitValue > 0x0F) + return ERROR_BAD_FORMAT; + + // Insert the digit to the binary buffer + ByteValue = (ByteValue << 0x04) | DigitValue; + dwCounter++; + + // If we reached the second digit, it means that we need + // to flush the byte value and move on + if((dwCounter & 0x01) == 0) + *pbBinary++ = ByteValue; + szString++; + } + + return ERROR_SUCCESS; +} + char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer) { char * szSaveBuffer = szBuffer; @@ -308,6 +387,11 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer) return szSaveBuffer; } +char * StringFromMD5(LPBYTE md5, char * szBuffer) +{ + return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer); +} + //----------------------------------------------------------------------------- // File name utilities @@ -341,87 +425,55 @@ const char * GetPlainFileName(const char * szFileName) bool CheckWildCard(const char * szString, const char * szWildCard) { - const char * szSubString; - int nSubStringLength; - int nMatchCount = 0; + const char * szWildCardPtr; - // When the mask is empty, it never matches - if(szWildCard == NULL || *szWildCard == 0) - return false; - - // If the wildcard contains just "*", then it always matches - if(szWildCard[0] == '*' && szWildCard[1] == 0) - return true; - - // Do normal test for(;;) { // If there is '?' in the wildcard, we skip one char - while(*szWildCard == '?') + while(szWildCard[0] == '?') { + if(szString[0] == 0) + return false; + szWildCard++; szString++; } - // If there is '*', means zero or more chars. We have to - // find the sequence after '*' - if(*szWildCard == '*') + // Handle '*' + szWildCardPtr = szWildCard; + if(szWildCardPtr[0] != 0) { - // More stars is equal to one star - while(*szWildCard == '*' || *szWildCard == '?') - szWildCard++; - - // If we found end of the wildcard, it's a match - if(*szWildCard == 0) - return true; - - // Determine the length of the substring in szWildCard - szSubString = szWildCard; - while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*') - szSubString++; - nSubStringLength = (int)(szSubString - szWildCard); - nMatchCount = 0; - - // Now we have to find a substring in szString, - // that matches the substring in szWildCard - while(*szString != 0) + if(szWildCardPtr[0] == '*') { - // Calculate match count - while(nMatchCount < nSubStringLength) - { - if(AsciiToUpperTable_BkSlash[(BYTE)szString[nMatchCount]] != AsciiToUpperTable_BkSlash[(BYTE)szWildCard[nMatchCount]]) - break; - if(szString[nMatchCount] == 0) - break; - nMatchCount++; - } + szWildCardPtr++; + + if(szWildCardPtr[0] == '*') + continue; + + if(szWildCardPtr[0] == 0) + return true; - // If the match count has reached substring length, we found a match - if(nMatchCount == nSubStringLength) + if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] == AsciiToUpperTable_BkSlash[szString[0]]) { - szWildCard += nMatchCount; - szString += nMatchCount; - break; + if(CheckWildCard(szString, szWildCardPtr)) + return true; } + } + else + { + if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] != AsciiToUpperTable_BkSlash[szString[0]]) + return false; - // No match, move to the next char in szString - nMatchCount = 0; - szString++; + szWildCard = szWildCardPtr + 1; } + + if(szString[0] == 0) + return false; + szString++; } else { - // If we came to the end of the string, compare it to the wildcard - if(AsciiToUpperTable_BkSlash[(BYTE)*szString] != AsciiToUpperTable_BkSlash[(BYTE)*szWildCard]) - return false; - - // If we arrived to the end of the string, it's a match - if(*szString == 0) - return true; - - // Otherwise, continue in comparing - szWildCard++; - szString++; + return (szString[0] == 0) ? true : false; } } } diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h index ae098adb084..484276a3bd7 100644 --- a/dep/CascLib/src/common/Common.h +++ b/dep/CascLib/src/common/Common.h @@ -29,33 +29,31 @@ extern unsigned char AsciiToUpperTable_BkSlash[256]; extern unsigned char IntToHexChar[]; //----------------------------------------------------------------------------- -// GetLastError/SetLastError support for non-Windows platform - -#ifndef PLATFORM_WINDOWS -int GetLastError(); -void SetLastError(int nError); -#endif // PLATFORM_WINDOWS - -//----------------------------------------------------------------------------- // String manipulation void CopyString(char * szTarget, const char * szSource, size_t cchLength); void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength); void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength); -char * NewStr(const char * szString, size_t nCharsToReserve); -wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve); +char * CascNewStr(const char * szString, size_t nCharsToReserve); +wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve); -TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd); +TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd); TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir); +TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength); + +size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); +size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); -void NormalizeFileName_UpperBkSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars); -void NormalizeFileName_LowerSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars); +ULONGLONG CalcFileNameHash(const char * szFileName); int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue); +int ConvertStringToInt08(const char * szString, PDWORD PtrValue); int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue); +int ConvertStringToBinary(const char * szString, size_t nMaxDigits, LPBYTE pbBinary); char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer); +char * StringFromMD5(LPBYTE md5, char * szBuffer); //----------------------------------------------------------------------------- // File name utilities diff --git a/dep/CascLib/src/common/Directory.h b/dep/CascLib/src/common/Directory.h new file mode 100644 index 00000000000..ae97dca3d76 --- /dev/null +++ b/dep/CascLib/src/common/Directory.h @@ -0,0 +1,29 @@ +/*****************************************************************************/ +/* Directory.h Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 30.10.15 1.00 Lad The first version of Directory.h */ +/*****************************************************************************/ + +#ifndef __DIRECTORY_H__ +#define __DIRECTORY_H__ + +//----------------------------------------------------------------------------- +// Scanning a directory + +typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext); + +bool DirectoryExists(const TCHAR * szDirectory); + +int ScanIndexDirectory( + const TCHAR * szIndexPath, + INDEX_FILE_FOUND pfnOnFileFound, + PDWORD IndexArray, + PDWORD OldIndexArray, + void * pvContext + ); + +#endif // __DIRECTORY_H__ diff --git a/dep/CascLib/src/common/DumpContext.cpp b/dep/CascLib/src/common/DumpContext.cpp new file mode 100644 index 00000000000..8783ff201ba --- /dev/null +++ b/dep/CascLib/src/common/DumpContext.cpp @@ -0,0 +1,153 @@ +/*****************************************************************************/ +/* DumpContext.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Implementation of dump context */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 16.03.15 1.00 Lad Created */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static TCHAR * FormatFileName(TCascStorage * hs, const TCHAR * szNameFormat) +{ + TCHAR * szFileName; + TCHAR * szSrc; + TCHAR * szTrg; + + // Create copy of the file name + szFileName = szSrc = szTrg = CascNewStr(szNameFormat, 0); + if(szFileName != NULL) + { + // Format the file name + while(szSrc[0] != 0) + { + if(szSrc[0] == _T('%')) + { + // Replace "%build%" with a build number + if(!_tcsncmp(szSrc, _T("%build%"), 7)) + { + szTrg += _stprintf(szTrg, _T("%u"), hs->dwBuildNumber); + szSrc += 7; + continue; + } + } + + // Just copy the character + *szTrg++ = *szSrc++; + } + + // Terminate the target file name + szTrg[0] = 0; + } + + return szFileName; +} + +//----------------------------------------------------------------------------- +// Public functions + +TDumpContext * CreateDumpContext(TCascStorage * hs, const TCHAR * szNameFormat) +{ + TDumpContext * dc; + TCHAR * szFileName; + + // Validate the storage handle + if(hs != NULL) + { + // Calculate the number of bytes needed for dump context + dc = CASC_ALLOC(TDumpContext, 1); + if(dc != NULL) + { + // Initialize the dump context + memset(dc, 0, sizeof(TDumpContext)); + + // Format the real file name + szFileName = FormatFileName(hs, szNameFormat); + if(szFileName != NULL) + { + // Create the file + dc->pStream = FileStream_CreateFile(szFileName, 0); + if(dc->pStream != NULL) + { + // Initialize buffers + dc->pbBufferBegin = + dc->pbBufferPtr = dc->DumpBuffer; + dc->pbBufferEnd = dc->DumpBuffer + CASC_DUMP_BUFFER_SIZE; + + // Success + CASC_FREE(szFileName); + return dc; + } + + // File create failed --> delete the file name + CASC_FREE(szFileName); + } + + // Free the dump context + CASC_FREE(dc); + } + } + + return NULL; +} + +int dump_print(TDumpContext * dc, const char * szFormat, ...) +{ + va_list argList; + char szBuffer[0x200]; + int nLength = 0; + + // Only if the dump context is valid + if(dc != NULL) + { + // Print the buffer using sprintf + va_start(argList, szFormat); + nLength = vsprintf(szBuffer, szFormat, argList); + assert(nLength < 0x200); + va_end(argList); + + // Copy the string. Replace "\n" with "\n\r" + for(int i = 0; i < nLength; i++) + { + // Flush the buffer, if needed + if((dc->pbBufferPtr + 2) >= dc->pbBufferEnd) + { + FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin)); + dc->pbBufferPtr = dc->pbBufferBegin; + } + + // Copy the char + if(szBuffer[i] == 0x0A) + *dc->pbBufferPtr++ = 0x0D; + *dc->pbBufferPtr++ = szBuffer[i]; + } + } + + return nLength; +} + +int dump_close(TDumpContext * dc) +{ + // Only if the dump context is valid + if(dc != NULL) + { + // Flush the dump context if there are some data + if(dc->pbBufferPtr > dc->pbBufferBegin) + FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin)); + dc->pbBufferPtr = dc->pbBufferBegin; + + // Free the file stream and the entire context + if(dc->pStream != NULL) + FileStream_Close(dc->pStream); + CASC_FREE(dc); + } + + return 0; +} diff --git a/dep/CascLib/src/common/DumpContext.h b/dep/CascLib/src/common/DumpContext.h new file mode 100644 index 00000000000..6f725f5b942 --- /dev/null +++ b/dep/CascLib/src/common/DumpContext.h @@ -0,0 +1,38 @@ +/*****************************************************************************/ +/* DumpContext.h Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Interface for TDumpContext */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 16.03.15 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __DUMP_CONTEXT_H__ +#define __DUMP_CONTEXT_H__ + +//----------------------------------------------------------------------------- +// Defines + +// Size of the buffer for the dump context +#define CASC_DUMP_BUFFER_SIZE 0x10000 + +// Structure for dump context +struct TDumpContext +{ + TFileStream * pStream; // Pointer to the open stream + LPBYTE pbBufferBegin; // Begin of the dump buffer + LPBYTE pbBufferPtr; // Current dump buffer pointer + LPBYTE pbBufferEnd; // End of the dump buffer + + BYTE DumpBuffer[CASC_DUMP_BUFFER_SIZE]; // Dump buffer +}; + +//----------------------------------------------------------------------------- +// Dump context functions + +TDumpContext * CreateDumpContext(struct _TCascStorage * hs, const TCHAR * szNameFormat); +int dump_print(TDumpContext * dc, const char * szFormat, ...); +int dump_close(TDumpContext * dc); + +#endif // __DUMP_CONTEXT_H__ diff --git a/dep/CascLib/src/common/DynamicArray.cpp b/dep/CascLib/src/common/DynamicArray.cpp new file mode 100644 index 00000000000..e744fbe3912 --- /dev/null +++ b/dep/CascLib/src/common/DynamicArray.cpp @@ -0,0 +1,101 @@ +/*****************************************************************************/ +/* DynamicArray.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Description: */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 30.10.15 1.00 Lad The first version of DynamicArray.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static bool EnlargeArray(PDYNAMIC_ARRAY pArray, size_t NewItemCount) +{ + char * NewItemArray; + size_t ItemCountMax; + + // We expect the array to be already allocated + assert(pArray->ItemArray != NULL); + assert(pArray->ItemCountMax != 0); + + // Shall we enlarge the table? + if(NewItemCount > pArray->ItemCountMax) + { + // Calculate new table size + ItemCountMax = pArray->ItemCountMax; + while(ItemCountMax < NewItemCount) + ItemCountMax = ItemCountMax << 1; + + // Allocate new table + NewItemArray = CASC_REALLOC(char, pArray->ItemArray, pArray->ItemSize * ItemCountMax); + if(NewItemArray == NULL) + return false; + + // Set the new table size + pArray->ItemCountMax = ItemCountMax; + pArray->ItemArray = NewItemArray; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Public functions + +int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax) +{ + pArray->ItemArray = CASC_ALLOC(char, (ItemSize * ItemCountMax)); + if(pArray->ItemArray == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + pArray->ItemCountMax = ItemCountMax; + pArray->ItemCount = 0; + pArray->ItemSize = ItemSize; + return ERROR_SUCCESS; +} + +void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount) +{ + char * NewItemPtr; + + // Try to enlarge the buffer, if needed + if(!EnlargeArray(pArray, pArray->ItemCount + NewItemCount)) + return NULL; + NewItemPtr = pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize); + + // Copy the old item(s), if any + if(NewItems != NULL) + memcpy(NewItemPtr, NewItems, (NewItemCount * pArray->ItemSize)); + + // Increment the size of the array + pArray->ItemCount += NewItemCount; + return NewItemPtr; +} + +void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex) +{ + assert(ItemIndex < pArray->ItemCount); + return pArray->ItemArray + (ItemIndex * pArray->ItemSize); +} + +size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr) +{ + char * ArrayItem = (char *)ArrayPtr; + + assert(pArray->ItemArray <= ArrayItem && ArrayItem <= pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize)); + return ((ArrayItem - pArray->ItemArray) / pArray->ItemSize); +} + +void Array_Free(PDYNAMIC_ARRAY pArray) +{ + if(pArray != NULL && pArray->ItemArray != NULL) + { + CASC_FREE(pArray->ItemArray); + } +} diff --git a/dep/CascLib/src/common/DynamicArray.h b/dep/CascLib/src/common/DynamicArray.h new file mode 100644 index 00000000000..11cefacdcc4 --- /dev/null +++ b/dep/CascLib/src/common/DynamicArray.h @@ -0,0 +1,37 @@ +/*****************************************************************************/ +/* DynamicArray.h Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Common array implementation */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 30.10.15 1.00 Lad The first version of DynamicArray.h */ +/*****************************************************************************/ + +#ifndef __DYNAMIC_ARRAY_H__ +#define __DYNAMIC_ARRAY_H__ + +//----------------------------------------------------------------------------- +// Structures + +typedef struct _DYNAMIC_ARRAY +{ + char * ItemArray; // Pointer to items + size_t ItemCountMax; // Current number of items + size_t ItemCount; // Total size of the buffer + size_t ItemSize; // Size of the single item + +} DYNAMIC_ARRAY, *PDYNAMIC_ARRAY; + +//----------------------------------------------------------------------------- +// Functions for managing the array + +int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax); +void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount); +void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex); +size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr); +void Array_Free(PDYNAMIC_ARRAY pArray); + +#define Array_Create(pArray, type, ItemCountMax) Array_Create_(pArray, sizeof(type), ItemCountMax) + +#endif // __DYNAMIC_ARRAY__ diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp index 8e541b44fca..ef9cc55d7ea 100644 --- a/dep/CascLib/src/common/FileStream.cpp +++ b/dep/CascLib/src/common/FileStream.cpp @@ -16,7 +16,6 @@ #define __CASCLIB_SELF__ #include "../CascLib.h" #include "../CascCommon.h" -#include "FileStream.h" #ifdef _MSC_VER #pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp index 42131a2bc2c..b9915f0f31c 100644 --- a/dep/CascLib/src/common/ListFile.cpp +++ b/dep/CascLib/src/common/ListFile.cpp @@ -13,117 +13,118 @@ #include "../CascCommon.h" //----------------------------------------------------------------------------- -// Listfile entry structure +// Listfile cache structure -#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer +typedef struct _LISTFILE_CACHE +{ + char * pBegin; // The begin of the listfile cache + char * pPos; // Current position in the cache + char * pEnd; // The last character in the file cache -typedef bool (*RELOAD_CACHE)(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead); -typedef void (*CLOSE_STREAM)(void * pvCacheContext); + // Followed by the cache (variable length) -struct TListFileCache -{ - RELOAD_CACHE pfnReloadCache; // Function for reloading the cache - CLOSE_STREAM pfnCloseStream; // Function for closing the stream - void * pvCacheContext; // Reload context passed to reload function - char * szMask; // Self-relative pointer to file mask - DWORD dwFileSize; // Total size of the cached file - DWORD dwFilePos; // Position of the cache in the file - BYTE * pBegin; // The begin of the listfile cache - BYTE * pPos; - BYTE * pEnd; // The last character in the file cache - - BYTE Buffer[CACHE_BUFFER_SIZE]; -// char MaskBuff[1] // Followed by the name mask (if any) -}; +} LISTFILE_CACHE, *PLISTFILE_CACHE; //----------------------------------------------------------------------------- -// Local functions +// Creating the listfile cache for the given amount of data -static bool ReloadCache_ExternalFile(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead) +static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize) { - TFileStream * pStream = (TFileStream *)pvCacheContext; + PLISTFILE_CACHE pCache; + + // Allocate cache for one file block + pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize); + if(pCache != NULL) + { + // Set the initial pointers + pCache->pBegin = + pCache->pPos = (char *)(pCache + 1); + pCache->pEnd = pCache->pBegin + dwFileSize; + } - return FileStream_Read(pStream, NULL, pbBuffer, dwBytesToRead); + // Return the cache + return pCache; } -static void CloseStream_ExternalFile(void * pvCacheContext) +//----------------------------------------------------------------------------- +// Functions for parsing an external listfile + +void * ListFile_OpenExternal(const TCHAR * szListFile) { - TFileStream * pStream = (TFileStream *)pvCacheContext; + PLISTFILE_CACHE pCache = NULL; + TFileStream * pStream; + ULONGLONG FileSize = 0; + + // Open the external listfile + pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY); + if(pStream != NULL) + { + // Retrieve the size of the external listfile + FileStream_GetSize(pStream, &FileSize); + if(0 < FileSize && FileSize <= 0x30000000) + { + // Create the in-memory cache for the entire listfile + // The listfile does not have any data loaded yet + pCache = CreateListFileCache((DWORD)FileSize); + if(pCache != NULL) + { + if(!FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize)) + { + ListFile_Free(pCache); + pCache = NULL; + } + } + } - return FileStream_Close(pStream); + // Close the file stream + FileStream_Close(pStream); + } + + return pCache; } +void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer) +{ + PLISTFILE_CACHE pCache = NULL; + + // Create the in-memory cache for the entire listfile + // The listfile does not have any data loaded yet + pCache = CreateListFileCache(cbBuffer); + if(pCache != NULL) + memcpy(pCache->pBegin, pbBuffer, cbBuffer); -// Reloads the cache. Returns number of characters -// that has been loaded into the cache. -static DWORD ReloadListFileCache(TListFileCache * pCache) + return pCache; +} + +// Performs the MD5-based check on the listfile +bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5) { - DWORD dwBytesToRead = 0; + PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; - // Only do something if the cache is empty - if(pCache->pPos >= pCache->pEnd) - { - // Move the file position forward - pCache->dwFilePos += CACHE_BUFFER_SIZE; - if(pCache->dwFilePos >= pCache->dwFileSize) - return 0; - - // Get the number of bytes remaining - dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos; - if(dwBytesToRead > CACHE_BUFFER_SIZE) - dwBytesToRead = CACHE_BUFFER_SIZE; - - // Load the next data chunk to the cache - // If we didn't read anything, it might mean that the block - // of the file is not available - // We stop reading the file at this point, because the rest - // of the listfile is unreliable - if(!pCache->pfnReloadCache(pCache->pvCacheContext, pCache->Buffer, dwBytesToRead)) - return 0; - - // Set the buffer pointers - pCache->pBegin = - pCache->pPos = &pCache->Buffer[0]; - pCache->pEnd = pCache->pBegin + dwBytesToRead; - } + // Must be at the beginning + assert(pCache->pPos == pCache->pBegin); - return dwBytesToRead; + // Verify the MD5 hash for the entire block + return VerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5); } -static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nMaxChars) +size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd) { - char * szLineBegin = szLine; - char * szLineEnd = szLine + nMaxChars - 1; + PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; char * szExtraString = NULL; + char * szLineBegin; + char * szLineEnd; // Skip newlines, spaces, tabs and another non-printable stuff - for(;;) - { - // If we need to reload the cache, do it - if(pCache->pPos == pCache->pEnd) - { - if(ReloadListFileCache(pCache) == 0) - break; - } - - // If we found a non-whitespace character, stop - if(pCache->pPos[0] > 0x20) - break; - - // Skip the character + while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20) pCache->pPos++; - } + + // Remember the begin of the line + szLineBegin = pCache->pPos; // Copy the remaining characters - while(szLine < szLineEnd) + while(pCache->pPos < pCache->pEnd) { - // If we need to reload the cache, do it now and resume copying - if(pCache->pPos == pCache->pEnd) - { - if(ReloadListFileCache(pCache) == 0) - break; - } - // If we have found a newline, stop loading if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A) break; @@ -131,119 +132,64 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nM // Blizzard listfiles can also contain information about patch: // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326) if(pCache->pPos[0] == '~') - szExtraString = szLine; + szExtraString = pCache->pPos; - // Copy the character - *szLine++ = *pCache->pPos++; + // Move the position by one character forward + pCache->pPos++; } - // Terminate line with zero - *szLine = 0; + // Remember the end of the line + szLineEnd = (szExtraString != NULL && szExtraString[0] == '~' && szExtraString[1] == 'P') ? szExtraString : pCache->pPos; - // If there was extra string after the file name, clear it - if(szExtraString != NULL) - { - if(szExtraString[0] == '~' && szExtraString[1] == 'P') - { - szLine = szExtraString; - *szExtraString = 0; - } - } - - // Return the length of the line - return (szLine - szLineBegin); + // Give the caller the positions of the begin and end of the line + pszLineBegin[0] = szLineBegin; + pszLineEnd[0] = szLineEnd; + return (size_t)(szLineEnd - szLineBegin); } -static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_STREAM pfnCloseStream, void * pvCacheContext, DWORD dwFileSize) +size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars) { - TListFileCache * pCache = NULL; - DWORD dwBytesToRead; - - // Allocate cache for one file block - pCache = (TListFileCache *)CASC_ALLOC(BYTE, sizeof(TListFileCache)); - if(pCache != NULL) - { - // Clear the entire structure - memset(pCache, 0, sizeof(TListFileCache)); - pCache->pfnReloadCache = pfnReloadCache; - pCache->pfnCloseStream = pfnCloseStream; - pCache->pvCacheContext = pvCacheContext; - pCache->dwFileSize = dwFileSize; - - // Load the file cache from the file - dwBytesToRead = CASCLIB_MIN(CACHE_BUFFER_SIZE, dwFileSize); - if(pfnReloadCache(pvCacheContext, pCache->Buffer, dwBytesToRead)) - { - // Allocate pointers - pCache->pBegin = pCache->pPos = &pCache->Buffer[0]; - pCache->pEnd = pCache->pBegin + dwBytesToRead; - } - else - { - ListFile_Free(pCache); - pCache = NULL; - } - } - - // Return the cache - return pCache; -} - -//----------------------------------------------------------------------------- -// Functions for parsing an external listfile + const char * szLineBegin = NULL; + const char * szLineEnd = NULL; + size_t nLength; -void * ListFile_OpenExternal(const TCHAR * szListFile) -{ - TListFileCache * pCache; - TFileStream * pStream; - ULONGLONG FileSize = 0; + // Retrieve the next line + nLength = ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd); - // Open the external listfile - pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY); - if(pStream != NULL) + // Check the length + if(nLength > nMaxChars) { - // Retrieve the size of the external listfile - FileStream_GetSize(pStream, &FileSize); - if(0 < FileSize && FileSize <= 0xFFFFFFFF) - { - // Create the cache for the listfile - pCache = CreateListFileCache(ReloadCache_ExternalFile, CloseStream_ExternalFile, pStream, (DWORD)FileSize); - if(pCache != NULL) - return pCache; - } - - // Close the file stream - FileStream_Close(pStream); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; } - return NULL; + // Copy the line to the user buffer + memcpy(szBuffer, szLineBegin, nLength); + szBuffer[nLength] = 0; + return nLength; } size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars) { - TListFileCache * pCache = (TListFileCache *)pvListFile; size_t nLength = 0; - int nError = ERROR_INVALID_PARAMETER; + int nError = ERROR_SUCCESS; // Check for parameters - if(pCache != NULL) + for(;;) { - for(;;) + // Read the (next) line + nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars); + if(nLength == 0) { - // Read the (next) line - nLength = ReadListFileLine(pCache, szBuffer, nMaxChars); - if(nLength == 0) - { - nError = ERROR_NO_MORE_FILES; - break; - } + nError = ERROR_NO_MORE_FILES; + break; + } - // If some mask entered, check it - if(CheckWildCard(szBuffer, szMask)) - { - nError = ERROR_SUCCESS; - break; - } + // If some mask entered, check it + if(CheckWildCard(szBuffer, szMask)) + { + nError = ERROR_SUCCESS; + break; } } @@ -254,14 +200,9 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, void ListFile_Free(void * pvListFile) { - TListFileCache * pCache = (TListFileCache *)pvListFile; - - // Valid parameter check - if(pCache != NULL) + if(pvListFile != NULL) { - if(pCache->pfnCloseStream != NULL) - pCache->pfnCloseStream(pCache->pvCacheContext); - CASC_FREE(pCache); + CASC_FREE(pvListFile); } } @@ -293,11 +234,8 @@ static PLISTFILE_MAP ListMap_Create() static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szFileName, size_t nLength) { PLISTFILE_ENTRY pListEntry; - char szFileName2[MAX_PATH+1]; size_t cbToAllocate; size_t cbEntrySize; - uint32_t dwHashHigh = 0; - uint32_t dwHashLow = 0; // Make sure there is enough space in the list map cbEntrySize = sizeof(LISTFILE_ENTRY) + nLength; @@ -314,14 +252,10 @@ static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szF // Get the pointer to the first entry pListEntry = (PLISTFILE_ENTRY)((LPBYTE)(pListMap + 1) + pListMap->cbBuffer); - - // Get the name hash - NormalizeFileName_UpperBkSlash(szFileName2, szFileName, MAX_PATH); - hashlittle2(szFileName2, nLength, &dwHashHigh, &dwHashLow); - - // Calculate the HASH value of the normalized file name - pListEntry->FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; + pListEntry->FileNameHash = CalcFileNameHash(szFileName); pListEntry->cbEntrySize = (DWORD)cbEntrySize; + + // Copy the file name to the entry memcpy(pListEntry->szFileName, szFileName, nLength); pListEntry->szFileName[nLength] = 0; @@ -357,7 +291,7 @@ static PLISTFILE_MAP ListMap_Finish(PLISTFILE_MAP pListMap) pbEntry += pListEntry->cbEntrySize; // Insert the entry to the map - Map_InsertObject(pMap, pListEntry); + Map_InsertObject(pMap, pListEntry, &pListEntry->FileNameHash); } return pListMap; @@ -408,7 +342,7 @@ const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash) PLISTFILE_ENTRY pListEntry = NULL; if(pListMap != NULL) - pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash); + pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash, NULL); return (pListEntry != NULL) ? pListEntry->szFileName : ""; } diff --git a/dep/CascLib/src/common/ListFile.h b/dep/CascLib/src/common/ListFile.h index 9815160e1ea..84bec3ed751 100644 --- a/dep/CascLib/src/common/ListFile.h +++ b/dep/CascLib/src/common/ListFile.h @@ -37,6 +37,10 @@ typedef struct _LISTFILE_MAP // Functions for parsing an external listfile void * ListFile_OpenExternal(const TCHAR * szListFile); +void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer); +bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5); +size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd); +size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars); size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars); void ListFile_Free(void * pvListFile); diff --git a/dep/CascLib/src/common/Map.cpp b/dep/CascLib/src/common/Map.cpp index 70697a158ab..30ae8ea0531 100644 --- a/dep/CascLib/src/common/Map.cpp +++ b/dep/CascLib/src/common/Map.cpp @@ -15,51 +15,82 @@ //----------------------------------------------------------------------------- // Local functions -static DWORD CalcHashIndex(PCASC_MAP pMap, void * pvIdentifier) +// Returns the extension, right after "." +static const char * String_GetExtension(const char * szString) { + const char * szExtension = strrchr(szString, '.'); + return (szExtension != NULL) ? szExtension + 1 : NULL; +} + +static DWORD CalcHashIndex_Key(PCASC_MAP pMap, void * pvKey) +{ + LPBYTE pbKey = (LPBYTE)pvKey; DWORD dwHash = 0x7EEE7EEE; - // Is it a string table? - if(pMap->KeyLength == KEY_LENGTH_STRING) - { - char * szString = (char *)pvIdentifier; + // Construct the hash from the first 8 digits + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[0]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[1]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[2]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[3]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[4]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[5]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[6]; + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[7]; - for(size_t i = 0; szString[i] != 0; i++) - dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[i]; - } - else - { - LPBYTE pbHash = (LPBYTE)pvIdentifier; + // Return the hash limited by the table size + return (dwHash % pMap->TableSize); +} + +static DWORD CalcHashIndex_String(PCASC_MAP pMap, const char * szString, const char * szStringEnd) +{ + LPBYTE pbKeyEnd = (LPBYTE)szStringEnd; + LPBYTE pbKey = (LPBYTE)szString; + DWORD dwHash = 0x7EEE7EEE; - // Construct the hash from the first 4 digits - for(size_t i = 0; i < pMap->KeyLength; i++) - dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbHash[i]; + // Hash the string itself + while(pbKey < pbKeyEnd) + { + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]]; + pbKey++; } // Return the hash limited by the table size return (dwHash % pMap->TableSize); } -static bool CompareIdentifier(PCASC_MAP pMap, void * pvTableEntry, void * pvIdentifier) +static bool CompareObject_Key(PCASC_MAP pMap, void * pvObject, void * pvKey) { - // Is it a string table? - if(pMap->KeyLength == KEY_LENGTH_STRING) - { - char * szTableEntry = (char *)pvTableEntry; - char * szIdentifier = (char *)pvIdentifier; + LPBYTE pbObjectKey = (LPBYTE)pvObject + pMap->KeyOffset; - return (strcmp(szTableEntry, szIdentifier) == 0); - } - else + return (memcmp(pbObjectKey, pvKey, pMap->KeyLength) == 0); +} + +static bool CompareObject_String( + PCASC_MAP pMap, + const char * szExistingString, + const char * szString, + const char * szStringEnd) +{ + // Keep compiler happy + CASCLIB_UNUSED(pMap); + + // Compare the whole part, case insensitive + while(szString < szStringEnd) { - return (memcmp(pvTableEntry, pvIdentifier, pMap->KeyLength) == 0); + if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString]) + return false; + + szExistingString++; + szString++; } + + return true; } //----------------------------------------------------------------------------- // Public functions -PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset) +PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset) { PCASC_MAP pMap; size_t cbToAllocate; @@ -76,7 +107,7 @@ PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset) memset(pMap, 0, cbToAllocate); pMap->KeyLength = dwKeyLength; pMap->TableSize = dwTableSize; - pMap->MemberOffset = dwMemberOffset; + pMap->KeyOffset = dwKeyOffset; } // Return the allocated map @@ -104,24 +135,28 @@ size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray) return pMap->ItemCount; } -void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier) +void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex) { - void * pvTableEntry; + void * pvObject; DWORD dwHashIndex; // Verify pointer to the map if(pMap != NULL) { // Construct the main index - dwHashIndex = CalcHashIndex(pMap, pvIdentifier); + dwHashIndex = CalcHashIndex_Key(pMap, pvKey); while(pMap->HashTable[dwHashIndex] != NULL) { // Get the pointer at that position - pvTableEntry = pMap->HashTable[dwHashIndex]; + pvObject = pMap->HashTable[dwHashIndex]; // Compare the hash - if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier)) - return ((LPBYTE)pvTableEntry - pMap->MemberOffset); + if(CompareObject_Key(pMap, pvObject, pvKey)) + { + if(PtrIndex != NULL) + PtrIndex[0] = dwHashIndex; + return pvObject; + } // Move to the next entry dwHashIndex = (dwHashIndex + 1) % pMap->TableSize; @@ -132,9 +167,47 @@ void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier) return NULL; } -bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier) +bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey) +{ + void * pvExistingObject; + DWORD dwHashIndex; + + // Verify pointer to the map + if(pMap != NULL) + { + // Limit check + if((pMap->ItemCount + 1) >= pMap->TableSize) + return false; + + // Construct the hash index + dwHashIndex = CalcHashIndex_Key(pMap, pvKey); + while(pMap->HashTable[dwHashIndex] != NULL) + { + // Get the pointer at that position + pvExistingObject = pMap->HashTable[dwHashIndex]; + + // Check if hash being inserted conflicts with an existing hash + if(CompareObject_Key(pMap, pvExistingObject, pvKey)) + return false; + + // Move to the next entry + dwHashIndex = (dwHashIndex + 1) % pMap->TableSize; + } + + // Insert at that position + pMap->HashTable[dwHashIndex] = pvNewObject; + pMap->ItemCount++; + return true; + } + + // Failed + return false; +} + +bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension) { - void * pvTableEntry; + const char * szExistingString; + const char * szStringEnd = NULL; DWORD dwHashIndex; // Verify pointer to the map @@ -144,15 +217,21 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier) if((pMap->ItemCount + 1) >= pMap->TableSize) return false; + // Retrieve the length of the string without extension + if(bCutExtension) + szStringEnd = String_GetExtension(szString); + if(szStringEnd == NULL) + szStringEnd = szString + strlen(szString); + // Construct the hash index - dwHashIndex = CalcHashIndex(pMap, pvIdentifier); + dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd); while(pMap->HashTable[dwHashIndex] != NULL) { // Get the pointer at that position - pvTableEntry = pMap->HashTable[dwHashIndex]; + szExistingString = (const char *)pMap->HashTable[dwHashIndex]; // Check if hash being inserted conflicts with an existing hash - if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier)) + if(CompareObject_String(pMap, szExistingString, szString, szStringEnd)) return false; // Move to the next entry @@ -160,7 +239,7 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier) } // Insert at that position - pMap->HashTable[dwHashIndex] = pvIdentifier; + pMap->HashTable[dwHashIndex] = (void *)szString; pMap->ItemCount++; return true; } @@ -169,6 +248,34 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier) return false; } +const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd) +{ + const char * szExistingString; + DWORD dwHashIndex; + + // Verify pointer to the map + if(pMap != NULL) + { + // Construct the main index + dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd); + while(pMap->HashTable[dwHashIndex] != NULL) + { + // Get the pointer at that position + szExistingString = (const char *)pMap->HashTable[dwHashIndex]; + + // Compare the hash + if(CompareObject_String(pMap, szExistingString, szString, szStringEnd)) + return szExistingString; + + // Move to the next entry + dwHashIndex = (dwHashIndex + 1) % pMap->TableSize; + } + } + + // Not found, sorry + return NULL; +} + void Map_Free(PCASC_MAP pMap) { if(pMap != NULL) diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h index 40ea4238b81..7b0c1321e6c 100644 --- a/dep/CascLib/src/common/Map.h +++ b/dep/CascLib/src/common/Map.h @@ -20,20 +20,23 @@ typedef struct _CASC_MAP { size_t TableSize; size_t ItemCount; // Number of items in the map - size_t MemberOffset; // How far is the hash from the begin of the structure (in bytes) + size_t KeyOffset; // How far is the hash from the begin of the structure (in bytes) size_t KeyLength; // Length of the hash key void * HashTable[1]; // Pointer table } CASC_MAP, *PCASC_MAP; +typedef bool (*MAP_COMPARE)(PCASC_MAP pMap, void * pvObject, void * pvKey); + //----------------------------------------------------------------------------- // Functions -PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset); +PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset); size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray); -void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier); -bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier); -void Map_Sort(PCASC_MAP pMap); +void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex); +bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey); +const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd); +bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension); void Map_Free(PCASC_MAP pMap); #endif // __HASHTOPTR_H__ diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp new file mode 100644 index 00000000000..df9953f3ea6 --- /dev/null +++ b/dep/CascLib/src/common/RootHandler.cpp @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* RootHandler.cpp Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Implementation of root handler */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 09.03.15 1.00 Lad Created */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Common support + +int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey) +{ + if(pRootHandler == NULL || pRootHandler->Insert == NULL) + return ERROR_NOT_SUPPORTED; + + return pRootHandler->Insert(pRootHandler, szFileName, pbEncodingKey); +} + +LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags) +{ + // Check if the root structure is valid at all + if(pRootHandler == NULL) + return NULL; + + return pRootHandler->Search(pRootHandler, pSearch, PtrFileSize, PtrLocaleFlags); +} + +void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch) +{ + // Check if the root structure is valid at all + if(pRootHandler != NULL) + { + pRootHandler->EndSearch(pRootHandler, pSearch); + } +} + +LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName) +{ + // Check if the root structure is valid at all + if(pRootHandler == NULL) + return NULL; + + return pRootHandler->GetKey(pRootHandler, szFileName); +} + +void RootHandler_Dump(TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel) +{ + TDumpContext * dc; + + // Only if the ROOT provider suports the dump option + if(hs->pRootHandler != NULL && hs->pRootHandler->Dump != NULL) + { + // Create the dump file + dc = CreateDumpContext(hs, szNameFormat); + if(dc != NULL) + { + // Dump the content and close the file + hs->pRootHandler->Dump(hs, dc, pbRootHandler, cbRootHandler, szListFile, nDumpLevel); + dump_close(dc); + } + } +} + +void RootHandler_Close(TRootHandler * pRootHandler) +{ + // Check if the root structure is allocated at all + if(pRootHandler != NULL) + { + pRootHandler->Close(pRootHandler); + } +} diff --git a/dep/CascLib/src/common/RootHandler.h b/dep/CascLib/src/common/RootHandler.h new file mode 100644 index 00000000000..e1869e351cc --- /dev/null +++ b/dep/CascLib/src/common/RootHandler.h @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* RootHandler.h Copyright (c) Ladislav Zezula 2015 */ +/*---------------------------------------------------------------------------*/ +/* Interface for root handlers */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 09.03.15 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __ROOT_HANDLER_H__ +#define __ROOT_HANDLER_H__ + +//----------------------------------------------------------------------------- +// Defines + +#define CASC_MNDX_ROOT_SIGNATURE 0x58444E4D // 'MNDX' +#define CASC_DIABLO3_ROOT_SIGNATURE 0x8007D0C4 +#define CASC_OVERWATCH_ROOT_SIGNATURE 0x35444D23 // '#MD5' + +#define ROOT_FLAG_HAS_NAMES 0x00000001 // The root file contains file names + +#define DUMP_LEVEL_ROOT_FILE 1 // Dump root file +#define DUMP_LEVEL_ENCODING_FILE 2 // Dump root file + encoding file +#define DUMP_LEVEL_INDEX_ENTRIES 3 // Dump root file + encoding file + index entries + +//----------------------------------------------------------------------------- +// Root file function prototypes + +typedef int (*ROOT_INSERT)( + struct TRootHandler * pRootHandler, // Pointer to an initialized root handler + const char * szFileName, // Pointer to the file name + LPBYTE pbEncodingKey // Pointer to the encoding key of the file name + ); + +typedef LPBYTE (*ROOT_SEARCH)( + struct TRootHandler * pRootHandler, // Pointer to an initialized root handler + struct _TCascSearch * pSearch, // Pointer to the initialized search structure + PDWORD PtrFileSize, // Pointer to receive file size (optional) + PDWORD PtrLocaleFlags // Pointer to receive locale flags (optional) + ); + +typedef void (*ROOT_ENDSEARCH)( + struct TRootHandler * pRootHandler, // Pointer to an initialized root handler + struct _TCascSearch * pSearch // Pointer to the initialized search structure + ); + +typedef LPBYTE (*ROOT_GETKEY)( + struct TRootHandler * pRootHandler, // Pointer to an initialized root handler + const char * szFileName // Pointer to the name of a file + ); + +typedef void (*ROOT_DUMP)( + struct _TCascStorage * hs, // Pointer to the open storage + TDumpContext * dc, // Opened dump context + LPBYTE pbRootHandler, // Pointer to the loaded ROOT file + DWORD cbRootHandler, // Length of the loaded ROOT file + const TCHAR * szListFile, + int nDumpLevel + ); + +typedef void (*ROOT_CLOSE)( + struct TRootHandler * pRootHandler // Pointer to an initialized root handler + ); + +struct TRootHandler +{ + ROOT_INSERT Insert; // Inserts an existing file name + ROOT_SEARCH Search; // Performs the root file search + ROOT_ENDSEARCH EndSearch; // Performs cleanup after searching + ROOT_GETKEY GetKey; // Retrieves encoding key for a file name + ROOT_DUMP Dump; + ROOT_CLOSE Close; // Closing the root file + + DWORD dwRootFlags; // Root flags - see the ROOT_FLAG_XXX +}; + +//----------------------------------------------------------------------------- +// Public functions + +int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey); +LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags); +void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch); +LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName); +void RootHandler_Dump(struct _TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel); +void RootHandler_Close(TRootHandler * pRootHandler); + +#endif // __ROOT_HANDLER_H__ diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c index 537516d80d9..1cd875f5260 100644 --- a/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c +++ b/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c @@ -19,8 +19,8 @@ #if (ARGTYPE == 0) void crypt_argchk(char *v, char *s, int d) { - fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", - v, d, s); + fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", + v, d, s); (void)raise(SIGABRT); } #endif diff --git a/dep/PackageList.txt b/dep/PackageList.txt index f7ae2c36e3f..5ad9cef5982 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -46,7 +46,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014) https://github.com/ladislav-zezula/CascLib - Version: d1d617d4feecd39bae049e19b0e217a1a84bedc6 + Version: 919a2d670cb749c501ee15887a88e9b9a538961b zmqpp (C++ binding for 0mq/zmq is a 'high-level' library that hides most of the c-style interface core 0mq provides.) https://github.com/zeromq/zmqpp diff --git a/issue_template.md b/issue_template.md new file mode 100644 index 00000000000..87f2e21af41 --- /dev/null +++ b/issue_template.md @@ -0,0 +1,23 @@ +**Description**: + +**Current behaviour**: Tell us what happens + +**Expected behaviour**: Tell us what should happen instead + +**Steps to reproduce the problem**: + +1. +2. +3. + +**Branch(es)**: 335/6x + +**TC hash/commit**: + +**TDB version**: + +**Operating system**: + + +[//]: # (This template is for problem reports, for other type of reports edit it accordingly) +[//]: # (If this is a crash report, include the crashlog with https://gist.github.com/) diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000000..7438ed561cf --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,13 @@ +**Changes proposed**: + +- +- +- + +**Target branch(es)**: 335/6x + +**Issues addressed**: Fixes # + +**Tests performed**: (Does it build? Tested in-game?) + +**Known issues and TODO list**: diff --git a/sql/updates/world/2015_01_16_00_world_2015_12_14_00.sql b/sql/updates/world/2016_01_16_00_world_2015_12_14_00.sql index 85faf4448e2..85faf4448e2 100644 --- a/sql/updates/world/2015_01_16_00_world_2015_12_14_00.sql +++ b/sql/updates/world/2016_01_16_00_world_2015_12_14_00.sql diff --git a/sql/updates/world/2016_01_16_00_world_2015_12_15_03.sql b/sql/updates/world/2016_01_16_01_world_2015_12_15_03.sql index 99cc9e14646..99cc9e14646 100644 --- a/sql/updates/world/2016_01_16_00_world_2015_12_15_03.sql +++ b/sql/updates/world/2016_01_16_01_world_2015_12_15_03.sql diff --git a/sql/updates/world/2016_01_16_01_world_2015_12_16_00.sql b/sql/updates/world/2016_01_16_02_world_2015_12_16_00.sql index 62d640117f7..62d640117f7 100644 --- a/sql/updates/world/2016_01_16_01_world_2015_12_16_00.sql +++ b/sql/updates/world/2016_01_16_02_world_2015_12_16_00.sql diff --git a/sql/updates/world/2016_01_16_02_world_2015_12_16_01.sql b/sql/updates/world/2016_01_16_03_world_2015_12_16_01.sql index 73c9675e5be..73c9675e5be 100644 --- a/sql/updates/world/2016_01_16_02_world_2015_12_16_01.sql +++ b/sql/updates/world/2016_01_16_03_world_2015_12_16_01.sql diff --git a/sql/updates/world/2016_01_16_03_world_2015_12_17_00.sql b/sql/updates/world/2016_01_16_05_world_2015_12_17_00.sql index 8419473b203..8419473b203 100644 --- a/sql/updates/world/2016_01_16_03_world_2015_12_17_00.sql +++ b/sql/updates/world/2016_01_16_05_world_2015_12_17_00.sql diff --git a/sql/updates/world/2016_01_16_04_world_2015_12_18_01.sql b/sql/updates/world/2016_01_16_06_world_2015_12_18_01.sql index 2ccc93def23..2ccc93def23 100644 --- a/sql/updates/world/2016_01_16_04_world_2015_12_18_01.sql +++ b/sql/updates/world/2016_01_16_06_world_2015_12_18_01.sql diff --git a/sql/updates/world/2016_01_16_05_world_2015_12_18_02.sql b/sql/updates/world/2016_01_16_07_world_2015_12_18_02.sql index f590d18a8ab..f590d18a8ab 100644 --- a/sql/updates/world/2016_01_16_05_world_2015_12_18_02.sql +++ b/sql/updates/world/2016_01_16_07_world_2015_12_18_02.sql diff --git a/sql/updates/world/2016_01_16_06_world_2015_12_18_04.sql b/sql/updates/world/2016_01_16_08_world_2015_12_18_04.sql index c8c3352319c..c8c3352319c 100644 --- a/sql/updates/world/2016_01_16_06_world_2015_12_18_04.sql +++ b/sql/updates/world/2016_01_16_08_world_2015_12_18_04.sql diff --git a/sql/updates/world/2016_01_16_07_world_2015_12_20_00.sql b/sql/updates/world/2016_01_16_09_world_2015_12_20_00.sql index 5f8c6b168ce..5f8c6b168ce 100644 --- a/sql/updates/world/2016_01_16_07_world_2015_12_20_00.sql +++ b/sql/updates/world/2016_01_16_09_world_2015_12_20_00.sql diff --git a/sql/updates/world/2016_01_16_08_world_2015_12_24_00.sql b/sql/updates/world/2016_01_16_10_world_2015_12_24_00.sql index 52d83bb63c3..52d83bb63c3 100644 --- a/sql/updates/world/2016_01_16_08_world_2015_12_24_00.sql +++ b/sql/updates/world/2016_01_16_10_world_2015_12_24_00.sql diff --git a/sql/updates/world/2016_01_27_00_world.sql b/sql/updates/world/2016_01_27_00_world.sql new file mode 100644 index 00000000000..0690f8ceed7 --- /dev/null +++ b/sql/updates/world/2016_01_27_00_world.sql @@ -0,0 +1,6 @@ +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_warr_heroic_leap', 'spell_warr_heroic_leap_jump'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(6544,'spell_warr_heroic_leap'), +(178368,'spell_warr_heroic_leap_jump'); + +UPDATE `creature_template` SET `flags_extra`=`flags_extra`|0x80 WHERE `entry`=47319; diff --git a/sql/updates/world/2016_02_10_00_world.sql b/sql/updates/world/2016_02_10_00_world.sql new file mode 100644 index 00000000000..872861b08dc --- /dev/null +++ b/sql/updates/world/2016_02_10_00_world.sql @@ -0,0 +1,18 @@ +-- Pathing for Kalecgos Entry: 24844 'TDB FORMAT' +SET @NPC := 24844; +SET @PATH := @NPC * 10; +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,163.9735,-398.0906,2.083333,0,0,0,0,100,0), -- 16:16:43 +(@PATH,2,164.3802,-397.1771,2.083333,0,0,0,0,100,0), -- 16:16:43 +(@PATH,3,162.7923,-386.1964,15.67094,0,0,0,0,100,0), -- 16:16:43 +(@PATH,4,151.5555,-345.349,5.92646,0,0,0,0,100,0), -- 16:16:43 +(@PATH,5,162.2416,-299.8032,-5.436685,0,0,0,0,100,0), -- 16:16:43 +(@PATH,6,199.7482,-272.3315,-7.186677,0,0,0,0,100,0), -- 16:16:43 +(@PATH,7,199.7482,-272.3315,-7.186677,0,0,0,0,100,0), -- 16:16:43 +(@PATH,8,199.7482,-272.3315,-7.186677,0.06981317,0,0,0,100,0); -- 16:16:54 +-- 0x1C2F2C4920184300001F1D000038BF6E .go 163.9735 -398.0906 2.083333 + +DELETE FROM `creature_text` WHERE `entry` = 24844 AND `groupid` = 0; +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(24844, 0, 0, 'Be still, mortals, and hearken to my words.', 14, 0, 100, 0, 0, 0, 23936, 3, 'Kalecgos - SAY_KALECGOS_SPAWN'); diff --git a/sql/updates/world/2016_02_10_01_world.sql b/sql/updates/world/2016_02_10_01_world.sql new file mode 100644 index 00000000000..2b8cca7cfb0 --- /dev/null +++ b/sql/updates/world/2016_02_10_01_world.sql @@ -0,0 +1,4 @@ +-- DB/Creature: Ironforge Gryphon Riders Hovering +DELETE FROM `creature_template_addon` WHERE `entry`=51383; +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `auras`) VALUES +(51383, 0, 24688, 50331648, 257, 0, ''); -- 51383 diff --git a/sql/updates/world/2016_02_10_02_world.sql b/sql/updates/world/2016_02_10_02_world.sql new file mode 100644 index 00000000000..967295f1fec --- /dev/null +++ b/sql/updates/world/2016_02_10_02_world.sql @@ -0,0 +1,15 @@ +-- DB/Creature: Loot for NPC Tunneling Worm +UPDATE `creature_template` SET `lootid`= 34865 WHERE `entry`= 34865; +DELETE FROM `creature_loot_template` WHERE `entry`= 34865; +INSERT INTO `creature_loot_template` (`entry`,`item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES +(34865, 55973, 0, 75, 0,1,0,1,1,'Tunneling Worm - Inert Elemental Speck'), +(34865, 55983, 0, 25, 0,1,0,1,1,'Tunneling Worm - Inert Elemental Scintilla'), +(34865, 805, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Small Red Pouch'), +(34865, 828, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Small Blue Pouch'), +(34865, 1411, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Withered Staff'), +(34865, 1431, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Patchwork Pants'), +(34865, 2589, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Linen Cloth'), +(34865, 4469, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Rod of Order - Quest item'), +(34865, 5571, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Small Black Pouch'), +(34865, 5572, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Small Green Pouch'), +(34865, 23333, 0, 0.5, 0,1,0,1,1,'Tunneling Worm - Shattered Power Core'); diff --git a/sql/updates/world/2016_02_10_03_world.sql b/sql/updates/world/2016_02_10_03_world.sql new file mode 100644 index 00000000000..9e53fa3431a --- /dev/null +++ b/sql/updates/world/2016_02_10_03_world.sql @@ -0,0 +1,5 @@ +-- DB/Creature: Nalpak Spawn +SET @CGUID := 452399; +DELETE FROM `creature` WHERE `guid`=@CGUID; +INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES +(@CGUID, 5767, 43, 1, -150.298, 123.82, -75.95126, 3.228859, 7200, 0, 0); -- 5767 (Area: -1) diff --git a/sql/updates/world/2016_02_10_04_world.sql b/sql/updates/world/2016_02_10_04_world.sql new file mode 100644 index 00000000000..00397623b90 --- /dev/null +++ b/sql/updates/world/2016_02_10_04_world.sql @@ -0,0 +1,4 @@ +-- +UPDATE `quest_template` SET `AllowableRaces`=0 WHERE `id` IN (13094, 13095); +UPDATE `quest_template` SET `AllowableRaces`=`AllowableRaces`|1|4|8|64|1024|2097152|16777216 WHERE id=13094; +UPDATE `quest_template` SET `AllowableRaces`=`AllowableRaces`|2|16|32|128|256|512|33554432 WHERE id=13095; diff --git a/sql/updates/world/2016_02_11_00_world.sql b/sql/updates/world/2016_02_11_00_world.sql new file mode 100644 index 00000000000..929f34154de --- /dev/null +++ b/sql/updates/world/2016_02_11_00_world.sql @@ -0,0 +1,11 @@ +-- +DELETE FROM `spell_target_position` WHERE `ID` IN (159895,159896,159897,159898,159899,159900,159901,159902); +INSERT INTO `spell_target_position` (`ID`,`EffectIndex`,`MapID`,`PositionX`,`PositionY`,`PositionZ`,`VerifiedBuild`) VALUES +(159895, 0, 1116, 7263.71, 4453.39, 129.221, 20779), -- Path of the Bloodmaul +(159896, 0, 1116, 8850.58, 1370.09, 97.097, 20779), -- Path of the Iron Prow +(159897, 0, 1116, 1489.18, 3075.6, 109.725, 20779), -- Path of the Vigilant +(159898, 0, 1116, 32.4602, 2527.42, 103.606, 20779), -- Path of the Skies +(159899, 0, 1116, 762.092, 130.033, 7.53102, 20779), -- Path of the Crescent Moon +(159900, 0, 1116, 7811.06, 542.615, 122.757, 20779), -- Path of the Dark Rail +(159901, 0, 1116, 7117.57, 197.1, 145.211, 20779), -- Path of the Verdant +(159902, 0, 0,-7508.83,-1326.77, 301.364, 20779); -- Path of the Burning Mountain diff --git a/sql/updates/world/2016_02_11_01_world.sql b/sql/updates/world/2016_02_11_01_world.sql new file mode 100644 index 00000000000..5a13d28f34b --- /dev/null +++ b/sql/updates/world/2016_02_11_01_world.sql @@ -0,0 +1,3 @@ +-- +-- delete multiple spawns +DELETE FROM `gameobject` WHERE `guid` IN (218987,232480,234051,218986,232479,234050,218988,218989,200969,203095,212811,216447,220558,200970,203096,212810,216448,220557,200968,203094,212812,216446,220559,200972,203101,212808,216450,220552,200971,203100,212809,216449,220553,200973,203102,212804,216451,220551,200975,203098,212800,216453,220555,200974,203099,212802,216452,220554,200976,203097,212799,216454,220556,218990,232504,234053,218991,232503,234052,203060,212761,216473,220569,203061,212763,216474,220568,218955,201000,203066,212765,216471,220564,200995,203067,212769,216470,220562,219016,219017,234295,219015,234296,219010,234315,219008,219013,200998,203065,212766,216469,220565,200997,203064,212767,216468,220566,219025,219018); diff --git a/src/common/Collision/Management/MMapManager.cpp b/src/common/Collision/Management/MMapManager.cpp index 3f873b704d1..486219b9f32 100644 --- a/src/common/Collision/Management/MMapManager.cpp +++ b/src/common/Collision/Management/MMapManager.cpp @@ -179,7 +179,7 @@ namespace MMAP dtTileRef tileRef = 0; // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed - if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, 0/*DT_TILE_FREE_DATA*/, 0, &tileRef))) + if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))) { mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef)); ++loadedTiles; diff --git a/src/common/Threading/MPSCQueue.h b/src/common/Threading/MPSCQueue.h new file mode 100644 index 00000000000..09648b844be --- /dev/null +++ b/src/common/Threading/MPSCQueue.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MPSCQueue_h__ +#define MPSCQueue_h__ + +#include <atomic> +#include <utility> + +// C++ implementation of Dmitry Vyukov's lock free MPSC queue +// http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue +template<typename T> +class MPSCQueue +{ +public: + MPSCQueue() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed)) + { + Node* front = _head.load(std::memory_order_relaxed); + front->Next.store(nullptr, std::memory_order_relaxed); + } + + ~MPSCQueue() + { + T* output; + while (this->Dequeue(output)) + ; + + Node* front = _head.load(std::memory_order_relaxed); + delete front; + } + + void Enqueue(T* input) + { + Node* node = new Node(input); + Node* prevHead = _head.exchange(node, std::memory_order_acq_rel); + prevHead->Next.store(node, std::memory_order_release); + } + + bool Dequeue(T*& result) + { + Node* tail = _tail.load(std::memory_order_relaxed); + Node* next = tail->Next.load(std::memory_order_acquire); + if (!next) + return false; + + result = next->Data; + _tail.store(next, std::memory_order_release); + delete tail; + return true; + } + +private: + struct Node + { + Node() = default; + explicit Node(T* data) : Data(data) { Next.store(nullptr, std::memory_order_relaxed); } + + T* Data; + std::atomic<Node*> Next; + }; + + std::atomic<Node*> _head; + std::atomic<Node*> _tail; + + MPSCQueue(MPSCQueue const&) = delete; + MPSCQueue& operator=(MPSCQueue const&) = delete; +}; + +#endif // MPSCQueue_h__ diff --git a/src/server/bnetserver/Main.cpp b/src/server/bnetserver/Main.cpp index 3d1481fae9e..740a0efa438 100644 --- a/src/server/bnetserver/Main.cpp +++ b/src/server/bnetserver/Main.cpp @@ -71,13 +71,16 @@ void ServiceStatusWatcher(boost::system::error_code const& error); bool StartDB(); void StopDB(); -void SignalHandler(const boost::system::error_code& error, int signalNumber); -void KeepDatabaseAliveHandler(const boost::system::error_code& error); +void SignalHandler(boost::system::error_code const& error, int signalNumber); +void KeepDatabaseAliveHandler(boost::system::error_code const& error); +void BanExpiryHandler(boost::system::error_code const& error); variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService); static boost::asio::io_service* _ioService; static boost::asio::deadline_timer* _dbPingTimer; static uint32 _dbPingInterval; +static boost::asio::deadline_timer* _banExpiryCheckTimer; +static uint32 _banExpiryCheckInterval; LoginDatabaseWorkerPool LoginDatabase; int main(int argc, char** argv) @@ -180,6 +183,11 @@ int main(int argc, char** argv) _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); _dbPingTimer->async_wait(KeepDatabaseAliveHandler); + _banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); + _banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService); + _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); + _banExpiryCheckTimer->async_wait(BanExpiryHandler); + sComponentMgr->Load(); sModuleMgr->Load(); @@ -195,6 +203,7 @@ int main(int argc, char** argv) // Start the io service worker loop _ioService->run(); + _banExpiryCheckTimer->cancel(); _dbPingTimer->cancel(); sSessionMgr.StopNetwork(); @@ -210,6 +219,7 @@ int main(int argc, char** argv) signals.cancel(); + delete _banExpiryCheckTimer; delete _dbPingTimer; delete _ioService; return 0; @@ -240,13 +250,13 @@ void StopDB() MySQL::Library_End(); } -void SignalHandler(const boost::system::error_code& error, int /*signalNumber*/) +void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/) { if (!error) _ioService->stop(); } -void KeepDatabaseAliveHandler(const boost::system::error_code& error) +void KeepDatabaseAliveHandler(boost::system::error_code const& error) { if (!error) { @@ -258,6 +268,18 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error) } } +void BanExpiryHandler(boost::system::error_code const& error) +{ + if (!error) + { + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + + _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); + _banExpiryCheckTimer->async_wait(BanExpiryHandler); + } +} + #if PLATFORM == PLATFORM_WINDOWS void ServiceStatusWatcher(boost::system::error_code const& error) { diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp index 51a50b48a5b..4d54562501f 100644 --- a/src/server/bnetserver/Server/Session.cpp +++ b/src/server/bnetserver/Server/Session.cpp @@ -615,18 +615,6 @@ void Battlenet::Session::Start() std::string ip_address = GetRemoteIpAddress().to_string(); TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str()); - if (_queryCallback) - { - Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST); - AsyncWrite(logonResponse); - TC_LOG_DEBUG("session", "[Session::Start] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str()); - return; - } - - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); @@ -666,12 +654,37 @@ void Battlenet::Session::CheckIpCallback(PreparedQueryResult result) bool Battlenet::Session::Update() { + EncryptableBuffer* queued; + MessageBuffer buffer((std::size_t(BufferSizes::Read))); + while (_bufferQueue.Dequeue(queued)) + { + std::size_t packetSize = queued->Buffer.GetActiveSize(); + if (queued->Encrypt) + _crypt.EncryptSend(queued->Buffer.GetReadPointer(), packetSize); + + if (buffer.GetRemainingSpace() < packetSize) + { + QueuePacket(std::move(buffer)); + buffer.Resize(std::size_t(BufferSizes::Read)); + } + + if (buffer.GetRemainingSpace() >= packetSize) + buffer.Write(queued->Buffer.GetReadPointer(), packetSize); + else // single packet larger than 16384 bytes - client will reject. + QueuePacket(std::move(queued->Buffer)); + + delete queued; + } + + if (buffer.GetActiveSize() > 0) + QueuePacket(std::move(buffer)); + if (!BattlenetSocket::Update()) return false; if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - auto callback = std::move(_queryCallback); + auto callback = _queryCallback; _queryCallback = nullptr; callback(_queryFuture.get()); } @@ -691,15 +704,12 @@ void Battlenet::Session::AsyncWrite(ServerPacket* packet) packet->Write(); - MessageBuffer buffer; - buffer.Write(packet->GetData(), packet->GetSize()); + EncryptableBuffer* buffer = new EncryptableBuffer(); + buffer->Buffer.Write(packet->GetData(), packet->GetSize()); + buffer->Encrypt = _crypt.IsInitialized(); delete packet; - std::unique_lock<std::mutex> guard(_writeLock); - - _crypt.EncryptSend(buffer.GetReadPointer(), buffer.GetActiveSize()); - - QueuePacket(std::move(buffer), guard); + _bufferQueue.Enqueue(buffer); } inline void ReplaceResponse(Battlenet::ServerPacket** oldResponse, Battlenet::ServerPacket* newResponse) @@ -827,9 +837,6 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke return false; } - //set expired game account bans to inactive - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); - BigNumber M; sha.Initialize(); sha.UpdateBigNumbers(&A, &M1, &K, NULL); diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h index 75c30096417..2443d694a80 100644 --- a/src/server/bnetserver/Server/Session.h +++ b/src/server/bnetserver/Server/Session.h @@ -23,6 +23,7 @@ #include "Socket.h" #include "BigNumber.h" #include "Callback.h" +#include "MPSCQueue.h" #include <memory> #include <boost/asio/ip/tcp.hpp> @@ -174,6 +175,13 @@ namespace Battlenet std::queue<ModuleType> _modulesWaitingForData; + struct EncryptableBuffer + { + MessageBuffer Buffer; + bool Encrypt; + }; + + MPSCQueue<EncryptableBuffer> _bufferQueue; PacketCrypt _crypt; bool _authed; bool _subscribedToRealmListUpdates; diff --git a/src/server/bnetserver/Server/SessionManager.cpp b/src/server/bnetserver/Server/SessionManager.cpp index 8201f4869b4..c53214495d4 100644 --- a/src/server/bnetserver/Server/SessionManager.cpp +++ b/src/server/bnetserver/Server/SessionManager.cpp @@ -22,7 +22,8 @@ bool Battlenet::SessionManager::StartNetwork(boost::asio::io_service& service, s if (!BaseSocketMgr::StartNetwork(service, bindIp, port)) return false; - _acceptor->AsyncAcceptManaged(&OnSocketAccept); + _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); return true; } @@ -31,9 +32,9 @@ NetworkThread<Battlenet::Session>* Battlenet::SessionManager::CreateThreads() co return new NetworkThread<Session>[GetNetworkThreadCount()]; } -void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock) +void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) { - sSessionMgr.OnSocketOpen(std::forward<tcp::socket>(sock)); + sSessionMgr.OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex); } void Battlenet::SessionManager::AddSession(Session* session) diff --git a/src/server/bnetserver/Server/SessionManager.h b/src/server/bnetserver/Server/SessionManager.h index fe262b29f4e..5cf0b199f15 100644 --- a/src/server/bnetserver/Server/SessionManager.h +++ b/src/server/bnetserver/Server/SessionManager.h @@ -75,7 +75,7 @@ namespace Battlenet NetworkThread<Session>* CreateThreads() const override; private: - static void OnSocketAccept(tcp::socket&& sock); + static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex); SessionMap _sessions; SessionByAccountMap _sessionsByAccountId; diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp index fd0dbdd4b5a..001fdf20610 100644 --- a/src/server/database/Updater/UpdateFetcher.cpp +++ b/src/server/database/Updater/UpdateFetcher.cpp @@ -142,14 +142,12 @@ UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path co std::ifstream in(file.c_str()); WPFatal(in.is_open(), "Could not read an update file."); - auto const start_pos = in.tellg(); - in.ignore(std::numeric_limits<std::streamsize>::max()); - auto const char_count = in.gcount(); - in.seekg(start_pos); + auto update = [&in] { + std::ostringstream ss; + ss << in.rdbuf(); + return Trinity::make_unique<std::string>(ss.str()); + }(); - SQLUpdate const update(new std::string(char_count, char{})); - - in.read(&(*update)[0], update->size()); in.close(); return update; } diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h index 22a0d08c7f8..32f8516413d 100644 --- a/src/server/database/Updater/UpdateFetcher.h +++ b/src/server/database/Updater/UpdateFetcher.h @@ -103,7 +103,7 @@ private: typedef std::unordered_map<std::string, std::string> HashToFileNameStorage; typedef std::unordered_map<std::string, AppliedFileEntry> AppliedFileStorage; typedef std::vector<UpdateFetcher::DirectoryEntry> DirectoryStorage; - typedef std::shared_ptr<std::string> SQLUpdate; + typedef std::unique_ptr<std::string> SQLUpdate; LocaleFileStorage GetFileList() const; void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 9796464e821..b02906a7164 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -469,7 +469,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry " SI64FMTD " SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map); return false; } - if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area)) + if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !sAreaTableStore.LookupEntry(e.event.respawn.area)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry " SI64FMTD " SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area); return false; diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 7c9739217d2..6462c89a694 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -2275,17 +2275,18 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteria const* achieve bool matchFound = false; for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) { - uint32 area_id = worldOverlayEntry->AreaID[j]; - if (!area_id) // array have 0 only in empty tail + AreaTableEntry const* area = sAreaTableStore.LookupEntry(worldOverlayEntry->AreaID[j]); + if (!area) break; - int32 exploreFlag = GetAreaFlagByAreaID(area_id); - if (exploreFlag < 0) + if (area->AreaBit < 0) continue; - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1 << (uint32(exploreFlag) % 32); + uint32 playerIndexOffset = uint32(area->AreaBit) / 32; + if (playerIndexOffset >= PLAYER_EXPLORED_ZONES_SIZE) + continue; + uint32 mask = 1 << (uint32(area->AreaBit) % 32); if (referencePlayer->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) { matchFound = true; diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 72c03f7fbce..2f4d101b4f1 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -624,13 +624,12 @@ void Battlefield::RemovePlayerFromResurrectQueue(ObjectGuid playerGuid) } } -void Battlefield::SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid guid) +void Battlefield::SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid const& guid) { - WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); - uint32 time = m_LastResurrectTimer; // resurrect every 30 seconds - - data << guid << time; - player->SendDirectMessage(&data); + WorldPackets::Battleground::AreaSpiritHealerTime areaSpiritHealerTime; + areaSpiritHealerTime.HealerGuid = guid; + areaSpiritHealerTime.TimeLeft = m_LastResurrectTimer; + player->SendDirectMessage(areaSpiritHealerTime.Write()); } // ---------------------- diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h index 1b28dd82ed1..0159ba95eeb 100644 --- a/src/server/game/Battlefield/Battlefield.h +++ b/src/server/game/Battlefield/Battlefield.h @@ -334,7 +334,7 @@ class Battlefield : public ZoneScript /// Return if we can use mount in battlefield bool CanFlyIn() { return !m_isActive; } - void SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid guid); + void SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid const& guid); void StartBattle(); void EndBattle(bool endByTimer); diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index f39babba46e..57f61c0e195 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -592,14 +592,16 @@ void BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, Batt TC_LOG_ERROR("bg.battleground", "BattlegroundMgr::SendToBattleground: Instance %u (bgType %u) not found while trying to teleport player %s", instanceId, bgTypeId, player->GetName().c_str()); } -void BattlegroundMgr::SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid guid) +void BattlegroundMgr::SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid const& guid) { - WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds if (time_ == uint32(-1)) time_ = 0; - data << guid << time_; - player->GetSession()->SendPacket(&data); + + WorldPackets::Battleground::AreaSpiritHealerTime areaSpiritHealerTime; + areaSpiritHealerTime.HealerGuid = guid; + areaSpiritHealerTime.TimeLeft = time_; + player->GetSession()->SendPacket(areaSpiritHealerTime.Write()); } bool BattlegroundMgr::IsArenaType(BattlegroundTypeId bgTypeId) diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h index 9f776e3c62b..354f37b8cd8 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.h +++ b/src/server/game/Battlegrounds/BattlegroundMgr.h @@ -90,7 +90,7 @@ class BattlegroundMgr void BuildBattlegroundStatusActive(WorldPackets::Battleground::BattlefieldStatusActive* battlefieldStatus, Battleground* bg, Player* player, uint32 ticketId, uint32 joinTime, uint32 arenaType); void BuildBattlegroundStatusQueued(WorldPackets::Battleground::BattlefieldStatusQueued* battlefieldStatus, Battleground* bg, Player* player, uint32 ticketId, uint32 joinTime, uint32 avgWaitTime, uint32 arenaType, bool asGroup); void BuildBattlegroundStatusFailed(WorldPackets::Battleground::BattlefieldStatusFailed* battlefieldStatus, Battleground* bg, Player* pPlayer, uint32 ticketId, uint32 arenaType, GroupJoinBattlegroundResult result, ObjectGuid const* errorGuid = nullptr); - void SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid guid); + void SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid const& guid); /* Battlegrounds */ Battleground* GetBattleground(uint32 InstanceID, BattlegroundTypeId bgTypeId); diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 89d871137f2..07e77a4e6a3 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -297,10 +297,10 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c uint32 areaId = player->GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; - if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) { areaName = area->AreaName_lang; - if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->ParentAreaID)) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->ParentAreaID)) zoneName = zone->AreaName_lang; } diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index ff08b5ea7e8..ed3ae7cb8e5 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1768,7 +1768,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const } case CONDITION_SOURCE_TYPE_PHASE: { - if (cond->SourceEntry && !GetAreaEntryByAreaID(cond->SourceEntry)) + if (cond->SourceEntry && !sAreaTableStore.LookupEntry(cond->SourceEntry)) { TC_LOG_ERROR("sql.sql", "%s SourceEntry in `condition` table, does not exist in AreaTable.dbc, ignoring.", cond->ToString().c_str()); return false; @@ -1845,7 +1845,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const } case CONDITION_ZONEID: { - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->ConditionValue1); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(cond->ConditionValue1); if (!areaEntry) { TC_LOG_ERROR("sql.sql", "%s Area (%u) does not exist, skipped.", cond->ToString(true).c_str(), cond->ConditionValue1); @@ -2592,12 +2592,9 @@ bool ConditionMgr::IsPlayerMeetingCondition(Player* player, PlayerConditionEntry for (std::size_t i = 0; i < ExploredCount::value; ++i) { - if (condition->Explored[i]) - { - int32 exploreFlag = GetAreaFlagByAreaID(condition->Explored[i]); - if (exploreFlag != -1 && !(player->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + exploreFlag / 32) & (1 << (uint32(exploreFlag) % 32)))) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(condition->Explored[i])) + if (area->AreaBit != -1 && !(player->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + area->AreaBit / 32) & (1 << (uint32(area->AreaBit) % 32)))) return false; - } } } diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 2bf38bdc769..b1a8a1179c6 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -44,16 +44,12 @@ struct WMOAreaTableTripple int32 adtId; }; -typedef std::map<uint16, uint32> AreaFlagByAreaID; -typedef std::map<uint32, uint32> AreaFlagByMapID; typedef std::multimap<uint32, CharSectionsEntry const*> CharSectionsMap; typedef std::map<uint32, std::vector<uint32>> FactionTeamMap; typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const*> WMOAreaInfoByTripple; DBCStorage<AnimKitEntry> sAnimKitStore(AnimKitfmt); -DBCStorage<AreaTableEntry> sAreaStore(AreaTablefmt); -static AreaFlagByAreaID sAreaFlagByAreaID; -static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files +DBCStorage<AreaTableEntry> sAreaTableStore(AreaTablefmt); DBCStorage<AreaTriggerEntry> sAreaTriggerStore(AreaTriggerfmt); DBCStorage<ArmorLocationEntry> sArmorLocationStore(ArmorLocationfmt); @@ -298,7 +294,7 @@ void LoadDBCStores(const std::string& dataPath, uint32 defaultLocale) #define LOAD_DBC(store, file) LoadDBC(availableDbcLocales, bad_dbc_files, store, dbcPath, file, defaultLocale) LOAD_DBC(sAnimKitStore, "AnimKit.dbc");//20444 - LOAD_DBC(sAreaStore, "AreaTable.dbc");//20444 + LOAD_DBC(sAreaTableStore, "AreaTable.dbc");//20444 LOAD_DBC(sAreaTriggerStore, "AreaTrigger.dbc");//20444 LOAD_DBC(sArmorLocationStore, "ArmorLocation.dbc");//20444 LOAD_DBC(sBankBagSlotPricesStore, "BankBagSlotPrices.dbc");//20444 @@ -382,20 +378,6 @@ void LoadDBCStores(const std::string& dataPath, uint32 defaultLocale) #undef LOAD_DBC - // must be after sAreaStore loading - for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 - { - if (AreaTableEntry const* area = sAreaStore.LookupEntry(i)) - { - // fill AreaId->DBC records - sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->AreaBit)); - - // fill MapId->DBC records (skip sub zones and continents) - if (area->ParentAreaID == 0) - sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->MapID, area->AreaBit)); - } - } - for (uint32 i = 0; i < sCharSectionsStore.GetNumRows(); ++i) if (CharSectionsEntry const* entry = sCharSectionsStore.LookupEntry(i)) if (entry->Race && ((1 << (entry->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) //ignore Nonplayable races @@ -494,7 +476,7 @@ void LoadDBCStores(const std::string& dataPath, uint32 defaultLocale) } // Check loaded DBC files proper version - if (!sAreaStore.LookupEntry(6565) || // last area (areaflag) added in 6.2.2 (20444) + if (!sAreaTableStore.LookupEntry(7941) || // last area added in 6.2.2 (20444) !sCharTitlesStore.LookupEntry(457) || // last char title added in 6.2.2 (20444) !sGemPropertiesStore.LookupEntry(2544) || // last gem property added in 6.2.2 (20444) !sMapStore.LookupEntry(1497) || // last map added in 6.2.2 (20444) @@ -580,41 +562,12 @@ char const* GetCreatureFamilyPetName(uint32 petfamily, uint32 /*locale*/) return pet_family->Name_lang ? pet_family->Name_lang : NULL; } -int32 GetAreaFlagByAreaID(uint32 area_id) -{ - AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); - if (i == sAreaFlagByAreaID.end()) - return -1; - - return i->second; -} - WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid) { WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid)); - if (i == sWMOAreaInfoByTripple.end()) - return NULL; - return i->second; -} - -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) -{ - int32 areaflag = GetAreaFlagByAreaID(area_id); - if (areaflag < 0) + if (i == sWMOAreaInfoByTripple.end()) return NULL; - - return sAreaStore.LookupEntry(areaflag); -} - -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id) -{ - if (area_flag) - return sAreaStore.LookupEntry(area_flag); - - if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) - return GetAreaEntryByAreaID(mapEntry->AreaTableID); - - return NULL; + return i->second; } char const* GetRaceName(uint8 race, uint8 /*locale*/) @@ -629,15 +582,6 @@ char const* GetClassName(uint8 class_, uint8 /*locale*/) return classEntry ? classEntry->Name_lang : NULL; } -uint32 GetAreaFlagByMapId(uint32 mapid) -{ - AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); - if (i == sAreaFlagByMapID.end()) - return 0; - else - return i->second; -} - uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) { if (mapid != 530 && mapid != 571 && mapid != 732) // speed for most cases diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 017f641fb18..d8af7418be4 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -24,12 +24,6 @@ #include "DB2Structure.h" #include "SharedDefines.h" -// AreaTable -int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id); -uint32 GetAreaFlagByMapId(uint32 mapid); - // CharSections CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color); @@ -128,7 +122,7 @@ private: }; extern DBCStorage<AnimKitEntry> sAnimKitStore; -extern DBCStorage<AreaTableEntry> sAreaStore;// recommend access using functions +extern DBCStorage<AreaTableEntry> sAreaTableStore; extern DBCStorage<AreaTriggerEntry> sAreaTriggerStore; extern DBCStorage<ArmorLocationEntry> sArmorLocationStore; extern DBCStorage<BankBagSlotPricesEntry> sBankBagSlotPricesStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 418c238b52d..ec334fb3b8f 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -34,15 +34,12 @@ struct AnimKitEntry //uint32 LowDefAnimKitID; // 3 }; -// Temporary define until max depth is found somewhere (adt?) -#define MAX_MAP_DEPTH -5000 - struct AreaTableEntry { uint32 ID; // 0 uint32 MapID; // 1 uint32 ParentAreaID; // 2 if 0 then it's zone, else it's zone id of this area - uint32 AreaBit; // 3, main index + int32 AreaBit; // 3 uint32 Flags[2]; // 4-5, //uint32 SoundProviderPref; // 6, //uint32 SoundProviderPrefUnderwater; // 7, diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index ce378ea5d43..d52891576da 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -23,7 +23,7 @@ // n - index (included), l - uint64, p - field present in sql dbc, a - field absent in sql dbc char const AnimKitfmt[] = "nxxx"; -char const AreaTablefmt[] = "iiiniixxxxxxisiiiiixxxxxxxxxx"; +char const AreaTablefmt[] = "niiiiixxxxxxisiiiiixxxxxxxxxx"; char const AreaTriggerfmt[] = "nifffxxxfffffxxxx"; char const ArmorLocationfmt[] = "nfffff"; char const BankBagSlotPricesfmt[] = "ni"; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 02176cca13c..050217996ee 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -463,9 +463,7 @@ void GameObject::Update(uint32 diff) if (caster && caster->GetTypeId() == TYPEID_PLAYER) { caster->ToPlayer()->RemoveGameObject(this, false); - - WorldPacket data(SMSG_FISH_ESCAPED, 0); - caster->ToPlayer()->SendDirectMessage(&data); + caster->ToPlayer()->SendDirectMessage(WorldPackets::GameObject::FishEscaped().Write()); } // can be delete m_lootState = GO_JUST_DEACTIVATED; @@ -1442,7 +1440,10 @@ void GameObject::Use(Unit* user) player->SendCinematicStart(info->camera.camera); if (info->camera.eventID) + { GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this); + EventInform(info->camera.eventID, user); + } return; } @@ -1517,9 +1518,7 @@ void GameObject::Use(Unit* user) default: { SetLootState(GO_JUST_DEACTIVATED); - - WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); - player->SendDirectMessage(&data); + player->SendDirectMessage(WorldPackets::GameObject::FishNotHooked().Write()); break; } } diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h index ce4fd7dcfbc..cc528b973f8 100644 --- a/src/server/game/Entities/Object/Position.h +++ b/src/server/game/Entities/Object/Position.h @@ -20,6 +20,8 @@ #include "Common.h" +#include <G3D/Vector3.h> + class ByteBuffer; struct Position @@ -87,6 +89,11 @@ public: m_positionX = pos->m_positionX; m_positionY = pos->m_positionY; m_positionZ = pos->m_positionZ; SetOrientation(pos->m_orientation); } + void Relocate(G3D::Vector3 const& pos) + { + m_positionX = pos.x; m_positionY = pos.y; m_positionZ = pos.z; + } + void RelocateOffset(Position const &offset); void SetOrientation(float orientation) diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index b5fb841333c..d4db53634c2 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1726,25 +1726,22 @@ uint8 Pet::GetMaxTalentPointsForLevel(uint8 level) const void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply) { + ASSERT(spellInfo); + if (!spellInfo->IsAutocastable()) return; - uint32 spellid = spellInfo->Id; - - PetSpellMap::iterator itr = m_spells.find(spellid); + PetSpellMap::iterator itr = m_spells.find(spellInfo->Id); if (itr == m_spells.end()) return; - uint32 i; + auto autospellItr = std::find(m_autospells.begin(), m_autospells.end(), spellInfo->Id); if (apply) { - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i) - ; // just search - - if (i == m_autospells.size()) + if (autospellItr == m_autospells.end()) { - m_autospells.push_back(spellid); + m_autospells.push_back(spellInfo->Id); if (itr->second.active != ACT_ENABLED) { @@ -1756,13 +1753,10 @@ void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply) } else { - AutoSpellList::iterator itr2 = m_autospells.begin(); - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i, ++itr2) - ; // just search - - if (i < m_autospells.size()) + if (autospellItr != m_autospells.end()) { - m_autospells.erase(itr2); + m_autospells.erase(autospellItr); + if (itr->second.active != ACT_DISABLED) { itr->second.active = ACT_DISABLED; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a26c1f3c841..7e30a125827 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2124,7 +2124,7 @@ bool Player::CanInteractWithQuestGiver(Object* questGiver) const return false; } -Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint64 npcflagmask) const +Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint64 npcflagmask) const { // unit checks if (!guid) @@ -2168,7 +2168,21 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint64 npcflagmask) c return creature; } -GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const +{ + if (GameObject* go = GetMap()->GetGameObject(guid)) + { + if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) + return go; + + TC_LOG_DEBUG("maps", "Player::GetGameObjectIfCanInteractWith: GameObject '%s' (%s) is too far away from player '%s' (%s) to be used by him (Distance: %f, maximal %f is allowed)", + go->GetGOInfo()->name.c_str(), go->GetGUID().ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str(), go->GetDistance(this), go->GetInteractionDistance()); + } + + return nullptr; +} + +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const { if (GameObject* go = GetMap()->GetGameObject(guid)) { @@ -2177,8 +2191,8 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) return go; - TC_LOG_DEBUG("maps", "Player::GetGameObjectIfCanInteractWith: GameObject '%s' (%s) is too far away from player '%s' (%s) to be used by him (Distance: %f, maximal 10 is allowed)", - go->GetGOInfo()->name.c_str(), go->GetGUID().ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str(), go->GetDistance(this)); + TC_LOG_DEBUG("maps", "Player::GetGameObjectIfCanInteractWith: GameObject '%s' (%s) is too far away from player '%s' (%s) to be used by him (Distance: %f, maximal %f is allowed)", + go->GetGOInfo()->name.c_str(), go->GetGUID().ToString().c_str(), GetName().c_str(), GetGUID().ToString().c_str(), go->GetDistance(this), go->GetInteractionDistance()); } } @@ -4221,6 +4235,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) // remove death flag + set aura SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00); + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); // This must be called always even on Players with race != RACE_NIGHTELF in case of faction change RemoveAurasDueToSpell(20584); // RACE_NIGHTELF speed bonuses @@ -4618,10 +4633,10 @@ void Player::RepopAtGraveyard() // note: this can be called also when the player is alive // for example from WorldSession::HandleMovementOpcodes - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetAreaId()); // Such zones are considered unreachable as a ghost and the player must be automatically revived - if ((!IsAlive() && zone && zone->Flags[0] & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < MAX_MAP_DEPTH) + if ((!IsAlive() && zone && zone->Flags[0] & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < GetMap()->GetMinHeight(GetPositionX(), GetPositionY())) { ResurrectPlayer(0.5f); SpawnCorpseBones(); @@ -4656,8 +4671,10 @@ void Player::RepopAtGraveyard() GetSession()->SendPacket(packet.Write()); } } - else if (GetPositionZ() < MAX_MAP_DEPTH) + else if (GetPositionZ() < GetMap()->GetMinHeight(GetPositionX(), GetPositionY())) TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation()); + + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); } bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone) const @@ -4702,7 +4719,7 @@ void Player::UpdateLocalChannels(uint32 newZone) if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar()) return; // The client handles it automatically after loading, but not after teleporting - AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(newZone); if (!current_zone) return; @@ -5900,23 +5917,32 @@ void Player::CheckAreaExploreAndOutdoor() return; bool isOutdoor; - uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor) RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY); - if (areaFlag == 0xffff) + if (!areaId) return; - int offset = areaFlag / 32; + + if (!areaEntry) + { + TC_LOG_ERROR("entities.player", "Player '%s' (%s) discovered unknown area (x: %f y: %f z: %f map: %u)", + GetName().c_str(), GetGUID().ToString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); + return; + } + + uint32 offset = areaEntry->AreaBit / 32; if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { TC_LOG_ERROR("entities.player", "Player::CheckAreaExploreAndOutdoor: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", - areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); + areaId, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); return; } - uint32 val = (uint32)(1 << (areaFlag % 32)); + uint32 val = (uint32)(1 << (areaEntry->AreaBit % 32)); uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); if (!(currFields & val)) @@ -5925,20 +5951,11 @@ void Player::CheckAreaExploreAndOutdoor() UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId()); - if (!areaEntry) - { - TC_LOG_ERROR("entities.player", "Player '%s' (%s) discovered unknown area (x: %f y: %f z: %f map: %u)", - GetName().c_str(), GetGUID().ToString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); - return; - } - if (areaEntry->ExplorationLevel > 0) { - uint32 area = areaEntry->ID; if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { - SendExplorationExperience(area, 0); + SendExplorationExperience(areaId, 0); } else { @@ -5962,9 +5979,9 @@ void Player::CheckAreaExploreAndOutdoor() } GiveXP(XP, nullptr); - SendExplorationExperience(area, XP); + SendExplorationExperience(areaId, XP); } - TC_LOG_DEBUG("entities.player", "Player '%s' (%s) discovered a new area: %u", GetName().c_str(),GetGUID().ToString().c_str(), area); + TC_LOG_DEBUG("entities.player", "Player '%s' (%s) discovered a new area: %u", GetName().c_str(),GetGUID().ToString().c_str(), areaId); } } } @@ -6871,7 +6888,7 @@ void Player::UpdateArea(uint32 newArea) // so apply them accordingly m_areaUpdateId = newArea; - AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(newArea); pvpInfo.IsInFFAPvPArea = area && (area->Flags[0] & AREA_FLAG_ARENA); UpdatePvPState(true); @@ -6923,7 +6940,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) // zone changed, so area changed as well, update it UpdateArea(newArea); - AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(newZone); if (!zone) return; @@ -9045,10 +9062,11 @@ void Player::SetBindPoint(ObjectGuid guid) const void Player::SendRespecWipeConfirm(ObjectGuid const& guid, uint32 cost) const { - WorldPacket data(SMSG_RESPEC_WIPE_CONFIRM, 8 + 4); - data << guid; - data << cost; - GetSession()->SendPacket(&data); + WorldPackets::Talent::RespecWipeConfirm respecWipeConfirm; + respecWipeConfirm.RespecMaster = guid; + respecWipeConfirm.Cost = cost; + respecWipeConfirm.RespecType = SPEC_RESET_TALENTS; + GetSession()->SendPacket(respecWipeConfirm.Write()); } void Player::ResetPetTalents() @@ -25825,10 +25843,10 @@ std::string Player::GetMapAreaAndZoneString() const uint32 areaId = GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; - if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) { areaName = area->AreaName_lang; - if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->ParentAreaID)) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->ParentAreaID)) zoneName = zone->AreaName_lang; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 6b235a3d0e0..b5221a8cbc5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -155,6 +155,14 @@ enum TalentSpecialization // talent tabs TALENT_SPEC_MONK_MISTWEAVER = 270 }; +enum SpecResetType +{ + SPEC_RESET_TALENTS = 0, + SPEC_RESET_SPECIALIZATION = 1, + SPEC_RESET_GLYPHS = 2, + SPEC_RESET_PET_TALENTS = 3 +}; + // Spell modifier (used for modify other spells) struct SpellModifier { @@ -1221,8 +1229,9 @@ class Player : public Unit, public GridObject<Player> void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time, bool welcome) const; bool CanInteractWithQuestGiver(Object* questGiver) const; - Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint64 npcflagmask) const; - GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const; + Creature* GetNPCIfCanInteractWith(ObjectGuid const& guid, uint64 npcflagmask) const; + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const; + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const; void ToggleAFK(); void ToggleDND(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 4c945365640..9902b2a2af9 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -668,7 +668,7 @@ enum DamageEffectType }; // Value masks for UNIT_FIELD_FLAGS -enum UnitFlags +enum UnitFlags : uint32 { UNIT_FLAG_SERVER_CONTROLLED = 0x00000001, // set only when unit movement is controlled by server - by SPLINE/MONSTER_MOVE packets, together with UNIT_FLAG_STUNNED; only set to units controlled by client; client function CGUnit_C::IsClientControlled returns false when set for owner UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index e15a4e59d9b..d818ab0edb5 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3929,7 +3929,7 @@ void ObjectMgr::LoadQuests() // client quest log visual (area case) if (qinfo->QuestSortID > 0) { - if (!GetAreaEntryByAreaID(qinfo->QuestSortID)) + if (!sAreaTableStore.LookupEntry(qinfo->QuestSortID)) { TC_LOG_ERROR("sql.sql", "Quest %u has `QuestSortID` = %u (zone case) but zone with this id does not exist.", qinfo->GetQuestId(), qinfo->QuestSortID); @@ -5868,7 +5868,7 @@ void ObjectMgr::LoadGraveyardZones() continue; } - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry) { TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId); @@ -7764,7 +7764,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel() uint32 entry = fields[0].GetUInt32(); int32 skill = fields[1].GetInt16(); - AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry); + AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry); if (!fArea) { TC_LOG_ERROR("sql.sql", "AreaId %u defined in `skill_fishing_base_level` does not exist", entry); diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 09bcbfb5e64..1e304fcaca3 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -227,7 +227,7 @@ namespace Trinity inline bool IsValidMapCoord(float x, float y, float z) { - return IsValidMapCoord(x, y) && std::isfinite(z); + return IsValidMapCoord(x, y) && IsValidMapCoord(z); } inline bool IsValidMapCoord(float x, float y, float z, float o) diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index c37928d32ad..e8fdb384d89 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -595,13 +595,8 @@ void WorldSession::HandleReportPvPAFK(WorldPackets::Battleground::ReportPvPPlaye reportedPlayer->ReportedAfkBy(_player); } -void WorldSession::HandleRequestRatedBattlefieldInfo(WorldPacket& recvData) +void WorldSession::HandleRequestRatedBattlefieldInfo(WorldPackets::Battleground::RequestRatedBattlefieldInfo& /*packet*/) { - uint8 unk; - recvData >> unk; - - TC_LOG_DEBUG("bg.battleground", "WorldSession::HandleRequestRatedBattlefieldInfo: unk = %u", unk); - /// @Todo: perfome research in this case /// The unk fields are related to arenas WorldPacket data(SMSG_RATED_BATTLEFIELD_INFO, 72); @@ -640,7 +635,7 @@ void WorldSession::HandleGetPVPOptionsEnabled(WorldPackets::Battleground::GetPVP SendPacket(pvpOptionsEnabled.Write()); } -void WorldSession::HandleRequestPvpReward(WorldPacket& /*recvData*/) +void WorldSession::HandleRequestPvpReward(WorldPackets::Battleground::RequestPVPRewards& /*packet*/) { _player->SendPvpRewards(); } @@ -688,7 +683,7 @@ void WorldSession::HandleHearthAndResurrect(WorldPackets::Battleground::HearthAn return; } - AreaTableEntry const* atEntry = GetAreaEntryByAreaID(_player->GetAreaId()); + AreaTableEntry const* atEntry = sAreaTableStore.LookupEntry(_player->GetAreaId()); if (!atEntry || !(atEntry->Flags[0] & AREA_FLAG_CAN_HEARTH_AND_RESURRECT)) return; diff --git a/src/server/game/Handlers/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp index 09569e833d1..344592580cf 100644 --- a/src/server/game/Handlers/ChannelHandler.cpp +++ b/src/server/game/Handlers/ChannelHandler.cpp @@ -35,7 +35,7 @@ void WorldSession::HandleJoinChannel(WorldPackets::Channel::JoinChannel& packet) if (!channel) return; - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetPlayer()->GetZoneId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone)) return; } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 8c89f19357f..ef197cbe541 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1350,80 +1350,64 @@ void WorldSession::HandleCharRenameCallBack(PreparedQueryResult result, WorldPac sWorld->UpdateCharacterInfo(renameInfo->Guid, renameInfo->NewName); } -void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) +void WorldSession::HandleSetPlayerDeclinedNames(WorldPackets::Character::SetPlayerDeclinedNames& packet) { - ObjectGuid guid; - - recvData >> guid; - // not accept declined names for unsupported languages std::string name; - if (!ObjectMgr::GetPlayerNameByGUID(guid, name)) + if (!ObjectMgr::GetPlayerNameByGUID(packet.Player, name)) { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, packet.Player); return; } std::wstring wname; if (!Utf8toWStr(name, wname)) { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, packet.Player); return; } if (!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); - return; - } - - std::string name2; - DeclinedName declinedname; - - recvData >> name2; - - if (name2 != name) // character have different name - { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, packet.Player); return; } for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) { - recvData >> declinedname.name[i]; - if (!normalizePlayerName(declinedname.name[i])) + if (!normalizePlayerName(packet.DeclinedNames.name[i])) { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, packet.Player); return; } } - if (!ObjectMgr::CheckDeclinedNames(wname, declinedname)) + if (!ObjectMgr::CheckDeclinedNames(wname, packet.DeclinedNames)) { - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, packet.Player); return; } for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - CharacterDatabase.EscapeString(declinedname.name[i]); + CharacterDatabase.EscapeString(packet.DeclinedNames.name[i]); SQLTransaction trans = CharacterDatabase.BeginTransaction(); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); - stmt->setUInt64(0, guid.GetCounter()); + stmt->setUInt64(0, packet.Player.GetCounter()); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_DECLINED_NAME); - stmt->setUInt64(0, guid.GetCounter()); + stmt->setUInt64(0, packet.Player.GetCounter()); - for (uint8 i = 0; i < 5; i++) - stmt->setString(i+1, declinedname.name[i]); + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; i++) + stmt->setString(i + 1, packet.DeclinedNames.name[i]); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); - SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_SUCCESS, guid); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_SUCCESS, packet.Player); } void WorldSession::HandleAlterAppearance(WorldPackets::Character::AlterApperance& packet) @@ -2564,11 +2548,11 @@ void WorldSession::SendCharFactionChange(ResponseCodes result, WorldPackets::Cha void WorldSession::SendSetPlayerDeclinedNamesResult(DeclinedNameResult result, ObjectGuid guid) { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(result); - if (result == DECLINED_NAMES_RESULT_SUCCESS) - data << guid; - SendPacket(&data); + WorldPackets::Character::SetPlayerDeclinedNamesResult packet; + packet.ResultCode = result; + packet.Player = guid; + + SendPacket(packet.Write()); } void WorldSession::SendBarberShopResult(BarberShopResult result) diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 462ed8051be..2c77d8df04d 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -653,14 +653,12 @@ void WorldSession::SendChatPlayerNotfoundNotice(std::string const& name) void WorldSession::SendPlayerAmbiguousNotice(std::string const& name) { - WorldPacket data(SMSG_CHAT_PLAYER_AMBIGUOUS, name.size()+1); - data << name; - SendPacket(&data); + SendPacket(WorldPackets::Chat::ChatPlayerAmbiguous(name).Write()); } -void WorldSession::SendChatRestrictedNotice(ChatRestrictionType restriction) +void WorldSession::SendChatRestricted(ChatRestrictionType restriction) { - WorldPacket data(SMSG_CHAT_RESTRICTED, 1); - data << uint8(restriction); - SendPacket(&data); + WorldPackets::Chat::ChatRestricted packet; + packet.Reason = restriction; + SendPacket(packet.Write()); } diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index 49a10b61ed5..297cd9c3124 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -524,10 +524,8 @@ void WorldSession::HandleSetAssistantLeaderOpcode(WorldPackets::Party::SetAssist group->SetGroupMemberFlag(packet.Target, packet.Apply, MEMBER_FLAG_ASSISTANT); } -void WorldSession::HandlePartyAssignmentOpcode(WorldPacket& recvData) +void WorldSession::HandleSetPartyAssignment(WorldPackets::Party::SetPartyAssignment& packet) { - TC_LOG_DEBUG("network", "WORLD: Received MSG_PARTY_ASSIGNMENT"); - Group* group = GetPlayer()->GetGroup(); if (!group) return; @@ -536,21 +534,15 @@ void WorldSession::HandlePartyAssignmentOpcode(WorldPacket& recvData) if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid)) return; - uint8 assignment; - bool apply; - ObjectGuid guid; - recvData >> assignment >> apply; - recvData >> guid; - - switch (assignment) + switch (packet.Assignment) { case GROUP_ASSIGN_MAINASSIST: group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINASSIST); - group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINASSIST); + group->SetGroupMemberFlag(packet.Target, packet.Set, MEMBER_FLAG_MAINASSIST); break; case GROUP_ASSIGN_MAINTANK: group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINTANK); // Remove main assist flag from current if any. - group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINTANK); + group->SetGroupMemberFlag(packet.Target, packet.Set, MEMBER_FLAG_MAINTANK); default: break; } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 7a88c3a1315..c43c996614a 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -1083,20 +1083,17 @@ void WorldSession::HandleGetItemPurchaseData(WorldPackets::Item::GetItemPurchase GetPlayer()->SendRefundInfo(item); } -void WorldSession::HandleItemRefund(WorldPacket &recvData) +void WorldSession::HandleItemRefund(WorldPackets::Item::ItemPurchaseRefund& packet) { - ObjectGuid guid; - recvData >> guid; // item guid - - Item* item = _player->GetItemByGuid(guid); + Item* item = _player->GetItemByGuid(packet.ItemGUID); if (!item) { - TC_LOG_DEBUG("network", "Item refund: item not found!"); + TC_LOG_DEBUG("network", "WorldSession::HandleItemRefund: Item (%s) not found!", packet.ItemGUID.ToString().c_str()); return; } // Don't try to refund item currently being disenchanted - if (_player->GetLootGUID() == guid) + if (_player->GetLootGUID() == packet.ItemGUID) return; GetPlayer()->RefundItem(item); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index a51b61050ee..b94b4ce838e 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -197,7 +197,7 @@ void WorldSession::HandleWhoOpcode(WorldPackets::Who::WhoRequestPkt& whoRequest) if (!wWords.empty()) { std::string aName; - if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(target->GetZoneId())) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(target->GetZoneId())) aName = areaEntry->AreaName_lang; bool show = false; @@ -724,46 +724,6 @@ void WorldSession::HandleWhoIsOpcode(WorldPackets::Who::WhoIsRequest& packet) SendPacket(response.Write()); } -void WorldSession::HandleComplainOpcode(WorldPacket& recvData) -{ - uint8 spam_type; // 0 - mail, 1 - chat - ObjectGuid spammer_guid; - uint32 unk1 = 0; - uint32 unk2 = 0; - uint32 unk3 = 0; - uint32 unk4 = 0; - std::string description = ""; - recvData >> spam_type; // unk 0x01 const, may be spam type (mail/chat) - recvData >> spammer_guid; // player guid - switch (spam_type) - { - case 0: - recvData >> unk1; // const 0 - recvData >> unk2; // probably mail id - recvData >> unk3; // const 0 - break; - case 1: - recvData >> unk1; // probably language - recvData >> unk2; // message type? - recvData >> unk3; // probably channel id - recvData >> unk4; // time - recvData >> description; // spam description string (messagetype, channel name, player name, message) - break; - } - - // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam. - // if it's mail spam - ALL mails from this spammer automatically removed by client - - // Complaint Received message - WorldPacket data(SMSG_COMPLAINT_RESULT, 2); - data << uint8(0); // value 1 resets CGChat::m_complaintsSystemStatus in client. (unused?) - data << uint8(0); // value 0xC generates a "CalendarError" in client. - SendPacket(&data); - - TC_LOG_DEBUG("network", "REPORT SPAM: type %u, %s, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", - spam_type, spammer_guid.ToString().c_str(), unk1, unk2, unk3, unk4, description.c_str()); -} - void WorldSession::HandleFarSightOpcode(WorldPackets::Misc::FarSight& packet) { if (packet.Enable) @@ -988,21 +948,9 @@ void WorldSession::HandleSetRaidDifficultyOpcode(WorldPackets::Misc::SetRaidDiff } } -void WorldSession::HandleRequestPetInfoOpcode(WorldPacket& /*recvData */) +void WorldSession::HandleSetTaxiBenchmark(WorldPackets::Misc::SetTaxiBenchmarkMode& packet) { - /* - recvData.hexlike(); - */ -} - -void WorldSession::HandleSetTaxiBenchmarkOpcode(WorldPacket& recvData) -{ - uint8 mode; - recvData >> mode; - - mode ? _player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK) : _player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK); - - TC_LOG_DEBUG("network", "Client used \"/timetest %d\" command", mode); + _player->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK, packet.Enable); } void WorldSession::HandleGuildSetFocusedAchievement(WorldPackets::Achievement::GuildSetFocusedAchievement& setFocusedAchievement) @@ -1046,48 +994,6 @@ void WorldSession::HandleInstanceLockResponse(WorldPackets::Instance::InstanceLo _player->SetPendingBind(0, 0); } -void WorldSession::HandleUpdateMissileTrajectory(WorldPacket& recvPacket) -{ - ObjectGuid guid; - uint32 spellId; - float pitch, speed; - float curX, curY, curZ; - float targetX, targetY, targetZ; - uint8 moveStop; - - recvPacket >> guid >> spellId >> pitch >> speed; - recvPacket >> curX >> curY >> curZ; - recvPacket >> targetX >> targetY >> targetZ; - recvPacket >> moveStop; - - Unit* caster = ObjectAccessor::GetUnit(*_player, guid); - Spell* spell = caster ? caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) : NULL; - if (!spell || spell->m_spellInfo->Id != spellId || !spell->m_targets.HasDst() || !spell->m_targets.HasSrc()) - { - recvPacket.rfinish(); - return; - } - - Position pos = *spell->m_targets.GetSrcPos(); - pos.Relocate(curX, curY, curZ); - spell->m_targets.ModSrc(pos); - - pos = *spell->m_targets.GetDstPos(); - pos.Relocate(targetX, targetY, targetZ); - spell->m_targets.ModDst(pos); - - spell->m_targets.SetPitch(pitch); - spell->m_targets.SetSpeed(speed); - - if (moveStop) - { - uint32 opcode; - recvPacket >> opcode; - recvPacket.SetOpcode(CMSG_MOVE_STOP); // always set to CMSG_MOVE_STOP in client SetOpcode - //HandleMovementOpcodes(recvPacket); - } -} - void WorldSession::HandleViolenceLevel(WorldPackets::Misc::ViolenceLevel& /*violenceLevel*/) { // do something? diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index fb391229340..3ab9f683dea 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -382,7 +382,7 @@ void WorldSession::HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMov plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); - if (movementInfo.pos.GetPositionZ() < MAX_MAP_DEPTH) + if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY())) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { @@ -391,6 +391,7 @@ void WorldSession::HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMov /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { + plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index e514a1c2767..7590ef2b120 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -256,7 +256,7 @@ void WorldSession::SendTrainerBuyFailed(ObjectGuid trainerGUID, uint32 spellID, void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) { - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_NONE); + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_GOSSIP); if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleGossipHelloOpcode - %s not found or you can not interact with him.", packet.Unit.ToString().c_str()); @@ -296,7 +296,6 @@ void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) unit->AI()->sGossipHello(_player); } - void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelectOption& packet) { if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(packet.GossipIndex)) @@ -310,20 +309,19 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelec GameObject* go = nullptr; if (packet.GossipUnit.IsCreatureOrVehicle()) { - unit = GetPlayer()->GetNPCIfCanInteractWith(packet.GossipUnit, UNIT_NPC_FLAG_NONE); + unit = GetPlayer()->GetNPCIfCanInteractWith(packet.GossipUnit, UNIT_NPC_FLAG_GOSSIP); if (!unit) { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", packet.GossipUnit.ToString().c_str()); return; } } else if (packet.GossipUnit.IsGameObject()) { - go = _player->GetMap()->GetGameObject(packet.GossipUnit); + go = _player->GetGameObjectIfCanInteractWith(packet.GossipUnit); if (!go) { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found.", packet.GossipUnit.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", packet.GossipUnit.ToString().c_str()); return; } } @@ -465,14 +463,9 @@ void WorldSession::SendBindPoint(Creature* npc) _player->PlayerTalkClass->SendCloseGossip(); } -void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData) +void WorldSession::HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPets& packet) { - TC_LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS"); - ObjectGuid npcGUID; - - recvData >> npcGUID; - - if (!CheckStableMaster(npcGUID)) + if (!CheckStableMaster(packet.StableMaster)) return; // remove fake death @@ -483,7 +476,7 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData) if (GetPlayer()->IsMounted()) GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED); - SendStablePet(npcGUID); + SendStablePet(packet.StableMaster); } void WorldSession::SendStablePet(ObjectGuid guid) diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 83792e6fc05..6f62fc7cd5a 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -33,22 +33,18 @@ #include "SpellHistory.h" #include "SpellInfo.h" #include "Player.h" +#include "PetPackets.h" #include "SpellPackets.h" #include "QueryPackets.h" -void WorldSession::HandleDismissCritter(WorldPacket& recvData) +void WorldSession::HandleDismissCritter(WorldPackets::Pet::DismissCritter& packet) { - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network", "WORLD: Received CMSG_DISMISS_CRITTER for %s", guid.ToString().c_str()); - - Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.CritterGUID); if (!pet) { TC_LOG_DEBUG("network", "Vanitypet (%s) does not exist - player '%s' (%s / account: %u) attempted to dismiss it (possibly lagged out)", - guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetAccountId()); + packet.CritterGUID.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetAccountId()); return; } @@ -59,6 +55,10 @@ void WorldSession::HandleDismissCritter(WorldPacket& recvData) } } +void WorldSession::HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo& /*packet */) +{ +} + void WorldSession::HandlePetAction(WorldPacket& recvData) { ObjectGuid guid1; @@ -119,25 +119,20 @@ void WorldSession::HandlePetAction(WorldPacket& recvData) } } -void WorldSession::HandlePetStopAttack(WorldPacket &recvData) +void WorldSession::HandlePetStopAttack(WorldPackets::Pet::PetStopAttack& packet) { - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_STOP_ATTACK for %s", guid.ToString().c_str()); - - Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.PetGUID); if (!pet) { - TC_LOG_ERROR("network", "HandlePetStopAttack: %s does not exist", guid.ToString().c_str()); + TC_LOG_ERROR("network", "HandlePetStopAttack: %s does not exist", packet.PetGUID.ToString().c_str()); return; } if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { TC_LOG_ERROR("network", "HandlePetStopAttack: %s isn't a pet or charmed creature of player %s", - guid.ToString().c_str(), GetPlayer()->GetName().c_str()); + packet.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str()); return; } @@ -666,71 +661,61 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped } -void WorldSession::HandlePetAbandon(WorldPacket& recvData) +void WorldSession::HandlePetAbandon(WorldPackets::Pet::PetAbandon& packet) { - ObjectGuid guid; - recvData >> guid; //pet guid - TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_ABANDON %s", guid.ToString().c_str()); - if (!_player->IsInWorld()) return; - // pet/charmed - Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.Pet); if (pet) { if (pet->IsPet()) - _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); + _player->RemovePet(pet->ToPet(), PET_SAVE_AS_DELETED); else if (pet->GetGUID() == _player->GetCharmGUID()) _player->StopCastingCharm(); } } -void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) +void WorldSession::HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast& packet) { - ObjectGuid guid; - uint32 spellid; - uint8 state; //1 for on, 0 for off - recvPacket >> guid >> spellid >> state; - - if (!_player->GetGuardianPet() && !_player->GetCharm()) - return; - - if (ObjectAccessor::FindPlayer(guid)) + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.PetGUID); + if (!pet) + { + TC_LOG_ERROR("network", "WorldSession::HandlePetSpellAutocastOpcode: Pet %s not found.", packet.PetGUID.ToString().c_str()); return; + } - Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - - if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm())) + if (pet != _player->GetGuardianPet() && pet != _player->GetCharm()) { - TC_LOG_ERROR("network", "HandlePetSpellAutocastOpcode. %s isn't pet of player %s (%s).", guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); + TC_LOG_ERROR("network", "WorldSession::HandlePetSpellAutocastOpcode: %s isn't pet of player %s (%s).", + packet.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); return; } - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID); if (!spellInfo) { - TC_LOG_ERROR("network", "WORLD: unknown PET spell id %u", spellid); + TC_LOG_ERROR("network", "WorldSession::HandlePetSpellAutocastOpcode: Unknown spell id %u used by %s.", packet.SpellID, packet.PetGUID.ToString().c_str()); return; } // do not add not learned spells/ passive spells - if (!pet->HasSpell(spellid) || !spellInfo->IsAutocastable()) + if (!pet->HasSpell(packet.SpellID) || !spellInfo->IsAutocastable()) return; CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { - TC_LOG_ERROR("network", "WorldSession::HandlePetSpellAutocastOpcod: object (%s) is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString().c_str()); + TC_LOG_ERROR("network", "WorldSession::HandlePetSpellAutocastOpcode: object (%s) is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString().c_str()); return; } if (pet->IsPet()) - ((Pet*)pet)->ToggleAutocast(spellInfo, state != 0); + pet->ToPet()->ToggleAutocast(spellInfo, packet.AutocastEnabled); else - pet->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, state != 0); + charmInfo->ToggleCreatureAutocast(spellInfo, packet.AutocastEnabled); - charmInfo->SetSpellAutocast(spellInfo, state != 0); + charmInfo->SetSpellAutocast(spellInfo, packet.AutocastEnabled); } void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& petCastSpell) @@ -738,19 +723,22 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID); if (!spellInfo) { - TC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", petCastSpell.Cast.SpellID); + TC_LOG_ERROR("network", "WorldSession::HandlePetCastSpellOpcode: unknown spell id %i tried to cast by %s", + petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString().c_str()); return; } - // This opcode is also sent from charmed and possessed units (players and creatures) - if (!_player->GetGuardianPet() && !_player->GetCharm()) - return; - Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID); + if (!caster) + { + TC_LOG_ERROR("network", "WorldSession::HandlePetCastSpellOpcode: Caster %s not found.", petCastSpell.PetGUID.ToString().c_str()); + return; + } - if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) + // This opcode is also sent from charmed and possessed units (players and creatures) + if (caster != _player->GetGuardianPet() && caster != _player->GetCharm()) { - TC_LOG_ERROR("network", "HandlePetCastSpellOpcode: %s isn't pet of player %s (%s).", petCastSpell.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); + TC_LOG_ERROR("network", "WorldSession::HandlePetCastSpellOpcode: %s isn't pet of player %s (%s).", petCastSpell.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); return; } diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 83369002096..0b496100346 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -71,7 +71,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHe { TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO %s", packet.QuestGiverGUID.ToString().c_str()); - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.QuestGiverGUID, UNIT_NPC_FLAG_NONE); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.QuestGiverGUID, UNIT_NPC_FLAG_QUESTGIVER); if (!creature) { TC_LOG_DEBUG("network", "WORLD: HandleQuestgiverHelloOpcode - %s not found or you can't interact with him.", @@ -543,17 +543,12 @@ void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket& /*recvPacket*/) { } -void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket) +void WorldSession::HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet) { - uint32 questId; - recvPacket >> questId; - - if (!_player->CanShareQuest(questId)) + if (!_player->CanShareQuest(packet.QuestID)) return; - TC_LOG_DEBUG("network", "WORLD: Received CMSG_PUSHQUESTTOPARTY questId = %u", questId); - - Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID); if (!quest) return; @@ -576,7 +571,7 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket) continue; } - if (receiver->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE) + if (receiver->GetQuestStatus(packet.QuestID) == QUEST_STATUS_COMPLETE) { sender->SendPushToPartyResponse(receiver, QUEST_PUSH_ALREADY_DONE); continue; diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index 59ffde0d6b4..2060468a41a 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -37,16 +37,18 @@ void WorldSession::HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& _player->SendTalentsInfoData(); } -void WorldSession::HandleConfirmRespecWipeOpcode(WorldPacket& recvData) +void WorldSession::HandleConfirmRespecWipeOpcode(WorldPackets::Talent::ConfirmRespecWipe& confirmRespecWipe) { - TC_LOG_DEBUG("network", "MSG_TALENT_WIPE_CONFIRM"); - ObjectGuid guid; - recvData >> guid; - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(confirmRespecWipe.RespecMaster, UNIT_NPC_FLAG_TRAINER); if (!unit) { - TC_LOG_DEBUG("network", "WORLD: HandleConfirmRespecWipeOpcode - %s not found or you can't interact with him.", guid.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: HandleConfirmRespecWipeOpcode - %s not found or you can't interact with him.", confirmRespecWipe.RespecMaster.ToString().c_str()); + return; + } + + if (confirmRespecWipe.RespecType != SPEC_RESET_TALENTS) + { + TC_LOG_DEBUG("network", "WORLD: HandleConfirmRespecWipeOpcode - reset type %d is not implemented.", confirmRespecWipe.RespecType); return; } diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index b58e2fca69e..4a28c076ce6 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -216,11 +216,8 @@ void WorldSession::HandleOpenItemOpcode(WorldPackets::Spells::OpenItem& packet) void WorldSession::HandleGameObjectUseOpcode(WorldPackets::GameObject::GameObjUse& packet) { - if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(packet.Guid)) + if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(packet.Guid)) { - if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance())) - return; - // ignore for remote control state if (GetPlayer()->m_mover != GetPlayer()) if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) @@ -236,17 +233,13 @@ void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjRe if (_player->m_mover != _player) return; - GameObject* go = GetPlayer()->GetMap()->GetGameObject(packet.Guid); - if (!go) - return; - - if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) - return; - - if (go->AI()->GossipHello(_player)) - return; + if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(packet.Guid)) + { + if (go->AI()->GossipHello(_player)) + return; - _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + } } void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast) @@ -367,32 +360,26 @@ void WorldSession::HandleCancelAuraOpcode(WorldPackets::Spells::CancelAura& canc } } -void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket) +void WorldSession::HandlePetCancelAuraOpcode(WorldPackets::Spells::PetCancelAura& packet) { - ObjectGuid guid; - uint32 spellId; - - recvPacket >> guid; - recvPacket >> spellId; - - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID); if (!spellInfo) { - TC_LOG_ERROR("network", "WORLD: unknown PET spell id %u", spellId); + TC_LOG_ERROR("network", "WORLD: unknown PET spell id %u", packet.SpellID); return; } - Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.PetGUID); if (!pet) { - TC_LOG_ERROR("network", "HandlePetCancelAura: Attempt to cancel an aura for non-existant %s by player '%s'", guid.ToString().c_str(), GetPlayer()->GetName().c_str()); + TC_LOG_ERROR("network", "HandlePetCancelAura: Attempt to cancel an aura for non-existant %s by player '%s'", packet.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str()); return; } if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm()) { - TC_LOG_ERROR("network", "HandlePetCancelAura: %s is not a pet of player '%s'", guid.ToString().c_str(), GetPlayer()->GetName().c_str()); + TC_LOG_ERROR("network", "HandlePetCancelAura: %s is not a pet of player '%s'", packet.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str()); return; } @@ -402,7 +389,7 @@ void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket) return; } - pet->RemoveOwnedAura(spellId, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL); + pet->RemoveOwnedAura(packet.SpellID, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL); } void WorldSession::HandleCancelGrowthAuraOpcode(WorldPackets::Spells::CancelGrowthAura& /*cancelGrowthAura*/) @@ -568,39 +555,53 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI } } -void WorldSession::HandleUpdateProjectilePosition(WorldPacket& recvPacket) +void WorldSession::HandleMissileTrajectoryCollision(WorldPackets::Spells::MissileTrajectoryCollision& packet) { - ObjectGuid casterGuid; - uint32 spellId; - uint8 castCount; - float x, y, z; // Position of missile hit - - recvPacket >> casterGuid; - recvPacket >> spellId; - recvPacket >> castCount; - recvPacket >> x; - recvPacket >> y; - recvPacket >> z; - - Unit* caster = ObjectAccessor::GetUnit(*_player, casterGuid); + Unit* caster = ObjectAccessor::GetUnit(*_player, packet.Target); if (!caster) return; - Spell* spell = caster->FindCurrentSpellBySpellId(spellId); + Spell* spell = caster->FindCurrentSpellBySpellId(packet.SpellID); if (!spell || !spell->m_targets.HasDst()) return; Position pos = *spell->m_targets.GetDstPos(); - pos.Relocate(x, y, z); + pos.Relocate(packet.CollisionPos); spell->m_targets.ModDst(pos); - WorldPacket data(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, 21); - data << casterGuid; - data << uint8(castCount); - data << float(x); - data << float(y); - data << float(z); - caster->SendMessageToSet(&data, true); + WorldPackets::Spells::NotifyMissileTrajectoryCollision notify; + notify.Caster = packet.Target; + notify.CastID = packet.CastID; + notify.CollisionPos = packet.CollisionPos; + caster->SendMessageToSet(notify.Write(), true); +} + +void WorldSession::HandleUpdateMissileTrajectory(WorldPackets::Spells::UpdateMissileTrajectory& packet) +{ + Unit* caster = ObjectAccessor::GetUnit(*_player, packet.Guid); + Spell* spell = caster ? caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) : NULL; + if (!spell || spell->m_spellInfo->Id != uint32(packet.SpellID) || !spell->m_targets.HasDst() || !spell->m_targets.HasSrc()) + return; + + Position pos = *spell->m_targets.GetSrcPos(); + pos.Relocate(packet.FirePos); + spell->m_targets.ModSrc(pos); + + pos = *spell->m_targets.GetDstPos(); + pos.Relocate(packet.ImpactPos); + spell->m_targets.ModDst(pos); + + spell->m_targets.SetPitch(packet.Pitch); + spell->m_targets.SetSpeed(packet.Speed); + + if (packet.Status.is_initialized()) + { + GetPlayer()->ValidateMovementInfo(packet.Status.get_ptr()); + /*uint32 opcode; + recvPacket >> opcode; + recvPacket.SetOpcode(CMSG_MOVE_STOP); // always set to CMSG_MOVE_STOP in client SetOpcode + //HandleMovementOpcodes(recvPacket);*/ + } } void WorldSession::HandleRequestCategoryCooldowns(WorldPackets::Spells::RequestCategoryCooldowns& /*requestCategoryCooldowns*/) diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index 907cd9cd6f4..6f7a6acaa10 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -95,3 +95,13 @@ void WorldSession::HandleBugReportOpcode(WorldPackets::Ticket::BugReport& bugRep stmt->setString(1, bugReport.DiagInfo); CharacterDatabase.Execute(stmt); } + +void WorldSession::HandleComplaint(WorldPackets::Ticket::Complaint& packet) +{ // NOTE: all chat messages from this spammer are automatically ignored by the spam reporter until logout in case of chat spam. + // if it's mail spam - ALL mails from this spammer are automatically removed by client + + WorldPackets::Ticket::ComplaintResult result; + result.ComplaintType = packet.ComplaintType; + result.Result = 0; + SendPacket(result.Write()); +} diff --git a/src/server/game/Handlers/VoiceChatHandler.cpp b/src/server/game/Handlers/VoiceChatHandler.cpp index 4c96e6bdebb..37d23c120e7 100644 --- a/src/server/game/Handlers/VoiceChatHandler.cpp +++ b/src/server/game/Handlers/VoiceChatHandler.cpp @@ -16,20 +16,14 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "Common.h" -#include "WorldPacket.h" #include "WorldSession.h" +#include "VoicePackets.h" -void WorldSession::HandleVoiceSessionEnableOpcode(WorldPacket& recvData) +void WorldSession::HandleVoiceSessionEnable(WorldPackets::Voice::VoiceSessionEnable& /*packet*/) { - // uint8 isVoiceEnabled, uint8 isMicrophoneEnabled - recvData.read_skip<uint8>(); - recvData.read_skip<uint8>(); } -void WorldSession::HandleSetActiveVoiceChannel(WorldPacket& recvData) +void WorldSession::HandleSetActiveVoiceChannel(WorldPackets::Voice::SetActiveVoiceChannel& /*packet*/) { - recvData.read_skip<uint32>(); - recvData.read_skip<char*>(); } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index bbeb2ce3040..e57bf681b8a 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -1586,8 +1586,8 @@ void LoadLootTemplates_Fishing() uint32 count = LootTemplates_Fishing.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot - for (uint32 i = 1; i < sAreaStore.GetNumRows(); ++i) - if (AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i)) + for (uint32 i = 1; i < sAreaTableStore.GetNumRows(); ++i) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i)) if (lootIdSet.find(areaEntry->ID) != lootIdSet.end()) lootIdSet.erase(areaEntry->ID); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index d1bc8a278c4..9335539bc6e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -40,7 +40,7 @@ #include "Weather.h" u_map_magic MapMagic = { {'M','A','P','S'} }; -u_map_magic MapVersionMagic = { {'v','1','.','5'} }; +u_map_magic MapVersionMagic = { {'v','1','.','8'} }; u_map_magic MapAreaMagic = { {'A','R','E','A'} }; u_map_magic MapHeightMagic = { {'M','H','G','T'} }; u_map_magic MapLiquidMagic = { {'M','L','I','Q'} }; @@ -1655,13 +1655,15 @@ GridMap::GridMap() _flags = 0; // Area data _gridArea = 0; - _areaMap = NULL; + _areaMap = nullptr; // Height level data _gridHeight = INVALID_HEIGHT; _gridGetHeight = &GridMap::getHeightFromFlat; _gridIntHeightMultiplier = 0; - m_V9 = NULL; - m_V8 = NULL; + m_V9 = nullptr; + m_V8 = nullptr; + _maxHeight = nullptr; + _minHeight = nullptr; // Liquid data _liquidType = 0; _liquidOffX = 0; @@ -1669,9 +1671,9 @@ GridMap::GridMap() _liquidWidth = 0; _liquidHeight = 0; _liquidLevel = INVALID_HEIGHT; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; } GridMap::~GridMap() @@ -1734,15 +1736,19 @@ void GridMap::unloadData() delete[] _areaMap; delete[] m_V9; delete[] m_V8; + delete[] _maxHeight; + delete[] _minHeight; delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; - _areaMap = NULL; - m_V9 = NULL; - m_V8 = NULL; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _areaMap = nullptr; + m_V9 = nullptr; + m_V8 = nullptr; + _maxHeight = nullptr; + _minHeight = nullptr; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; } @@ -1757,7 +1763,7 @@ bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/) _gridArea = header.gridArea; if (!(header.flags & MAP_AREA_NO_AREA)) { - _areaMap = new uint16 [16*16]; + _areaMap = new uint16[16 * 16]; if (fread(_areaMap, sizeof(uint16), 16*16, in) != 16*16) return false; } @@ -1807,6 +1813,16 @@ bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/) } else _gridGetHeight = &GridMap::getHeightFromFlat; + + if (header.flags & MAP_HEIGHT_HAS_FLIGHT_BOUNDS) + { + _maxHeight = new int16[3 * 3]; + _minHeight = new int16[3 * 3]; + if (fread(_maxHeight, sizeof(int16), 3 * 3, in) != 3 * 3 || + fread(_minHeight, sizeof(int16), 3 * 3, in) != 3 * 3) + return false; + } + return true; } @@ -2077,6 +2093,66 @@ float GridMap::getHeightFromUint16(float x, float y) const return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight; } +float GridMap::getMinHeight(float x, float y) const +{ + if (!_minHeight) + return -500.0f; + + static uint32 const indices[] = + { + 3, 0, 4, + 0, 1, 4, + 1, 2, 4, + 2, 5, 4, + 5, 8, 4, + 8, 7, 4, + 7, 6, 4, + 6, 3, 4 + }; + + static float const boundGridCoords[] = + { + 0.0f, 0.0f, + 0.0f, -266.66666f, + 0.0f, -533.33331f, + -266.66666f, 0.0f, + -266.66666f, -266.66666f, + -266.66666f, -533.33331f, + -533.33331f, 0.0f, + -533.33331f, -266.66666f, + -533.33331f, -533.33331f + }; + + Cell cell(x, y); + float gx = x - (int32(cell.GridX()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + float gy = y - (int32(cell.GridY()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + + uint32 quarterIndex = 0; + if (cell.CellY() < MAX_NUMBER_OF_CELLS / 2) + { + if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2) + { + quarterIndex = 4 + (gy > gx); + } + else + quarterIndex = 2 + ((-SIZE_OF_GRIDS - gx) > gy); + } + else if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2) + { + quarterIndex = 6 + ((-SIZE_OF_GRIDS - gx) <= gy); + } + else + quarterIndex = gx > gy; + + quarterIndex *= 3; + + return G3D::Plane( + G3D::Vector3(boundGridCoords[indices[quarterIndex + 0] * 2 + 0], boundGridCoords[indices[quarterIndex + 0] * 2 + 1], _minHeight[indices[quarterIndex + 0]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex + 1] * 2 + 0], boundGridCoords[indices[quarterIndex + 1] * 2 + 1], _minHeight[indices[quarterIndex + 1]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex + 2] * 2 + 0], boundGridCoords[indices[quarterIndex + 2] * 2 + 1], _minHeight[indices[quarterIndex + 2]]) + ).distance(G3D::Vector3(gx, gy, 0.0f)); +} + float GridMap::getLiquidLevel(float x, float y) const { if (!_liquidMap) @@ -2136,12 +2212,12 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R uint32 liqTypeIdx = liquidEntry->Type; if (entry < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(getArea(x, y), MAPID_INVALID)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { uint32 overrideLiquid = area->LiquidTypeID[liquidEntry->Type]; if (!overrideLiquid && area->ParentAreaID) { - area = GetAreaEntryByAreaID(area->ParentAreaID); + area = sAreaTableStore.LookupEntry(area->ParentAreaID); if (area) overrideLiquid = area->LiquidTypeID[liquidEntry->Type]; } @@ -2277,6 +2353,14 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float return mapHeight; // explicitly use map data } +float Map::GetMinHeight(float x, float y) const +{ + if (GridMap const* grid = const_cast<Map*>(this)->GetGrid(x, y)) + return grid->getMinHeight(x, y); + + return -500.0f; +} + inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry) { bool outdoor = true; @@ -2315,7 +2399,7 @@ bool Map::IsOutdoors(float x, float y, float z) const if (wmoEntry) { TC_LOG_DEBUG("maps", "Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->AreaTableID); - atEntry = GetAreaEntryByAreaID(wmoEntry->AreaTableID); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->AreaTableID); } return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } @@ -2339,7 +2423,7 @@ bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, in return false; } -uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const +uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const { uint32 mogpFlags; int32 adtId, rootId, groupId; @@ -2352,20 +2436,21 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const haveAreaInfo = true; wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); if (wmoEntry) - atEntry = GetAreaEntryByAreaID(wmoEntry->AreaTableID); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->AreaTableID); } - uint16 areaflag; + uint32 areaId = 0; if (atEntry) - areaflag = atEntry->AreaBit; + areaId = atEntry->ID; else { if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) - areaflag = gmap->getArea(x, y); + areaId = gmap->getArea(x, y); + // this used while not all *.map files generated (instances) - else - areaflag = GetAreaFlagByMapId(i_mapEntry->ID); + if (!areaId) + areaId = i_mapEntry->AreaTableID; } if (isOutdoors) @@ -2375,8 +2460,31 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const else *isOutdoors = true; } - return areaflag; - } + return areaId; +} + +uint32 Map::GetAreaId(float x, float y, float z) const +{ + return GetAreaId(x, y, z, nullptr); +} + +uint32 Map::GetZoneId(float x, float y, float z) const +{ + uint32 areaId = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) + if (area->ParentAreaID) + return area->ParentAreaID; + + return areaId; +} + +void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const +{ + areaid = zoneid = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) + if (area->ParentAreaID) + zoneid = area->ParentAreaID; +} uint8 Map::GetTerrainType(float x, float y) const { @@ -2412,12 +2520,12 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp if (liquid_type && liquid_type < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(GetAreaFlag(x, y, z), GetId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z))) { uint32 overrideLiquid = area->LiquidTypeID[liquidFlagType]; if (!overrideLiquid && area->ParentAreaID) { - area = GetAreaEntryByAreaID(area->ParentAreaID); + area = sAreaTableStore.LookupEntry(area->ParentAreaID); if (area) overrideLiquid = area->LiquidTypeID[liquidFlagType]; } @@ -2479,34 +2587,6 @@ float Map::GetWaterLevel(float x, float y) const return 0; } -uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return entry->ID; - else - return 0; -} - -uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return (entry->ParentAreaID != 0) ? entry->ParentAreaID : entry->ID; - else - return 0; -} - -void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - areaid = entry ? entry->ID : 0; - zoneid = entry ? ((entry->ParentAreaID != 0) ? entry->ParentAreaID : entry->ID) : 0; -} - bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const { return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 3ee28f69f12..34aa00e4bf9 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -100,9 +100,10 @@ struct map_areaHeader uint16 gridArea; }; -#define MAP_HEIGHT_NO_HEIGHT 0x0001 -#define MAP_HEIGHT_AS_INT16 0x0002 -#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_HAS_FLIGHT_BOUNDS 0x0008 struct map_heightHeader { @@ -168,6 +169,8 @@ class GridMap uint16* m_uint16_V8; uint8* m_uint8_V8; }; + int16* _maxHeight; + int16* _minHeight; // Height level data float _gridHeight; float _gridIntHeightMultiplier; @@ -208,6 +211,7 @@ public: uint16 getArea(float x, float y) const; inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);} + float getMinHeight(float x, float y) const; float getLiquidLevel(float x, float y) const; uint8 getTerrainType(float x, float y) const; ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = 0); @@ -329,11 +333,15 @@ class Map : public GridRefManager<NGridType> // some calls like isInWater should not use vmaps due to processor power // can return INVALID_HEIGHT if under z+2 z coord not found height float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + float GetMinHeight(float x, float y) const; ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; - uint16 GetAreaFlag(float x, float y, float z, bool *isOutdoors=nullptr) const; - bool GetAreaInfo(float x, float y, float z, uint32 &mogpflags, int32 &adtId, int32 &rootId, int32 &groupId) const; + uint32 GetAreaId(float x, float y, float z, bool *isOutdoors) const; + bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; + uint32 GetAreaId(float x, float y, float z) const; + uint32 GetZoneId(float x, float y, float z) const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; bool IsOutdoors(float x, float y, float z) const; @@ -342,25 +350,6 @@ class Map : public GridRefManager<NGridType> bool IsInWater(float x, float y, float z, LiquidData* data = nullptr) const; bool IsUnderWater(float x, float y, float z) const; - static uint32 GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id); - static uint32 GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id); - static void GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id); - - uint32 GetAreaId(float x, float y, float z) const - { - return GetAreaIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - uint32 GetZoneId(float x, float y, float z) const - { - return GetZoneIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const - { - GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(x, y, z), GetId()); - } - void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); void MoveAllDynamicObjectsInMoveList(); diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 086277448a8..c0811e7fecc 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -42,22 +42,20 @@ class MapManager Map* CreateMap(uint32 mapId, Player* player); Map* FindMap(uint32 mapId, uint32 instanceId) const; - uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const - { - Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); - return m->GetAreaFlag(x, y, z); - } uint32 GetAreaId(uint32 mapid, float x, float y, float z) const { - return Map::GetAreaIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + return m->GetAreaId(x, y, z); } uint32 GetZoneId(uint32 mapid, float x, float y, float z) const { - return Map::GetZoneIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + return m->GetZoneId(x, y, z); } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) { - Map::GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + m->GetZoneAndAreaId(zoneid, areaid, x, y, z); } void Initialize(void); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 88b9190af6f..238c39ed2c9 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -85,7 +85,7 @@ enum Expansions MAX_EXPANSIONS = 6 }; -#define CURRENT_EXPANSION EXPANSION_WARLORDS_OF_DRAENOR; +#define CURRENT_EXPANSION EXPANSION_WARLORDS_OF_DRAENOR enum Gender { diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index d10b196f08d..4f05c1871aa 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -650,7 +650,7 @@ void OutdoorPvP::BroadcastWorker(Worker& _worker, uint32 zoneId) void OutdoorPvP::SetMapFromZone(uint32 zone) { - AreaTableEntry const* areaTable = GetAreaEntryByAreaID(zone); + AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(zone); ASSERT(areaTable); Map* map = sMapMgr->CreateBaseMap(areaTable->MapID); ASSERT(!map->Instanceable()); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 774c4e279d0..38ca4911a21 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -37,7 +37,6 @@ // namespace // { - UnusedScriptContainer UnusedScripts; UnusedScriptNamesContainer UnusedScriptNames; // } @@ -107,8 +106,9 @@ class ScriptRegistry // The actual list of scripts. This will be accessed concurrently, so it must not be modified // after server startup. static ScriptMap ScriptPointerList; + static std::vector<TScript*> Scripts; - static void AddScript(TScript* const script) + static void AddScript(TScript* const script, bool addToDeleteContainer = true) { ASSERT(script); @@ -126,6 +126,8 @@ class ScriptRegistry } AddScript(is_script_database_bound<TScript>{}, script); + if (addToDeleteContainer) + Scripts.push_back(script); } // Gets a script by its ID (assigned by ObjectMgr). @@ -186,11 +188,6 @@ class ScriptRegistry { // The script uses a script name from database, but isn't assigned to anything. TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str()); - - // Avoid calling "delete script;" because we are currently in the script constructor - // In a valid scenario this will not happen because every script has a name assigned in the database - UnusedScripts.push_back(script); - return; } } @@ -210,6 +207,7 @@ class ScriptRegistry #define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptMap #define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptMapIterator #define SCR_REG_LST(T) ScriptRegistry<T>::ScriptPointerList +#define SCR_REG_VEC(T) ScriptRegistry<T>::Scripts // Utility macros for looping over scripts. #define FOR_SCRIPTS(T, C, E) \ @@ -266,17 +264,15 @@ void ScriptMgr::Initialize() } #endif - UnloadUnusedScripts(); - TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime)); } void ScriptMgr::Unload() { #define SCR_CLEAR(T) \ - for (SCR_REG_ITR(T) itr = SCR_REG_LST(T).begin(); itr != SCR_REG_LST(T).end(); ++itr) \ - delete itr->second; \ - SCR_REG_LST(T).clear(); + for (T* scr : SCR_REG_VEC(T)) \ + delete scr; \ + SCR_REG_VEC(T).clear(); // Clear scripts for every script type. SCR_CLEAR(SpellScriptLoader); @@ -308,19 +304,10 @@ void ScriptMgr::Unload() #undef SCR_CLEAR - UnloadUnusedScripts(); - delete[] SpellSummary; delete[] UnitAI::AISpellInfo; } -void ScriptMgr::UnloadUnusedScripts() -{ - for (size_t i = 0; i < UnusedScripts.size(); ++i) - delete UnusedScripts[i]; - UnusedScripts.clear(); -} - void ScriptMgr::LoadDatabase() { sScriptSystemMgr->LoadScriptWaypoints(); @@ -1558,8 +1545,7 @@ FormulaScript::FormulaScript(const char* name) UnitScript::UnitScript(const char* name, bool addToScripts) : ScriptObject(name) { - if (addToScripts) - ScriptRegistry<UnitScript>::AddScript(this); + ScriptRegistry<UnitScript>::AddScript(this, addToScripts); } WorldMapScript::WorldMapScript(const char* name, uint32 mapId) @@ -1699,6 +1685,7 @@ GroupScript::GroupScript(const char* name) // Instantiate static members of ScriptRegistry. template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList; +template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::Scripts; template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0; // Specialize for each script type class like so: diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 49d561f8885..aac2cdeabfa 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -842,10 +842,7 @@ class GroupScript : public ScriptObject // namespace // { - typedef std::vector<ScriptObject*> UnusedScriptContainer; typedef std::list<std::string> UnusedScriptNamesContainer; - - extern UnusedScriptContainer UnusedScripts; extern UnusedScriptNamesContainer UnusedScriptNames; // } @@ -877,7 +874,6 @@ class ScriptMgr public: /* Unloading */ void Unload(); - void UnloadUnusedScripts(); public: /* SpellScriptLoader */ diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 186d702cc8b..caf895abe03 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -50,6 +50,7 @@ #include "NPCPackets.h" #include "PacketUtilities.h" #include "PartyPackets.h" +#include "PetPackets.h" #include "PetitionPackets.h" #include "QueryPackets.h" #include "QuestPackets.h" @@ -67,7 +68,9 @@ #include "ToyPackets.h" #include "TradePackets.h" #include "VehiclePackets.h" +#include "VoicePackets.h" #include "VoidStoragePackets.h" +#include "WardenPackets.h" #include "WhoPackets.h" #include "WorldStatePackets.h" diff --git a/src/server/game/Server/Packets/BattlegroundPackets.cpp b/src/server/game/Server/Packets/BattlegroundPackets.cpp index cc1eee44c2c..52b99035141 100644 --- a/src/server/game/Server/Packets/BattlegroundPackets.cpp +++ b/src/server/game/Server/Packets/BattlegroundPackets.cpp @@ -35,6 +35,14 @@ void WorldPackets::Battleground::AreaSpiritHealerQueue::Read() _worldPacket >> HealerGuid; } +WorldPacket const* WorldPackets::Battleground::AreaSpiritHealerTime::Write() +{ + _worldPacket << HealerGuid; + _worldPacket << int32(TimeLeft); + + return &_worldPacket; +} + ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Battleground::PVPLogData::RatingData const& ratingData) { data.append(ratingData.Prematch, 2); diff --git a/src/server/game/Server/Packets/BattlegroundPackets.h b/src/server/game/Server/Packets/BattlegroundPackets.h index 7d9c6017db8..e67860e6727 100644 --- a/src/server/game/Server/Packets/BattlegroundPackets.h +++ b/src/server/game/Server/Packets/BattlegroundPackets.h @@ -59,6 +59,17 @@ namespace WorldPackets ObjectGuid HealerGuid; }; + class AreaSpiritHealerTime final : public ServerPacket + { + public: + AreaSpiritHealerTime() : ServerPacket(SMSG_AREA_SPIRIT_HEALER_TIME, 14 + 4) { } + + WorldPacket const* Write() override; + + ObjectGuid HealerGuid; + int32 TimeLeft = 0; + }; + class HearthAndResurrect final : public ClientPacket { public: @@ -378,6 +389,22 @@ namespace WorldPackets ObjectGuid Guid; }; + + class RequestPVPRewards final : public ClientPacket + { + public: + RequestPVPRewards(WorldPacket&& packet) : ClientPacket(CMSG_REQUEST_PVP_REWARDS, std::move(packet)) { } + + void Read() override { } + }; + + class RequestRatedBattlefieldInfo final : public ClientPacket + { + public: + RequestRatedBattlefieldInfo(WorldPacket&& packet) : ClientPacket(CMSG_REQUEST_RATED_BATTLEFIELD_INFO, std::move(packet)) { } + + void Read() override { } + }; } } diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index ef41d16c2b2..d54402d40c7 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -545,3 +545,24 @@ WorldPacket const* WorldPackets::Character::CharCustomizeFailed::Write() return &_worldPacket; } + +void WorldPackets::Character::SetPlayerDeclinedNames::Read() +{ + _worldPacket >> Player; + + uint8 stringLengths[MAX_DECLINED_NAME_CASES]; + + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + stringLengths[i] = _worldPacket.ReadBits(7); + + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + DeclinedNames.name[i] = _worldPacket.ReadString(stringLengths[i]); +} + +WorldPacket const * WorldPackets::Character::SetPlayerDeclinedNamesResult::Write() +{ + _worldPacket << int32(ResultCode); + _worldPacket << Player; + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/CharacterPackets.h b/src/server/game/Server/Packets/CharacterPackets.h index cbf4f55906c..bb163b075cc 100644 --- a/src/server/game/Server/Packets/CharacterPackets.h +++ b/src/server/game/Server/Packets/CharacterPackets.h @@ -716,6 +716,28 @@ namespace WorldPackets uint8 Result = 0; ObjectGuid CharGUID; }; + + class SetPlayerDeclinedNames final : public ClientPacket + { + public: + SetPlayerDeclinedNames(WorldPacket&& packet) : ClientPacket(CMSG_SET_PLAYER_DECLINED_NAMES, std::move(packet)) { } + + void Read() override; + + ObjectGuid Player; + DeclinedName DeclinedNames; + }; + + class SetPlayerDeclinedNamesResult final : public ServerPacket + { + public: + SetPlayerDeclinedNamesResult() : ServerPacket(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 8 + 4) { } + + WorldPacket const* Write() override; + + ObjectGuid Player; + int32 ResultCode = 0; + }; } } diff --git a/src/server/game/Server/Packets/ChatPackets.cpp b/src/server/game/Server/Packets/ChatPackets.cpp index be923c1b847..612b46ca919 100644 --- a/src/server/game/Server/Packets/ChatPackets.cpp +++ b/src/server/game/Server/Packets/ChatPackets.cpp @@ -271,3 +271,18 @@ void WorldPackets::Chat::ChatReportIgnored::Read() _worldPacket >> IgnoredGUID; _worldPacket >> Reason; } + +WorldPacket const* WorldPackets::Chat::ChatPlayerAmbiguous::Write() +{ + _worldPacket.WriteBits(Name.length(), 9); + _worldPacket.WriteString(Name); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Chat::ChatRestricted::Write() +{ + _worldPacket << uint8(Reason); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/ChatPackets.h b/src/server/game/Server/Packets/ChatPackets.h index 6f13419666e..faa060b3c0f 100644 --- a/src/server/game/Server/Packets/ChatPackets.h +++ b/src/server/game/Server/Packets/ChatPackets.h @@ -300,6 +300,26 @@ namespace WorldPackets ObjectGuid IgnoredGUID; uint8 Reason = 0; }; + + class ChatPlayerAmbiguous final : public ServerPacket + { + public: + ChatPlayerAmbiguous(std::string const& name) : ServerPacket(SMSG_CHAT_PLAYER_AMBIGUOUS, 2 + name.length()), Name(name) { } + + WorldPacket const* Write() override; + + std::string Name; + }; + + class ChatRestricted final : public ServerPacket + { + public: + ChatRestricted() : ServerPacket(SMSG_CHAT_RESTRICTED, 1) { } + + WorldPacket const* Write() override; + + uint8 Reason = 0; + }; } } diff --git a/src/server/game/Server/Packets/GameObjectPackets.h b/src/server/game/Server/Packets/GameObjectPackets.h index 65eb2c7bed8..5261beede13 100644 --- a/src/server/game/Server/Packets/GameObjectPackets.h +++ b/src/server/game/Server/Packets/GameObjectPackets.h @@ -92,6 +92,22 @@ namespace WorldPackets int32 Damage = 0; int32 SpellID = 0; }; + + class FishNotHooked final : public ServerPacket + { + public: + FishNotHooked() : ServerPacket(SMSG_FISH_NOT_HOOKED, 0) { } + + WorldPacket const* Write() override { return &_worldPacket; } + }; + + class FishEscaped final : public ServerPacket + { + public: + FishEscaped() : ServerPacket(SMSG_FISH_ESCAPED, 0) { } + + WorldPacket const* Write() override { return &_worldPacket; } + }; } } #endif // GOPackets_h__ diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp index ab25edcecf0..e627d80fdea 100644 --- a/src/server/game/Server/Packets/ItemPackets.cpp +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -109,6 +109,11 @@ WorldPacket const* WorldPackets::Item::SetItemPurchaseData::Write() return &_worldPacket; } +void WorldPackets::Item::ItemPurchaseRefund::Read() +{ + _worldPacket >> ItemGUID; +} + WorldPacket const* WorldPackets::Item::ItemPurchaseRefundResult::Write() { _worldPacket << ItemGUID; diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h index ecf264b1fde..833d1bd9261 100644 --- a/src/server/game/Server/Packets/ItemPackets.h +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -147,6 +147,16 @@ namespace WorldPackets ObjectGuid ItemGUID; }; + class ItemPurchaseRefund final : public ClientPacket + { + public: + ItemPurchaseRefund(WorldPacket&& packet) : ClientPacket(CMSG_ITEM_PURCHASE_REFUND, std::move(packet)) { } + + void Read() override; + + ObjectGuid ItemGUID; + }; + class ItemPurchaseRefundResult final : public ServerPacket { public: diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index 4b5fbf40a2d..74a098df47f 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -612,3 +612,8 @@ WorldPacket const* WorldPackets::Misc::CrossedInebriationThreshold::Write() return &_worldPacket; } + +void WorldPackets::Misc::SetTaxiBenchmarkMode::Read() +{ + Enable = _worldPacket.ReadBit(); +} diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index bb090525b9d..7125fd629d7 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -801,6 +801,16 @@ namespace WorldPackets int32 ItemID = 0; int32 Threshold = 0; }; + + class SetTaxiBenchmarkMode final : public ClientPacket + { + public: + SetTaxiBenchmarkMode(WorldPacket&& packet) : ClientPacket(CMSG_SET_TAXI_BENCHMARK_MODE, std::move(packet)) { } + + void Read() override; + + bool Enable = false; + }; } } diff --git a/src/server/game/Server/Packets/NPCPackets.cpp b/src/server/game/Server/Packets/NPCPackets.cpp index 710a6df42d2..9e8090b03b2 100644 --- a/src/server/game/Server/Packets/NPCPackets.cpp +++ b/src/server/game/Server/Packets/NPCPackets.cpp @@ -191,3 +191,8 @@ WorldPacket const* WorldPackets::NPC::TrainerBuyFailed::Write() return &_worldPacket; } + +void WorldPackets::NPC::RequestStabledPets::Read() +{ + _worldPacket >> StableMaster; +} diff --git a/src/server/game/Server/Packets/NPCPackets.h b/src/server/game/Server/Packets/NPCPackets.h index d20f78f3bf2..28444e87e75 100644 --- a/src/server/game/Server/Packets/NPCPackets.h +++ b/src/server/game/Server/Packets/NPCPackets.h @@ -240,6 +240,16 @@ namespace WorldPackets int32 SpellID = 0; int32 TrainerFailedReason = 0; }; + + class RequestStabledPets final : public ClientPacket + { + public: + RequestStabledPets(WorldPacket&& packet) : ClientPacket(CMSG_REQUEST_STABLED_PETS, std::move(packet)) { } + + void Read() override; + + ObjectGuid StableMaster; + }; } } diff --git a/src/server/game/Server/Packets/PartyPackets.cpp b/src/server/game/Server/Packets/PartyPackets.cpp index e93c0030cd5..2d5a85d4fd0 100644 --- a/src/server/game/Server/Packets/PartyPackets.cpp +++ b/src/server/game/Server/Packets/PartyPackets.cpp @@ -163,6 +163,15 @@ void WorldPackets::Party::SetPartyLeader::Read() _worldPacket >> TargetGUID; } +void WorldPackets::Party::SetPartyAssignment::Read() +{ + _worldPacket >> PartyIndex; + _worldPacket >> Assignment; + _worldPacket >> Target; + Set = _worldPacket.ReadBit(); +} + + void WorldPackets::Party::SetRole::Read() { _worldPacket >> PartyIndex; @@ -503,7 +512,7 @@ void WorldPackets::Party::PartyMemberStats::Initialize(Player const* player) // Pet if (player->GetPet()) { - Pet* pet = player->GetPet(); + ::Pet* pet = player->GetPet(); MemberStats.PetStats = boost::in_place(); diff --git a/src/server/game/Server/Packets/PartyPackets.h b/src/server/game/Server/Packets/PartyPackets.h index 85742d09f7e..1c31ba25879 100644 --- a/src/server/game/Server/Packets/PartyPackets.h +++ b/src/server/game/Server/Packets/PartyPackets.h @@ -362,6 +362,18 @@ namespace WorldPackets bool Apply = false; }; + class SetPartyAssignment final : public ClientPacket + { + public: + SetPartyAssignment(WorldPacket&& packet) : ClientPacket(CMSG_SET_PARTY_ASSIGNMENT, std::move(packet)) { } + + void Read() override; + uint8 Assignment = 0; + uint8 PartyIndex = 0; + ObjectGuid Target; + bool Set = false; + }; + class DoReadyCheck final : public ClientPacket { public: diff --git a/src/server/game/Server/Packets/PetPackets.cpp b/src/server/game/Server/Packets/PetPackets.cpp new file mode 100644 index 00000000000..803b956b4ee --- /dev/null +++ b/src/server/game/Server/Packets/PetPackets.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "PetPackets.h" + +void WorldPackets::Pet::DismissCritter::Read() +{ + _worldPacket >> CritterGUID; +} + +void WorldPackets::Pet::PetAbandon::Read() +{ + _worldPacket >> Pet; +} + +void WorldPackets::Pet::PetStopAttack::Read() +{ + _worldPacket >> PetGUID; +} + +void WorldPackets::Pet::PetSpellAutocast::Read() +{ + _worldPacket >> PetGUID; + _worldPacket >> SpellID; + AutocastEnabled = _worldPacket.ReadBit(); +} diff --git a/src/server/game/Server/Packets/PetPackets.h b/src/server/game/Server/Packets/PetPackets.h new file mode 100644 index 00000000000..04af0d2bdd3 --- /dev/null +++ b/src/server/game/Server/Packets/PetPackets.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PetPackets_h__ +#define PetPackets_h__ + +#include "Packet.h" +#include "ObjectGuid.h" + +namespace WorldPackets +{ + namespace Pet + { + class DismissCritter final : public ClientPacket + { + public: + DismissCritter(WorldPacket&& packet) : ClientPacket(CMSG_DISMISS_CRITTER, std::move(packet)) { } + + void Read() override; + + ObjectGuid CritterGUID; + }; + + class RequestPetInfo final : public ClientPacket + { + public: + RequestPetInfo(WorldPacket&& packet) : ClientPacket(CMSG_REQUEST_PET_INFO, std::move(packet)) { } + + void Read() override { } + }; + + class PetAbandon final : public ClientPacket + { + public: + PetAbandon(WorldPacket&& packet) : ClientPacket(CMSG_PET_ABANDON, std::move(packet)) { } + + void Read() override; + + ObjectGuid Pet; + }; + + class PetStopAttack final : public ClientPacket + { + public: + PetStopAttack(WorldPacket&& packet) : ClientPacket(CMSG_PET_STOP_ATTACK, std::move(packet)) { } + + void Read() override; + + ObjectGuid PetGUID; + }; + + class PetSpellAutocast final : public ClientPacket + { + public: + PetSpellAutocast(WorldPacket&& packet) : ClientPacket(CMSG_PET_SPELL_AUTOCAST, std::move(packet)) { } + + void Read() override; + + ObjectGuid PetGUID; + uint32 SpellID = 0; + bool AutocastEnabled = false; + }; + } +} + +#endif // PetPackets_h__ diff --git a/src/server/game/Server/Packets/QuestPackets.cpp b/src/server/game/Server/Packets/QuestPackets.cpp index 371da76570c..f7604e864d3 100644 --- a/src/server/game/Server/Packets/QuestPackets.cpp +++ b/src/server/game/Server/Packets/QuestPackets.cpp @@ -538,3 +538,8 @@ WorldPacket const* WorldPackets::Quest::QuestGiverQuestFailed::Write() return &_worldPacket; } + +void WorldPackets::Quest::PushQuestToParty::Read() +{ + _worldPacket >> QuestID; +} diff --git a/src/server/game/Server/Packets/QuestPackets.h b/src/server/game/Server/Packets/QuestPackets.h index b509bcc94af..ff5835047b5 100644 --- a/src/server/game/Server/Packets/QuestPackets.h +++ b/src/server/game/Server/Packets/QuestPackets.h @@ -548,6 +548,16 @@ namespace WorldPackets uint32 QuestID = 0; uint32 Reason = 0; }; + + class PushQuestToParty final : public ClientPacket + { + public: + PushQuestToParty(WorldPacket&& packet) : ClientPacket(CMSG_PUSH_QUEST_TO_PARTY, std::move(packet)) { } + + void Read() override; + + uint32 QuestID = 0; + }; } } diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index 0d0be9eae94..3eea414f024 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -24,6 +24,12 @@ void WorldPackets::Spells::CancelAura::Read() _worldPacket >> CasterGUID; } +void WorldPackets::Spells::PetCancelAura::Read() +{ + _worldPacket >> PetGUID; + _worldPacket >> SpellID; +} + void WorldPackets::Spells::CancelChannelling::Read() { _worldPacket >> ChannelSpell; @@ -793,3 +799,40 @@ WorldPacket const* WorldPackets::Spells::ResyncRunes::Write() return &_worldPacket; } + +void WorldPackets::Spells::MissileTrajectoryCollision::Read() +{ + _worldPacket >> Target; + _worldPacket >> SpellID; + _worldPacket >> CastID; + _worldPacket >> CollisionPos; +} + +WorldPacket const* WorldPackets::Spells::NotifyMissileTrajectoryCollision::Write() +{ + _worldPacket << Caster; + _worldPacket << uint8(CastID); + _worldPacket << CollisionPos; + + return &_worldPacket; +} + +void WorldPackets::Spells::UpdateMissileTrajectory::Read() +{ + _worldPacket >> Guid; + _worldPacket >> MoveMsgID; + _worldPacket >> SpellID; + _worldPacket >> Pitch; + _worldPacket >> Speed; + _worldPacket >> FirePos; + _worldPacket >> ImpactPos; + bool hasStatus = _worldPacket.ReadBit(); + + _worldPacket.ResetBitPos(); + if (hasStatus) + { + MovementInfo info; + _worldPacket >> info; + Status = info; + } +} diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index 3fc1d6cca56..15a2ac34fc2 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -19,6 +19,7 @@ #define SpellPackets_h__ #include "Packet.h" +#include "PacketUtilities.h" #include "Player.h" #include "SpellAuras.h" #include "Spell.h" @@ -72,6 +73,17 @@ namespace WorldPackets void Read() override { } }; + class PetCancelAura final : public ClientPacket + { + public: + PetCancelAura(WorldPacket&& packet) : ClientPacket(CMSG_PET_CANCEL_AURA, std::move(packet)) { } + + void Read() override; + + ObjectGuid PetGUID; + uint32 SpellID = 0; + }; + class RequestCategoryCooldowns final : public ClientPacket { public: @@ -813,6 +825,48 @@ namespace WorldPackets std::vector<ResyncRune> Runes; }; + + class MissileTrajectoryCollision final : public ClientPacket + { + public: + MissileTrajectoryCollision(WorldPacket&& packet) : ClientPacket(CMSG_MISSILE_TRAJECTORY_COLLISION, std::move(packet)) { } + + void Read() override; + + ObjectGuid Target; + int32 SpellID = 0; + uint8 CastID = 0; + G3D::Vector3 CollisionPos; + }; + + class NotifyMissileTrajectoryCollision : public ServerPacket + { + public: + NotifyMissileTrajectoryCollision() : ServerPacket(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, 8 + 1 + 12) { } + + WorldPacket const* Write() override; + + ObjectGuid Caster; + uint8 CastID = 0; + G3D::Vector3 CollisionPos; + }; + + class UpdateMissileTrajectory final : public ClientPacket + { + public: + UpdateMissileTrajectory(WorldPacket&& packet) : ClientPacket(CMSG_UPDATE_MISSILE_TRAJECTORY, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + uint16 MoveMsgID = 0; + int32 SpellID = 0; + float Pitch = 0.0f; + float Speed = 0.0f; + G3D::Vector3 FirePos; + G3D::Vector3 ImpactPos; + Optional<MovementInfo> Status; + }; } } diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp index 932392e0cb4..7dc2daaa706 100644 --- a/src/server/game/Server/Packets/TalentPackets.cpp +++ b/src/server/game/Server/Packets/TalentPackets.cpp @@ -54,4 +54,18 @@ void WorldPackets::Talent::LearnTalents::Read() _worldPacket >> talent; Talents.push_back(talent); } -}
\ No newline at end of file +} + +WorldPacket const* WorldPackets::Talent::RespecWipeConfirm::Write() +{ + _worldPacket << int8(RespecType); + _worldPacket << uint32(Cost); + _worldPacket << RespecMaster; + return &_worldPacket; +} + +void WorldPackets::Talent::ConfirmRespecWipe::Read() +{ + _worldPacket >> RespecMaster; + _worldPacket >> RespecType; +} diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h index 47519ba4e3b..ca6b0217a28 100644 --- a/src/server/game/Server/Packets/TalentPackets.h +++ b/src/server/game/Server/Packets/TalentPackets.h @@ -68,8 +68,31 @@ namespace WorldPackets void Read() override; std::vector<uint16> Talents; + }; + + class RespecWipeConfirm final : public ServerPacket + { + public: + RespecWipeConfirm() : ServerPacket(SMSG_RESPEC_WIPE_CONFIRM, 16 + 4 +1) { } + + WorldPacket const* Write() override; + + ObjectGuid RespecMaster; + uint32 Cost = 0; + int8 RespecType = 0; + }; + + class ConfirmRespecWipe final : public ClientPacket + { + public: + ConfirmRespecWipe(WorldPacket&& packet) : ClientPacket(CMSG_CONFIRM_RESPEC_WIPE, std::move(packet)) { } + void Read() override; + + ObjectGuid RespecMaster; + uint8 RespecType = 0; }; + } } diff --git a/src/server/game/Server/Packets/TicketPackets.cpp b/src/server/game/Server/Packets/TicketPackets.cpp index 97c23b47191..1e9eb14e3ea 100644 --- a/src/server/game/Server/Packets/TicketPackets.cpp +++ b/src/server/game/Server/Packets/TicketPackets.cpp @@ -18,6 +18,7 @@ #include "LFGPackets.h" #include "TicketPackets.h" #include "PacketUtilities.h" +#include "SupportMgr.h" using namespace WorldPackets; @@ -223,6 +224,46 @@ void WorldPackets::Ticket::SupportTicketSubmitComplaint::Read() _worldPacket >> LFGListApplicant; } +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::Complaint::ComplaintOffender& complaintOffender) +{ + data >> complaintOffender.PlayerGuid; + data >> complaintOffender.RealmAddress; + data >> complaintOffender.TimeSinceOffence; + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::Complaint::ComplaintChat& chat) +{ + data >> chat.Command; + data >> chat.ChannelID; + chat.MessageLog = data.ReadString(data.ReadBits(12)); + + return data; +} + +void WorldPackets::Ticket::Complaint::Read() +{ + _worldPacket >> ComplaintType; + _worldPacket >> Offender; + + switch (ComplaintType) + { + case SUPPORT_SPAM_TYPE_MAIL: + _worldPacket >> MailID; + break; + case SUPPORT_SPAM_TYPE_CHAT: + _worldPacket >> Chat; + break; + case SUPPORT_SPAM_TYPE_CALENDAR: + _worldPacket >> EventGuid; + _worldPacket >> InviteGuid; + break; + default: + break; + } +} + WorldPacket const* WorldPackets::Ticket::ComplaintResult::Write() { _worldPacket << uint32(ComplaintType); diff --git a/src/server/game/Server/Packets/TicketPackets.h b/src/server/game/Server/Packets/TicketPackets.h index 9b126d681b0..691a7b27bb8 100644 --- a/src/server/game/Server/Packets/TicketPackets.h +++ b/src/server/game/Server/Packets/TicketPackets.h @@ -193,6 +193,36 @@ namespace WorldPackets }; + class Complaint final : public ClientPacket + { + public: + struct ComplaintOffender + { + ObjectGuid PlayerGuid; + uint32 RealmAddress = 0; + uint32 TimeSinceOffence = 0; + }; + + struct ComplaintChat + { + uint32 Command = 0; + uint32 ChannelID = 0; + std::string MessageLog; + }; + + Complaint(WorldPacket&& packet) : ClientPacket(CMSG_COMPLAINT, std::move(packet)) { } + + void Read() override; + + uint8 ComplaintType = 0; + ComplaintOffender Offender; + uint32 MailID = 0; + ComplaintChat Chat; + ObjectGuid EventGuid; + ObjectGuid InviteGuid; + + }; + class ComplaintResult final : public ServerPacket { public: diff --git a/src/server/game/Server/Packets/VoicePackets.cpp b/src/server/game/Server/Packets/VoicePackets.cpp new file mode 100644 index 00000000000..3e2269a0905 --- /dev/null +++ b/src/server/game/Server/Packets/VoicePackets.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "VoicePackets.h" + +void WorldPackets::Voice::VoiceSessionEnable::Read() +{ + EnableVoiceChat = _worldPacket.ReadBit(); + EnableMicrophone = _worldPacket.ReadBit(); +} + +void WorldPackets::Voice::SetActiveVoiceChannel::Read() +{ + _worldPacket >> ChannelType; + ChannelName = _worldPacket.ReadString(_worldPacket.ReadBits(7)); +} diff --git a/src/server/game/Server/Packets/VoicePackets.h b/src/server/game/Server/Packets/VoicePackets.h new file mode 100644 index 00000000000..0ae2645c498 --- /dev/null +++ b/src/server/game/Server/Packets/VoicePackets.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef VoicePackets_h__ +#define VoicePackets_h__ + +#include "Packet.h" + +namespace WorldPackets +{ + namespace Voice + { + class VoiceSessionEnable final : public ClientPacket + { + public: + VoiceSessionEnable(WorldPacket&& packet) : ClientPacket(CMSG_VOICE_SESSION_ENABLE, std::move(packet)) { } + + void Read() override; + + bool EnableVoiceChat = false; + bool EnableMicrophone = false; + }; + + class SetActiveVoiceChannel final : public ClientPacket + { + public: + SetActiveVoiceChannel(WorldPacket&& packet) : ClientPacket(CMSG_SET_ACTIVE_VOICE_CHANNEL, std::move(packet)) { } + + void Read() override; + + uint8 ChannelType = 0; + std::string ChannelName; + }; + } +} + +#endif // VoicePackets_h__ diff --git a/src/server/game/Server/Packets/WardenPackets.cpp b/src/server/game/Server/Packets/WardenPackets.cpp new file mode 100644 index 00000000000..89ff90dc175 --- /dev/null +++ b/src/server/game/Server/Packets/WardenPackets.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "WardenPackets.h" + +void WorldPackets::Warden::WardenData::Read() +{ + uint32 size = _worldPacket.read<uint32>(); + + if (size) + { + Data.resize(size); + _worldPacket.read(Data.contents(), size); + } +} diff --git a/src/server/game/Server/Packets/WardenPackets.h b/src/server/game/Server/Packets/WardenPackets.h new file mode 100644 index 00000000000..a23046235ff --- /dev/null +++ b/src/server/game/Server/Packets/WardenPackets.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef WardenPackets_h__ +#define WardenPackets_h__ + +#include "Packet.h" + +namespace WorldPackets +{ + namespace Warden + { + class WardenData final : public ClientPacket + { + public: + WardenData(WorldPacket&& packet) : ClientPacket(CMSG_WARDEN_DATA, std::move(packet)) { } + + void Read() override; + + ByteBuffer Data; + }; + } +} + +#endif // WardenPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 337afae1c05..b7693d89b4b 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -291,10 +291,10 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_COMMENTATOR_GET_MAP_INFO, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_COMMENTATOR_GET_PLAYER_INFO, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_COMMENTATOR_START_WARGAME, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_COMPLAINT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleComplainOpcode ); + DEFINE_HANDLER(CMSG_COMPLAINT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::Complaint, &WorldSession::HandleComplaint); DEFINE_HANDLER(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::CompleteCinematic, &WorldSession::HandleCompleteCinematic); DEFINE_HANDLER(CMSG_COMPLETE_MOVIE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_CONFIRM_RESPEC_WIPE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleConfirmRespecWipeOpcode ); + DEFINE_HANDLER(CMSG_CONFIRM_RESPEC_WIPE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Talent::ConfirmRespecWipe, &WorldSession::HandleConfirmRespecWipeOpcode); DEFINE_HANDLER(CMSG_CONNECT_TO_FAILED, STATUS_NEVER, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess); DEFINE_HANDLER(CMSG_CONVERT_RAID, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Party::ConvertRaid, &WorldSession::HandleConvertRaidOpcode); DEFINE_HANDLER(CMSG_CREATE_CHARACTER, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::CreateCharacter, &WorldSession::HandleCharCreateOpcode); @@ -320,7 +320,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_DF_SET_ROLES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgSetRolesOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_DF_TELEPORT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgTeleportOpcode ); DEFINE_HANDLER(CMSG_DISCARDED_TIME_SYNC_ACKS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_DISMISS_CRITTER, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleDismissCritter ); + DEFINE_HANDLER(CMSG_DISMISS_CRITTER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Pet::DismissCritter, &WorldSession::HandleDismissCritter); DEFINE_HANDLER(CMSG_DO_MASTER_LOOT_ROLL, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_DO_READY_CHECK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::DoReadyCheck, &WorldSession::HandleDoReadyCheckOpcode); DEFINE_HANDLER(CMSG_DUEL_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Duel::DuelResponse, &WorldSession::HandleDuelResponseOpcode); @@ -422,7 +422,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_INSPECT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Inspect::Inspect, &WorldSession::HandleInspectOpcode); DEFINE_HANDLER(CMSG_INSPECT_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Inspect::InspectPVPRequest, &WorldSession::HandleInspectPVP); DEFINE_HANDLER(CMSG_INSTANCE_LOCK_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Instance::InstanceLockResponse, &WorldSession::HandleInstanceLockResponse); - DEFINE_OPCODE_HANDLER_OLD(CMSG_ITEM_PURCHASE_REFUND, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleItemRefund ); + DEFINE_HANDLER(CMSG_ITEM_PURCHASE_REFUND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::ItemPurchaseRefund, &WorldSession::HandleItemRefund); DEFINE_HANDLER(CMSG_ITEM_TEXT_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Query::ItemTextQuery, &WorldSession::HandleItemTextQuery); DEFINE_HANDLER(CMSG_JOIN_PET_BATTLE_QUEUE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_JOIN_RATED_BATTLEGROUND, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -474,7 +474,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_MAIL_TAKE_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Mail::MailTakeMoney, &WorldSession::HandleMailTakeMoney); DEFINE_OPCODE_HANDLER_OLD(CMSG_MASTER_LOOT_ITEM, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMasterGiveOpcode ); DEFINE_HANDLER(CMSG_MINIMAP_PING, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::MinimapPingClient, &WorldSession::HandleMinimapPingOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_MISSILE_TRAJECTORY_COLLISION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUpdateProjectilePosition ); + DEFINE_HANDLER(CMSG_MISSILE_TRAJECTORY_COLLISION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::MissileTrajectoryCollision, &WorldSession::HandleMissileTrajectoryCollision); DEFINE_HANDLER(CMSG_MOUNT_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_MOUNT_SPECIAL_ANIM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::MountSpecial, &WorldSession::HandleMountSpecialAnimOpcode); DEFINE_HANDLER(CMSG_MOVE_APPLY_MOVEMENT_FORCE_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -557,7 +557,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_PETITION_RENAME_GUILD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Petition::PetitionRenameGuild, &WorldSession::HandlePetitionRenameGuild); DEFINE_HANDLER(CMSG_PETITION_SHOW_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Petition::PetitionShowList, &WorldSession::HandlePetitionShowList); DEFINE_HANDLER(CMSG_PETITION_SHOW_SIGNATURES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Petition::PetitionShowSignatures, &WorldSession::HandlePetitionShowSignatures); - DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_ABANDON, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAbandon ); + DEFINE_HANDLER(CMSG_PET_ABANDON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Pet::PetAbandon ,&WorldSession::HandlePetAbandon); DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_ACTION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAction ); DEFINE_HANDLER(CMSG_PET_BATTLE_FINAL_NOTIFY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_PET_BATTLE_INPUT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -568,16 +568,16 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_PET_BATTLE_REQUEST_UPDATE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_PET_BATTLE_REQUEST_WILD, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_PET_BATTLE_SCRIPT_ERROR_NOTIFY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_CANCEL_AURA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetCancelAuraOpcode ); + DEFINE_HANDLER(CMSG_PET_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::PetCancelAura, &WorldSession::HandlePetCancelAuraOpcode); DEFINE_HANDLER(CMSG_PET_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::PetCastSpell, &WorldSession::HandlePetCastSpellOpcode); DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_RENAME, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetRename ); DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_SET_ACTION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetSetAction ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_SPELL_AUTOCAST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetSpellAutocastOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_STOP_ATTACK, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetStopAttack ); + DEFINE_HANDLER(CMSG_PET_SPELL_AUTOCAST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Pet::PetSpellAutocast, &WorldSession::HandlePetSpellAutocastOpcode); + DEFINE_HANDLER(CMSG_PET_STOP_ATTACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Pet::PetStopAttack, &WorldSession::HandlePetStopAttack); DEFINE_HANDLER(CMSG_PING, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPacket, &WorldSession::Handle_EarlyProccess); DEFINE_HANDLER(CMSG_PLAYER_LOGIN, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::PlayerLogin, &WorldSession::HandlePlayerLoginOpcode); DEFINE_HANDLER(CMSG_PROTOCOL_MISMATCH, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_PUSH_QUEST_TO_PARTY, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePushQuestToParty ); + DEFINE_HANDLER(CMSG_PUSH_QUEST_TO_PARTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Quest::PushQuestToParty, &WorldSession::HandlePushQuestToParty); DEFINE_HANDLER(CMSG_PVP_LOG_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battleground::PVPLogDataRequest, &WorldSession::HandlePVPLogDataOpcode); DEFINE_HANDLER(CMSG_QUERY_BATTLE_PET_NAME, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_QUERY_CORPSE_LOCATION_FROM_CLIENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Query::QueryCorpseLocationFromClient, &WorldSession::HandleQueryCorpseLocation); @@ -637,13 +637,13 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_REQUEST_LFG_LIST_BLACKLIST, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REQUEST_PARTY_JOIN_UPDATES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::RequestPartyJoinUpdates, &WorldSession::HandleRequestPartyJoinUpdates); DEFINE_HANDLER(CMSG_REQUEST_PARTY_MEMBER_STATS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::RequestPartyMemberStats, &WorldSession::HandleRequestPartyMemberStatsOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_REQUEST_PET_INFO, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestPetInfoOpcode ); + DEFINE_HANDLER(CMSG_REQUEST_PET_INFO, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Pet::RequestPetInfo, &WorldSession::HandleRequestPetInfo); DEFINE_HANDLER(CMSG_REQUEST_PLAYED_TIME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::RequestPlayedTime, &WorldSession::HandlePlayedTime); - DEFINE_OPCODE_HANDLER_OLD(CMSG_REQUEST_PVP_REWARDS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleRequestPvpReward ); + DEFINE_HANDLER(CMSG_REQUEST_PVP_REWARDS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battleground::RequestPVPRewards, &WorldSession::HandleRequestPvpReward); DEFINE_HANDLER(CMSG_REQUEST_RAID_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::RequestRaidInfo, &WorldSession::HandleRequestRaidInfoOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_REQUEST_RATED_BATTLEFIELD_INFO, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestRatedBattlefieldInfo); + DEFINE_HANDLER(CMSG_REQUEST_RATED_BATTLEFIELD_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battleground::RequestRatedBattlefieldInfo, &WorldSession::HandleRequestRatedBattlefieldInfo); DEFINE_HANDLER(CMSG_REQUEST_RESEARCH_HISTORY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_REQUEST_STABLED_PETS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleListStabledPetsOpcode ); + DEFINE_HANDLER(CMSG_REQUEST_STABLED_PETS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::RequestStabledPets, &WorldSession::HandleRequestStabledPets); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_EXIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Vehicle::RequestVehicleExit, &WorldSession::HandleRequestVehicleExit); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Vehicle::RequestVehicleNextSeat, &WorldSession::HandleRequestVehicleNextSeat); DEFINE_HANDLER(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Vehicle::RequestVehiclePrevSeat, &WorldSession::HandleRequestVehiclePrevSeat); @@ -673,7 +673,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_ACTION_BAR_TOGGLES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetActionBarToggles, &WorldSession::HandleSetActionBarToggles); DEFINE_HANDLER(CMSG_SET_ACTION_BUTTON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::SetActionButton, &WorldSession::HandleSetActionButtonOpcode); DEFINE_HANDLER(CMSG_SET_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Movement::SetActiveMover, &WorldSession::HandleSetActiveMoverOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_ACTIVE_VOICE_CHANNEL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActiveVoiceChannel ); + DEFINE_HANDLER(CMSG_SET_ACTIVE_VOICE_CHANNEL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Voice::SetActiveVoiceChannel, &WorldSession::HandleSetActiveVoiceChannel); DEFINE_HANDLER(CMSG_SET_ADVANCED_COMBAT_LOGGING, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::ClientConfig::SetAdvancedCombatLogging, &WorldSession::HandleSetAdvancedCombatLogging); DEFINE_HANDLER(CMSG_SET_ASSISTANT_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::SetAssistantLeader, &WorldSession::HandleSetAssistantLeaderOpcode); DEFINE_HANDLER(CMSG_SET_BACKPACK_AUTOSORT_DISABLED, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -691,10 +691,10 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_LFG_BONUS_FACTION_ID, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SET_LOOT_METHOD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::SetLootMethod, &WorldSession::HandleSetLootMethodOpcode); DEFINE_HANDLER(CMSG_SET_LOOT_SPECIALIZATION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Loot::SetLootSpecialization, &WorldSession::HandleSetLootSpecialization); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_PARTY_ASSIGNMENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePartyAssignmentOpcode ); + DEFINE_HANDLER(CMSG_SET_PARTY_ASSIGNMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::SetPartyAssignment, &WorldSession::HandleSetPartyAssignment); DEFINE_HANDLER(CMSG_SET_PARTY_LEADER, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Party::SetPartyLeader, &WorldSession::HandleSetPartyLeaderOpcode); DEFINE_HANDLER(CMSG_SET_PET_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPlayerDeclinedNames ); + DEFINE_HANDLER(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetPlayerDeclinedNames, &WorldSession::HandleSetPlayerDeclinedNames); DEFINE_HANDLER(CMSG_SET_PREFERRED_CEMETERY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SET_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::SetPvP, &WorldSession::HandleSetPvP); DEFINE_HANDLER(CMSG_SET_RAID_DIFFICULTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::SetRaidDifficulty, &WorldSession::HandleSetRaidDifficultyOpcode); @@ -704,7 +704,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_SHEATHED, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Combat::SetSheathed, &WorldSession::HandleSetSheathedOpcode); DEFINE_HANDLER(CMSG_SET_SORT_BAGS_RIGHT_TO_LEFT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SET_SPECIALIZATION, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Talent::SetSpecialization, &WorldSession::HandleSetSpecializationOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SET_TAXI_BENCHMARK_MODE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTaxiBenchmarkOpcode ); + DEFINE_HANDLER(CMSG_SET_TAXI_BENCHMARK_MODE, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Misc::SetTaxiBenchmarkMode, &WorldSession::HandleSetTaxiBenchmark); DEFINE_HANDLER(CMSG_SET_TITLE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetTitle, &WorldSession::HandleSetTitleOpcode); DEFINE_HANDLER(CMSG_SET_TRADE_CURRENCY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Trade::SetTradeCurrency, &WorldSession::HandleSetTradeCurrencyOpcode); DEFINE_HANDLER(CMSG_SET_TRADE_GOLD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Trade::SetTradeGold, &WorldSession::HandleSetTradeGoldOpcode); @@ -767,7 +767,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_UNLOCK_VOID_STORAGE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::VoidStorage::UnlockVoidStorage, &WorldSession::HandleVoidStorageUnlock); DEFINE_HANDLER(CMSG_UPDATE_ACCOUNT_DATA, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::ClientConfig::UserClientUpdateAccountData, &WorldSession::HandleUpdateAccountData); DEFINE_HANDLER(CMSG_UPDATE_CLIENT_SETTINGS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_UPDATE_MISSILE_TRAJECTORY, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUpdateMissileTrajectory ); + DEFINE_HANDLER(CMSG_UPDATE_MISSILE_TRAJECTORY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::UpdateMissileTrajectory, &WorldSession::HandleUpdateMissileTrajectory); DEFINE_HANDLER(CMSG_UPDATE_RAID_TARGET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::UpdateRaidTarget, &WorldSession::HandleUpdateRaidTargetOpcode); DEFINE_HANDLER(CMSG_UPDATE_VAS_PURCHASE_STATES, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_UPDATE_WOW_TOKEN_AUCTIONABLE_LIST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Token::UpdateListedAuctionableTokens, &WorldSession::HandleUpdateListedAuctionableTokens); @@ -782,9 +782,9 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_VIOLENCE_LEVEL, STATUS_AUTHED, PROCESS_INPLACE, WorldPackets::Misc::ViolenceLevel, &WorldSession::HandleViolenceLevel); DEFINE_HANDLER(CMSG_VOICE_ADD_IGNORE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_VOICE_DEL_IGNORE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); - DEFINE_OPCODE_HANDLER_OLD(CMSG_VOICE_SESSION_ENABLE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleVoiceSessionEnableOpcode ); + DEFINE_HANDLER(CMSG_VOICE_SESSION_ENABLE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Voice::VoiceSessionEnable, &WorldSession::HandleVoiceSessionEnable); DEFINE_HANDLER(CMSG_VOID_STORAGE_TRANSFER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::VoidStorage::VoidStorageTransfer, &WorldSession::HandleVoidStorageTransfer); - DEFINE_OPCODE_HANDLER_OLD(CMSG_WARDEN_DATA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleWardenDataOpcode ); + DEFINE_HANDLER(CMSG_WARDEN_DATA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Warden::WardenData, &WorldSession::HandleWardenData); DEFINE_HANDLER(CMSG_WHO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Who::WhoRequestPkt, &WorldSession::HandleWhoOpcode); DEFINE_HANDLER(CMSG_WHO_IS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Who::WhoIsRequest, &WorldSession::HandleWhoIsOpcode); DEFINE_HANDLER(CMSG_WORLD_PORT_RESPONSE, STATUS_TRANSFER, PROCESS_THREADUNSAFE, WorldPackets::Movement::WorldPortResponse, &WorldSession::HandleMoveWorldportAckOpcode); @@ -820,9 +820,9 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_GUILD_ACHIEVEMENTS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARCHAEOLOGY_SURVERY_CAST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_SPIRIT_HEALER_TIME, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_SPIRIT_HEALER_TIME, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_DENIED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_NO_CORPSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_NO_CORPSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_PATH, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_SHAPE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARENA_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -970,10 +970,10 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_IGNORED_ACCOUNT_MUTED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_IS_DOWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_NOT_IN_PARTY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_PLAYER_AMBIGUOUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_PLAYER_AMBIGUOUS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_PLAYER_NOTFOUND, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_RECONNECT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_RESTRICTED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_RESTRICTED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_SERVER_MESSAGE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHEAT_IGNORE_DIMISHING_RETURNS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHECK_WARGAME_ENTRY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -990,9 +990,9 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_MAP_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_STATE_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMPLAINT_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMPLAINT_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMPLETE_SHIPMENT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMPRESSED_PACKET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMPRESSED_PACKET, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CONNECT_TO, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CONQUEST_FORMULA_CONSTANTS, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CONSOLE_WRITE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1003,7 +1003,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_COOLDOWN_EVENT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_LOCATION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_RECLAIM_DELAY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_TRANSPORT_QUERY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_TRANSPORT_QUERY, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CREATE_CHAR, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CREATE_SHIPMENT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CRITERIA_DELETED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); @@ -1054,8 +1054,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEATURE_SYSTEM_STATUS_GLUE_SCREEN, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEIGN_DEATH_RESISTED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FINAL_CHUNK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_ESCAPED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_NOT_HOOKED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_ESCAPED, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_NOT_HOOKED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FLIGHT_SPLINE_SYNC, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FORCED_DEATH_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FORCE_ANIM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1124,7 +1124,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_REQUEST_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_CASE_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOD_MODE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOSSIP_COMPLETE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOSSIP_MESSAGE, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1380,11 +1380,11 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_NEW_TAXI_PATH, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_NEW_WORLD, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFY_DEST_LOC_SPELL_CAST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFY_MONEY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFY_RECEIVED_MAIL, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_OFFER_PETITION_ERROR, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ON_MONSTER_MOVE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_OPEN_CONTAINER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_OPEN_LFG_DUNGEON_FINDER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1446,7 +1446,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_OBJECT_SOUND, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_ONE_SHOT_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_ORPHAN_SPELL_VISUAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SCENE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SCENE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SOUND, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SPEAKERBOT_SOUND, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SPELL_VISUAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1458,7 +1458,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_PRINT_NOTIFICATION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PROC_RESIST, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PROPOSE_LEVEL_GRANT, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_CREDIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_CREDIT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_LOG_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_OPTIONS_ENABLED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_SEASON, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1467,7 +1467,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_GAME_OBJECT_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_GARRISON_CREATURE_NAME_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_GUILD_INFO_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_ITEM_TEXT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_ITEM_TEXT_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_NPC_TEXT_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_PAGE_TEXT_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_PETITION_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1527,7 +1527,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESET_FAILED_NOTIFY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESET_RANGED_COMBAT_TIMER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESET_WEEKLY_CURRENCY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESPEC_WIPE_CONFIRM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESPEC_WIPE_CONFIRM, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_CAST_BAR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_COMMS, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1584,7 +1584,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_MOVEMENT_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); @@ -1614,7 +1614,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_HEAL_LOG, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_INSTAKILL_LOG, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_INTERRUPT_LOG, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_MISS_LOG, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_MISS_LOG, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_MULTISTRIKE_EFFECT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_NON_MELEE_DAMAGE_LOG, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_OR_DAMAGE_IMMUNE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); @@ -1672,7 +1672,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_DUNGEON_ENCOUNTER_FOR_LOOT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_EXPANSION_LEVEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_INSTANCE_OWNERSHIP, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_LAST_INSTANCE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_LAST_INSTANCE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_OBJECT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_TALENT_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_TASK_PROGRESS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index dc4f3b32417..857988313d0 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -125,6 +125,8 @@ namespace WorldPackets class GetPVPOptionsEnabled; class RequestBattlefieldStatus; class ReportPvPPlayerAFK; + class RequestPVPRewards; + class RequestRatedBattlefieldInfo; } namespace BattlePet @@ -195,6 +197,7 @@ namespace WorldPackets class SetFactionNotAtWar; class SetFactionInactive; class SetWatchedFaction; + class SetPlayerDeclinedNames; enum class LoginFailureReason : uint8; } @@ -346,6 +349,7 @@ namespace WorldPackets class BuyBackItem; class DestroyItem; class GetItemPurchaseData; + class ItemPurchaseRefund; class RepairItem; class ReadItem; class SellItem; @@ -411,6 +415,7 @@ namespace WorldPackets class SetPvP; class WorldTeleport; class MountSpecial; + class SetTaxiBenchmarkMode; } namespace Movement @@ -433,6 +438,7 @@ namespace WorldPackets class GossipSelectOption; class SpiritHealerActivate; class TrainerBuySpell; + class RequestStabledPets; } namespace Party @@ -446,6 +452,7 @@ namespace WorldPackets class RequestPartyMemberStats; class PartyMemberStats; class SetPartyLeader; + class SetPartyAssignment; class SetRole; class RoleChangedInform; class SetLootMethod; @@ -476,6 +483,15 @@ namespace WorldPackets class ClearRaidMarker; } + namespace Pet + { + class DismissCritter; + class RequestPetInfo; + class PetAbandon; + class PetStopAttack; + class PetSpellAutocast; + } + namespace Petition { class DeclinePetition; @@ -520,6 +536,7 @@ namespace WorldPackets class QuestGiverAcceptQuest; class QuestLogRemoveQuest; class QuestPushResult; + class PushQuestToParty; } namespace RaF @@ -565,6 +582,7 @@ namespace WorldPackets class CancelChannelling; class CancelGrowthAura; class CancelMountAura; + class PetCancelAura; class RequestCategoryCooldowns; class CancelCast; class CastSpell; @@ -576,12 +594,15 @@ namespace WorldPackets class SelfRes; class GetMirrorImageData; class SpellClick; + class MissileTrajectoryCollision; + class UpdateMissileTrajectory; } namespace Talent { class SetSpecialization; class LearnTalents; + class ConfirmRespecWipe; } namespace Taxi @@ -602,6 +623,7 @@ namespace WorldPackets class SupportTicketSubmitSuggestion; class SupportTicketSubmitComplaint; class BugReport; + class Complaint; } namespace Token @@ -644,6 +666,12 @@ namespace WorldPackets class MoveSetVehicleRecIdAck; } + namespace Voice + { + class VoiceSessionEnable; + class SetActiveVoiceChannel; + } + namespace VoidStorage { class UnlockVoidStorage; @@ -652,6 +680,11 @@ namespace WorldPackets class SwapVoidItem; } + namespace Warden + { + class WardenData; + } + namespace Who { class WhoIsRequest; @@ -754,10 +787,11 @@ enum BFLeaveReason enum ChatRestrictionType { - ERR_CHAT_RESTRICTED = 0, - ERR_CHAT_THROTTLED = 1, - ERR_USER_SQUELCHED = 2, - ERR_YELL_RESTRICTED = 3 + ERR_CHAT_RESTRICTED = 0, + ERR_CHAT_THROTTLED = 1, + ERR_USER_SQUELCHED = 2, + ERR_YELL_RESTRICTED = 3, + ERR_CHAT_RAID_RESTRICTED = 4 }; enum CharterTypes @@ -1054,7 +1088,7 @@ class WorldSession void HandlePlayerLogin(LoginQueryHolder * holder); void HandleCharRenameOpcode(WorldPackets::Character::CharacterRenameRequest& request); void HandleCharRenameCallBack(PreparedQueryResult result, WorldPackets::Character::CharacterRenameInfo* renameInfo); - void HandleSetPlayerDeclinedNames(WorldPacket& recvData); + void HandleSetPlayerDeclinedNames(WorldPackets::Character::SetPlayerDeclinedNames& packet); void HandleAlterAppearance(WorldPackets::Character::AlterApperance& packet); void HandleCharCustomizeOpcode(WorldPackets::Character::CharCustomize& packet); void HandleCharCustomizeCallback(PreparedQueryResult result, WorldPackets::Character::CharCustomizeInfo* customizeInfo); @@ -1130,6 +1164,7 @@ class WorldSession void HandleSupportTicketSubmitSuggestion(WorldPackets::Ticket::SupportTicketSubmitSuggestion& packet); void HandleSupportTicketSubmitComplaint(WorldPackets::Ticket::SupportTicketSubmitComplaint& packet); void HandleBugReportOpcode(WorldPackets::Ticket::BugReport& bugReport); + void HandleComplaint(WorldPackets::Ticket::Complaint& packet); void HandleTogglePvP(WorldPackets::Misc::TogglePvP& packet); void HandleSetPvP(WorldPackets::Misc::SetPvP& packet); @@ -1211,7 +1246,7 @@ class WorldSession void HandleChangeSubGroupOpcode(WorldPackets::Party::ChangeSubGroup& packet); void HandleSwapSubGroupsOpcode(WorldPackets::Party::SwapSubGroups& packet); void HandleSetAssistantLeaderOpcode(WorldPackets::Party::SetAssistantLeader& packet); - void HandlePartyAssignmentOpcode(WorldPacket& recvData); + void HandleSetPartyAssignment(WorldPackets::Party::SetPartyAssignment& packet); void HandleInitiateRolePoll(WorldPackets::Party::InitiateRolePoll& packet); void HandleSetEveryoneIsAssistant(WorldPackets::Party::SetEveryoneIsAssistant& packet); void HandleClearRaidMarker(WorldPackets::Party::ClearRaidMarker& packet); @@ -1281,7 +1316,7 @@ class WorldSession void HandleSpiritHealerActivate(WorldPackets::NPC::SpiritHealerActivate& packet); void HandleNpcTextQueryOpcode(WorldPackets::Query::QueryNPCText& packet); void HandleBinderActivateOpcode(WorldPackets::NPC::Hello& packet); - void HandleListStabledPetsOpcode(WorldPacket& recvPacket); + void HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPets& packet); void HandleStablePet(WorldPacket& recvPacket); void HandleStablePetCallback(PreparedQueryResult result); void HandleUnstablePet(WorldPacket& recvPacket); @@ -1367,9 +1402,11 @@ class WorldSession void HandleCancelGrowthAuraOpcode(WorldPackets::Spells::CancelGrowthAura& cancelGrowthAura); void HandleCancelMountAuraOpcode(WorldPackets::Spells::CancelMountAura& cancelMountAura); void HandleCancelAutoRepeatSpellOpcode(WorldPackets::Spells::CancelAutoRepeatSpell& cancelAutoRepeatSpell); + void HandleMissileTrajectoryCollision(WorldPackets::Spells::MissileTrajectoryCollision& packet); + void HandleUpdateMissileTrajectory(WorldPackets::Spells::UpdateMissileTrajectory& packet); void HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents& packet); - void HandleConfirmRespecWipeOpcode(WorldPacket& recvPacket); + void HandleConfirmRespecWipeOpcode(WorldPackets::Talent::ConfirmRespecWipe& confirmRespecWipe); void HandleUnlearnSkillOpcode(WorldPackets::Spells::UnlearnSkill& packet); void HandleSetSpecializationOpcode(WorldPackets::Talent::SetSpecialization& packet); @@ -1386,7 +1423,7 @@ class WorldSession void HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAccept& packet); void HandleQuestgiverCompleteQuest(WorldPackets::Quest::QuestGiverCompleteQuest& packet); void HandleQuestgiverQuestAutoLaunch(WorldPacket& recvPacket); - void HandlePushQuestToParty(WorldPacket& recvPacket); + void HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet); void HandleQuestPushResult(WorldPackets::Quest::QuestPushResult& packet); void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage); @@ -1402,7 +1439,7 @@ class WorldSession void HandleChatMessageEmoteOpcode(WorldPackets::Chat::ChatMessageEmote& chatMessageEmote); void SendChatPlayerNotfoundNotice(std::string const& name); void SendPlayerAmbiguousNotice(std::string const& name); - void SendChatRestrictedNotice(ChatRestrictionType restriction); + void SendChatRestricted(ChatRestrictionType restriction); void HandleTextEmoteOpcode(WorldPackets::Chat::CTextEmote& packet); void HandleChatIgnoredOpcode(WorldPackets::Chat::ChatReportIgnored& chatReportIgnored); @@ -1424,8 +1461,8 @@ class WorldSession template<void(Channel::*CommandFunction)(Player const*, std::string const&)> void HandleChannelPlayerCommand(WorldPackets::Channel::ChannelPlayerCommand& packet); - void HandleVoiceSessionEnableOpcode(WorldPacket& recvData); - void HandleSetActiveVoiceChannel(WorldPacket& recvData); + void HandleVoiceSessionEnable(WorldPackets::Voice::VoiceSessionEnable& packet); + void HandleSetActiveVoiceChannel(WorldPackets::Voice::SetActiveVoiceChannel& packet); void HandleCompleteCinematic(WorldPackets::Misc::CompleteCinematic& packet); void HandleNextCinematicCamera(WorldPackets::Misc::NextCinematicCamera& packet); @@ -1436,20 +1473,20 @@ class WorldSession //Pet void HandlePetAction(WorldPacket& recvData); - void HandlePetStopAttack(WorldPacket& recvData); + void HandlePetStopAttack(WorldPackets::Pet::PetStopAttack& packet); void HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z); void HandleQueryPetName(WorldPackets::Query::QueryPetName& packet); void HandlePetSetAction(WorldPacket& recvData); - void HandlePetAbandon(WorldPacket& recvData); + void HandlePetAbandon(WorldPackets::Pet::PetAbandon& packet); void HandlePetRename(WorldPacket& recvData); - void HandlePetCancelAuraOpcode(WorldPacket& recvPacket); - void HandlePetSpellAutocastOpcode(WorldPacket& recvPacket); + void HandlePetCancelAuraOpcode(WorldPackets::Spells::PetCancelAura& packet); + void HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast& packet); void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& petCastSpell); void HandleSetActionBarToggles(WorldPackets::Character::SetActionBarToggles& packet); void HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& totemDestroyed); - void HandleDismissCritter(WorldPacket& recvData); + void HandleDismissCritter(WorldPackets::Pet::DismissCritter& dismissCritter); //Battleground void HandleBattlemasterHelloOpcode(WorldPackets::NPC::Hello& hello); @@ -1460,9 +1497,9 @@ class WorldSession void HandleBattlefieldLeaveOpcode(WorldPackets::Battleground::BattlefieldLeave& battlefieldLeave); void HandleBattlemasterJoinArena(WorldPackets::Battleground::BattlemasterJoinArena& packet); void HandleReportPvPAFK(WorldPackets::Battleground::ReportPvPPlayerAFK& reportPvPPlayerAFK); - void HandleRequestRatedBattlefieldInfo(WorldPacket& recvData); + void HandleRequestRatedBattlefieldInfo(WorldPackets::Battleground::RequestRatedBattlefieldInfo& packet); void HandleGetPVPOptionsEnabled(WorldPackets::Battleground::GetPVPOptionsEnabled& getPvPOptionsEnabled); - void HandleRequestPvpReward(WorldPacket& recvData); + void HandleRequestPvpReward(WorldPackets::Battleground::RequestPVPRewards& packet); void HandleAreaSpiritHealerQueryOpcode(WorldPackets::Battleground::AreaSpiritHealerQuery& areaSpiritHealerQuery); void HandleAreaSpiritHealerQueueOpcode(WorldPackets::Battleground::AreaSpiritHealerQueue& areaSpiritHealerQueue); void HandleHearthAndResurrect(WorldPackets::Battleground::HearthAndResurrect& hearthAndResurrect); @@ -1478,7 +1515,7 @@ class WorldSession void HandleBfQueueInviteResponse(WorldPackets::Battlefield::BFMgrQueueInviteResponse& bfMgrQueueInviteResponse); void HandleBfQueueExitRequest(WorldPackets::Battlefield::BFMgrQueueExitRequest& bfMgrQueueExitRequest); - void HandleWardenDataOpcode(WorldPacket& recvData); + void HandleWorldTeleportOpcode(WorldPackets::Misc::WorldTeleport& worldTeleport); void HandleMinimapPingOpcode(WorldPackets::Party::MinimapPingClient& packet); void HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet); @@ -1520,8 +1557,7 @@ class WorldSession void SendLfgTeleportError(uint8 err); void HandleSelfResOpcode(WorldPackets::Spells::SelfRes& packet); - void HandleComplainOpcode(WorldPacket& recvData); - void HandleRequestPetInfoOpcode(WorldPacket& recvData); + void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo& packet); // Socket gem void HandleSocketGems(WorldPackets::Item::SocketGems& socketGems); @@ -1529,9 +1565,9 @@ class WorldSession void HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& cancelTempEnchantment); void HandleGetItemPurchaseData(WorldPackets::Item::GetItemPurchaseData& packet); - void HandleItemRefund(WorldPacket& recvData); + void HandleItemRefund(WorldPackets::Item::ItemPurchaseRefund& packet); - void HandleSetTaxiBenchmarkOpcode(WorldPacket& recvData); + void HandleSetTaxiBenchmark(WorldPackets::Misc::SetTaxiBenchmarkMode& packet); // Guild Bank void HandleGuildPermissionsQuery(WorldPackets::Guild::GuildPermissionsQuery& packet); @@ -1594,8 +1630,6 @@ class WorldSession void HandleUITimeRequest(WorldPackets::Misc::UITimeRequest& /*request*/); void HandleQueryQuestCompletionNPCs(WorldPackets::Query::QueryQuestCompletionNPCs& queryQuestCompletionNPCs); void HandleQuestPOIQuery(WorldPackets::Query::QuestPOIQuery& questPoiQuery); - void HandleUpdateProjectilePosition(WorldPacket& recvPacket); - void HandleUpdateMissileTrajectory(WorldPacket& recvPacket); void HandleViolenceLevel(WorldPackets::Misc::ViolenceLevel& violenceLevel); void HandleObjectUpdateFailedOpcode(WorldPackets::Misc::ObjectUpdateFailed& objectUpdateFailed); void HandleObjectUpdateRescuedOpcode(WorldPackets::Misc::ObjectUpdateRescued& objectUpdateRescued); @@ -1635,6 +1669,9 @@ class WorldSession void HandleBattlePetSummon(WorldPackets::BattlePet::BattlePetSummon& battlePetSummon); void HandleCageBattlePet(WorldPackets::BattlePet::CageBattlePet& cageBattlePet); + // Warden + void HandleWardenData(WorldPackets::Warden::WardenData& packet); + union ConnectToKey { struct diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 8f8c9d89502..acec25a7363 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -38,6 +38,17 @@ struct CompressedWorldPacket uint32 CompressedAdler; }; +class EncryptablePacket : public WorldPacket +{ +public: + EncryptablePacket(WorldPacket const& packet, bool encrypt) : WorldPacket(packet), _encrypt(encrypt) { } + + bool NeedsEncryption() const { return _encrypt; } + +private: + bool _encrypt; +}; + #pragma pack(pop) using boost::asio::ip::tcp; @@ -76,11 +87,8 @@ void WorldSocket::Start() stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); - { - std::lock_guard<std::mutex> guard(_queryLock); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1)); - _queryFuture = LoginDatabase.AsyncQuery(stmt); - } + _queryCallback = std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); } void WorldSocket::CheckIpCallback(PreparedQueryResult result) @@ -116,23 +124,50 @@ void WorldSocket::CheckIpCallback(PreparedQueryResult result) initializer.Write(&header, sizeof(header.Setup.Size)); initializer.Write(ServerConnectionInitialize.c_str(), ServerConnectionInitialize.length()); - std::unique_lock<std::mutex> guard(_writeLock); - QueuePacket(std::move(initializer), guard); + // - io_service.run thread, safe. + QueuePacket(std::move(initializer)); } bool WorldSocket::Update() { + EncryptablePacket* queued; + MessageBuffer buffer; + while (_bufferQueue.Dequeue(queued)) + { + uint32 sizeOfHeader = SizeOfServerHeader[queued->NeedsEncryption()]; + uint32 packetSize = queued->size(); + if (packetSize > MinSizeForCompression && queued->NeedsEncryption()) + packetSize = compressBound(packetSize) + sizeof(CompressedWorldPacket); + + if (buffer.GetRemainingSpace() < packetSize + sizeOfHeader) + { + QueuePacket(std::move(buffer)); + buffer.Resize(4096); + } + + if (buffer.GetRemainingSpace() >= packetSize + sizeOfHeader) + WritePacketToBuffer(*queued, buffer); + else // single packet larger than 4096 bytes + { + MessageBuffer packetBuffer(packetSize + sizeOfHeader); + WritePacketToBuffer(*queued, packetBuffer); + QueuePacket(std::move(packetBuffer)); + } + + delete queued; + } + + if (buffer.GetActiveSize() > 0) + QueuePacket(std::move(buffer)); + if (!BaseSocket::Update()) return false; + if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - std::lock_guard<std::mutex> guard(_queryLock); - if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - auto callback = std::move(_queryCallback); - _queryCallback = nullptr; - callback(_queryFuture.get()); - } + auto callback = _queryCallback; + _queryCallback = nullptr; + callback(_queryFuture.get()); } return true; @@ -428,29 +463,13 @@ void WorldSocket::SendPacket(WorldPacket const& packet) if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort(), GetConnectionType()); - uint32 packetSize = packet.size(); - uint32 sizeOfHeader = SizeOfServerHeader[_authCrypt.IsInitialized()]; - if (packetSize > MinSizeForCompression && _authCrypt.IsInitialized()) - packetSize = compressBound(packetSize) + sizeof(CompressedWorldPacket); - - std::unique_lock<std::mutex> guard(_writeLock); - -#ifndef TC_SOCKET_USE_IOCP - if (_writeQueue.empty() && _writeBuffer.GetRemainingSpace() >= sizeOfHeader + packetSize) - WritePacketToBuffer(packet, _writeBuffer); - else -#endif - { - MessageBuffer buffer(sizeOfHeader + packetSize); - WritePacketToBuffer(packet, buffer); - QueuePacket(std::move(buffer), guard); - } + _bufferQueue.Enqueue(new EncryptablePacket(packet, _authCrypt.IsInitialized())); } -void WorldSocket::WritePacketToBuffer(WorldPacket const& packet, MessageBuffer& buffer) +void WorldSocket::WritePacketToBuffer(EncryptablePacket const& packet, MessageBuffer& buffer) { ServerPktHeader header; - uint32 sizeOfHeader = SizeOfServerHeader[_authCrypt.IsInitialized()]; + uint32 sizeOfHeader = SizeOfServerHeader[packet.NeedsEncryption()]; uint32 opcode = packet.GetOpcode(); uint32 packetSize = packet.size(); @@ -458,7 +477,7 @@ void WorldSocket::WritePacketToBuffer(WorldPacket const& packet, MessageBuffer& uint8* headerPos = buffer.GetWritePointer(); buffer.WriteCompleted(sizeOfHeader); - if (packetSize > MinSizeForCompression && _authCrypt.IsInitialized()) + if (packetSize > MinSizeForCompression && packet.NeedsEncryption()) { CompressedWorldPacket cmp; cmp.UncompressedSize = packetSize + 4; @@ -481,7 +500,7 @@ void WorldSocket::WritePacketToBuffer(WorldPacket const& packet, MessageBuffer& else if (!packet.empty()) buffer.Write(packet.contents(), packet.size()); - if (_authCrypt.IsInitialized()) + if (packet.NeedsEncryption()) { header.Normal.Size = packetSize; header.Normal.Command = opcode; @@ -598,11 +617,8 @@ void WorldSocket::HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSess stmt->setInt32(0, int32(realm.Id.Realm)); stmt->setString(1, authSession->Account); - { - std::lock_guard<std::mutex> guard(_queryLock); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1)); - _queryFuture = LoginDatabase.AsyncQuery(stmt); - } + _queryCallback = std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); } void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result) @@ -768,7 +784,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: if (wardenActive) _worldSession->InitWarden(&account.Game.SessionKey, account.BattleNet.OS); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1)); + _queryCallback = std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1); _queryFuture = _worldSession->LoadPermissionsAsync(); AsyncRead(); } @@ -801,11 +817,8 @@ void WorldSocket::HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth: PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_CONTINUED_SESSION); stmt->setUInt32(0, accountId); - { - std::lock_guard<std::mutex> guard(_queryLock); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthContinuedSessionCallback, this, authSession, std::placeholders::_1)); - _queryFuture = LoginDatabase.AsyncQuery(stmt); - } + _queryCallback = std::bind(&WorldSocket::HandleAuthContinuedSessionCallback, this, authSession, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); } void WorldSocket::HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result) diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 59975c12f13..205494ca4ea 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -26,12 +26,13 @@ #include "Util.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "MPSCQueue.h" #include <chrono> #include <boost/asio/ip/tcp.hpp> -#include <boost/asio/buffer.hpp> using boost::asio::ip::tcp; struct z_stream_s; +class EncryptablePacket; namespace WorldPackets { @@ -112,7 +113,7 @@ private: void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const; /// sends and logs network.opcode without accessing WorldSession void SendPacketAndLogOpcode(WorldPacket const& packet); - void WritePacketToBuffer(WorldPacket const& packet, MessageBuffer& buffer); + void WritePacketToBuffer(EncryptablePacket const& packet, MessageBuffer& buffer); uint32 CompressPacket(uint8* buffer, WorldPacket const& packet); void HandleSendAuthSession(); @@ -143,12 +144,12 @@ private: MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; + MPSCQueue<EncryptablePacket> _bufferQueue; z_stream_s* _compressionStream; bool _initialized; - std::mutex _queryLock; PreparedQueryResultFuture _queryFuture; std::function<void(PreparedQueryResult&&)> _queryCallback; std::string _ipCountry; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 937483e1179..4f42e943245 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -24,9 +24,9 @@ #include <boost/system/error_code.hpp> -static void OnSocketAccept(tcp::socket&& sock) +static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) { - sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock)); + sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex); } class WorldSocketThread : public NetworkThread<WorldSocket> @@ -49,7 +49,7 @@ WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _instanceAcceptor(nullptr), WorldSocketMgr::~WorldSocketMgr() { - delete _instanceAcceptor; + ASSERT(!_instanceAcceptor, "StopNetwork must be called prior to WorldSocketMgr destruction"); } bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) @@ -73,8 +73,11 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string BaseSocketMgr::StartNetwork(service, bindIp, port); _instanceAcceptor = new AsyncAcceptor(service, bindIp, uint16(sWorld->getIntConfig(CONFIG_PORT_INSTANCE))); - _acceptor->AsyncAcceptManaged(&OnSocketAccept); - _instanceAcceptor->AsyncAcceptManaged(&OnSocketAccept); + _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + _instanceAcceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + + _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); + _instanceAcceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); sScriptMgr->OnNetworkStart(); return true; @@ -82,12 +85,16 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string void WorldSocketMgr::StopNetwork() { + _instanceAcceptor->Close(); BaseSocketMgr::StopNetwork(); + delete _instanceAcceptor; + _instanceAcceptor = nullptr; + sScriptMgr->OnNetworkStop(); } -void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock) +void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) { // set some options here if (_socketSendBufferSize >= 0) @@ -115,7 +122,7 @@ void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock) //sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff); - BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock)); + BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex); } NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h index d4bf4115deb..2079b62d14f 100644 --- a/src/server/game/Server/WorldSocketMgr.h +++ b/src/server/game/Server/WorldSocketMgr.h @@ -49,7 +49,7 @@ public: /// Stops all network threads, It will wait for all running threads . void StopNetwork() override; - void OnSocketOpen(tcp::socket&& sock) override; + void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override; protected: WorldSocketMgr(); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 0be2fed3424..1cef132fcda 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5501,7 +5501,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->IsAlive()) { Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(m_originalCaster->GetZoneId()); - if (AreaTableEntry const* area = GetAreaEntryByAreaID(m_originalCaster->GetAreaId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(m_originalCaster->GetAreaId())) if (area->Flags[0] & AREA_FLAG_NO_FLY_ZONE || (Bf && !Bf->CanFlyIn())) return (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE; } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d014119f00c..a43c30917b4 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3866,14 +3866,14 @@ void Spell::EffectDuel(SpellEffIndex effIndex) return; // Players can only fight a duel in zones with this flag - AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetAreaId()); + AreaTableEntry const* casterAreaEntry = sAreaTableStore.LookupEntry(caster->GetAreaId()); if (casterAreaEntry && !(casterAreaEntry->Flags[0] & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here return; } - AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetAreaId()); + AreaTableEntry const* targetAreaEntry = sAreaTableStore.LookupEntry(target->GetAreaId()); if (targetAreaEntry && !(targetAreaEntry->Flags[0] & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index dd73c261853..a628e934465 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2625,7 +2625,7 @@ void SpellMgr::LoadSpellAreas() } } - if (spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) + if (spellArea.areaId && !sAreaTableStore.LookupEntry(spellArea.areaId)) { TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong area (%u) requirement", spell, spellArea.areaId); continue; diff --git a/src/server/game/Support/SupportMgr.h b/src/server/game/Support/SupportMgr.h index c312651d789..f79f71e7b2f 100644 --- a/src/server/game/Support/SupportMgr.h +++ b/src/server/game/Support/SupportMgr.h @@ -41,6 +41,13 @@ enum GMSupportComplaintType GMTICKET_SUPPORT_COMPLAINT_TYPE_SPAMMING = 24 }; +enum SupportSpamType +{ + SUPPORT_SPAM_TYPE_MAIL = 0, + SUPPORT_SPAM_TYPE_CHAT = 1, + SUPPORT_SPAM_TYPE_CALENDAR = 2 +}; + using ChatLog = WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLog; class Ticket diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp index 5f5147c739b..906e0cade08 100644 --- a/src/server/game/Warden/Warden.cpp +++ b/src/server/game/Warden/Warden.cpp @@ -26,6 +26,7 @@ #include "Util.h" #include "Warden.h" #include "AccountMgr.h" +#include "WardenPackets.h" #include <openssl/sha.h> @@ -220,16 +221,16 @@ std::string Warden::Penalty(WardenCheck* check /*= NULL*/) return "Undefined"; } -void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData) +void WorldSession::HandleWardenData(WorldPackets::Warden::WardenData& packet) { - if (!_warden || recvData.empty()) + if (!_warden || packet.Data.empty()) return; - _warden->DecryptData(recvData.contents(), recvData.size()); + _warden->DecryptData(packet.Data.contents(), packet.Data.size()); uint8 opcode; - recvData >> opcode; - TC_LOG_DEBUG("warden", "Got packet, opcode %02X, size %u", opcode, uint32(recvData.size())); - recvData.hexlike(); + packet.Data >> opcode; + TC_LOG_DEBUG("warden", "Got packet, opcode %02X, size %u", opcode, uint32(packet.Data.size())); + packet.Data.hexlike(); switch (opcode) { @@ -240,20 +241,20 @@ void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData) _warden->RequestHash(); break; case WARDEN_CMSG_CHEAT_CHECKS_RESULT: - _warden->HandleData(recvData); + _warden->HandleData(packet.Data); break; case WARDEN_CMSG_MEM_CHECKS_RESULT: TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MEM_CHECKS_RESULT received!"); break; case WARDEN_CMSG_HASH_RESULT: - _warden->HandleHashResult(recvData); + _warden->HandleHashResult(packet.Data); _warden->InitializeModule(); break; case WARDEN_CMSG_MODULE_FAILED: TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MODULE_FAILED received!"); break; default: - TC_LOG_DEBUG("warden", "Got unknown warden opcode %02X of size %u.", opcode, uint32(recvData.size() - 1)); + TC_LOG_DEBUG("warden", "Got unknown warden opcode %02X of size %u.", opcode, uint32(packet.Data.size() - 1)); break; } } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 972860f790f..d2ccb025165 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -227,7 +227,7 @@ void World::AddSession(WorldSession* s) addSessQueue.add(s); } -void World::AddInstanceSocket(std::shared_ptr<WorldSocket> sock, uint64 connectToKey) +void World::AddInstanceSocket(std::weak_ptr<WorldSocket> sock, uint64 connectToKey) { _linkSocketQueue.add(std::make_pair(sock, connectToKey)); } @@ -298,25 +298,28 @@ void World::AddSession_(WorldSession* s) } } -void World::ProcessLinkInstanceSocket(std::pair<std::shared_ptr<WorldSocket>, uint64> linkInfo) +void World::ProcessLinkInstanceSocket(std::pair<std::weak_ptr<WorldSocket>, uint64> linkInfo) { - if (!linkInfo.first->IsOpen()) - return; + if (std::shared_ptr<WorldSocket> sock = linkInfo.first.lock()) + { + if (!sock->IsOpen()) + return; - WorldSession::ConnectToKey key; - key.Raw = linkInfo.second; + WorldSession::ConnectToKey key; + key.Raw = linkInfo.second; - WorldSession* session = FindSession(uint32(key.Fields.AccountId)); - if (!session || session->GetConnectToInstanceKey() != linkInfo.second) - { - linkInfo.first->SendAuthResponseError(AUTH_SESSION_EXPIRED); - linkInfo.first->DelayedCloseSocket(); - return; - } + WorldSession* session = FindSession(uint32(key.Fields.AccountId)); + if (!session || session->GetConnectToInstanceKey() != linkInfo.second) + { + sock->SendAuthResponseError(AUTH_SESSION_EXPIRED); + sock->DelayedCloseSocket(); + return; + } - linkInfo.first->SetWorldSession(session); - session->AddInstanceConnection(linkInfo.first); - session->HandleContinuePlayerLogin(); + sock->SetWorldSession(session); + session->AddInstanceConnection(sock); + session->HandleContinuePlayerLogin(); + } } bool World::HasRecentlyDisconnected(WorldSession* session) @@ -427,9 +430,9 @@ void World::LoadConfigSettings(bool reload) m_defaultDbcLocale = LocaleConstant(sConfigMgr->GetIntDefault("DBC.Locale", 0)); - if (m_defaultDbcLocale >= TOTAL_LOCALES || m_defaultDbcLocale < LOCALE_enUS) + if (m_defaultDbcLocale >= TOTAL_LOCALES || m_defaultDbcLocale < LOCALE_enUS || m_defaultDbcLocale == LOCALE_none) { - TC_LOG_ERROR("server.loading", "Incorrect DBC.Locale! Must be >= 0 and < %d (set to 0)", TOTAL_LOCALES); + TC_LOG_ERROR("server.loading", "Incorrect DBC.Locale! Must be >= 0 and < %d and not %d (set to 0)", TOTAL_LOCALES, LOCALE_none); m_defaultDbcLocale = LOCALE_enUS; } @@ -1044,12 +1047,12 @@ void World::LoadConfigSettings(bool reload) if (reload) { - uint32 val = sConfigMgr->GetIntDefault("Expansion", 2); + uint32 val = sConfigMgr->GetIntDefault("Expansion", CURRENT_EXPANSION); if (val != m_int_configs[CONFIG_EXPANSION]) TC_LOG_ERROR("server.loading", "Expansion option can't be changed at worldserver.conf reload, using current value (%u).", m_int_configs[CONFIG_EXPANSION]); } else - m_int_configs[CONFIG_EXPANSION] = sConfigMgr->GetIntDefault("Expansion", 2); + m_int_configs[CONFIG_EXPANSION] = sConfigMgr->GetIntDefault("Expansion", CURRENT_EXPANSION); m_int_configs[CONFIG_CHATFLOOD_MESSAGE_COUNT] = sConfigMgr->GetIntDefault("ChatFlood.MessageCount", 10); m_int_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = sConfigMgr->GetIntDefault("ChatFlood.MessageDelay", 1); @@ -2821,7 +2824,7 @@ void World::SendServerMessage(ServerMessageType messageID, std::string stringPar void World::UpdateSessions(uint32 diff) { - std::pair<std::shared_ptr<WorldSocket>, uint64> linkInfo; + std::pair<std::weak_ptr<WorldSocket>, uint64> linkInfo; while (_linkSocketQueue.next(linkInfo)) ProcessLinkInstanceSocket(std::move(linkInfo)); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index d28738ffc8c..0f9a27c733f 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -568,7 +568,7 @@ class World WorldSession* FindSession(uint32 id) const; void AddSession(WorldSession* s); - void AddInstanceSocket(std::shared_ptr<WorldSocket> sock, uint64 connectToKey); + void AddInstanceSocket(std::weak_ptr<WorldSocket> sock, uint64 connectToKey); void SendAutoBroadcast(); bool RemoveSession(uint32 id); /// Get the number of current active sessions @@ -878,8 +878,8 @@ class World void AddSession_(WorldSession* s); LockedQueue<WorldSession*> addSessQueue; - void ProcessLinkInstanceSocket(std::pair<std::shared_ptr<WorldSocket>, uint64> linkInfo); - LockedQueue<std::pair<std::shared_ptr<WorldSocket>, uint64>> _linkSocketQueue; + void ProcessLinkInstanceSocket(std::pair<std::weak_ptr<WorldSocket>, uint64> linkInfo); + LockedQueue<std::pair<std::weak_ptr<WorldSocket>, uint64>> _linkSocketQueue; // used versions std::string m_DBVersion; diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 0995746b4b0..081280532fb 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -487,7 +487,7 @@ public: uint32 areaId = id ? (uint32)atoi(id) : player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (x < 0 || x > 100 || y < 0 || y > 100 || !areaEntry) { @@ -497,7 +497,7 @@ public: } // update to parent zone if exist (client map show only zones without parents) - AreaTableEntry const* zoneEntry = areaEntry->ParentAreaID ? GetAreaEntryByAreaID(areaEntry->ParentAreaID) : areaEntry; + AreaTableEntry const* zoneEntry = areaEntry->ParentAreaID ? sAreaTableStore.LookupEntry(areaEntry->ParentAreaID) : areaEntry; ASSERT(zoneEntry); Map const* map = sMapMgr->CreateBaseMap(zoneEntry->MapID); diff --git a/src/server/scripts/Commands/cs_group.cpp b/src/server/scripts/Commands/cs_group.cpp index 77a157443b8..33356409f17 100644 --- a/src/server/scripts/Commands/cs_group.cpp +++ b/src/server/scripts/Commands/cs_group.cpp @@ -347,10 +347,10 @@ public: onlineState = "online"; phase = (!p->IsGameMaster() ? p->GetPhaseMask() : -1); - AreaTableEntry const* area = GetAreaEntryByAreaID(p->GetAreaId()); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(p->GetAreaId()); if (area) { - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->ParentAreaID); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->ParentAreaID); if (zone) zoneName = zone->AreaName_lang; } diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index 847634637b0..57bf557771b 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -97,9 +97,9 @@ public: wstrToLower(wNamePart); // Search in AreaTable.dbc - for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag) + for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) { - AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(areaflag); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i); if (areaEntry) { std::string name = areaEntry->AreaName_lang; @@ -118,9 +118,9 @@ public: // send area in "id - [name]" format std::ostringstream ss; if (handler->GetSession()) - ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name<< "]|h|r"; + ss << i << " - |cffffffff|Harea:" << i << "|h[" << name<< "]|h|r"; else - ss << areaEntry->ID << " - " << name; + ss << i << " - " << name; handler->SendSysMessage(ss.str().c_str()); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 778eaaf44f1..0e9e8aecfec 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -201,8 +201,8 @@ public: uint32 mapId = object->GetMapId(); MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); - AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zoneId); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); float zoneX = object->GetPositionX(); float zoneY = object->GetPositionY(); @@ -988,7 +988,7 @@ public: uint32 zoneId = player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry || areaEntry->ParentAreaID !=0) { handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, graveyardId, zoneId); @@ -1079,17 +1079,30 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + if (area->AreaBit < 0) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } - if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + int32 offset = area->AreaBit / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - uint32 val = uint32((1 << (area % 32))); + uint32 val = uint32((1 << (area->AreaBit % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields | val))); @@ -1110,17 +1123,30 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + if (area->AreaBit < 0) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } - if (area < 0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + int32 offset = area->AreaBit / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - uint32 val = uint32((1 << (area % 32))); + uint32 val = uint32((1 << (area->AreaBit % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields ^ val))); @@ -1787,12 +1813,12 @@ public: // Position data MapEntry const* map = sMapStore.LookupEntry(mapId); - AreaTableEntry const* area = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId); if (area) { areaName = area->AreaName_lang; - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->ParentAreaID); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->ParentAreaID); if (zone) zoneName = zone->AreaName_lang; } diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index c726461044b..0a88ba17877 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -16,8 +16,10 @@ */ #include "ScriptMgr.h" +#include "ScriptedCreature.h" #include "InstanceScript.h" #include "magisters_terrace.h" +#include "EventMap.h" /* 0 - Selin Fireheart @@ -36,6 +38,8 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } // END }; +Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f }; + class instance_magisters_terrace : public InstanceMapScript { public: @@ -93,6 +97,10 @@ class instance_magisters_terrace : public InstanceMapScript case NPC_DELRISSA: DelrissaGUID = creature->GetGUID(); break; + case NPC_KALECGOS: + case NPC_HUMAN_KALECGOS: + KalecgosGUID = creature->GetGUID(); + break; default: break; } @@ -139,6 +147,25 @@ class instance_magisters_terrace : public InstanceMapScript } } + void ProcessEvent(WorldObject* obj, uint32 eventId) override + { + if (eventId == EVENT_SPAWN_KALECGOS) + if (!ObjectAccessor::GetCreature(*obj, KalecgosGUID) && Events.Empty()) + Events.ScheduleEvent(EVENT_SPAWN_KALECGOS, Minutes(1)); + } + + void Update(uint32 diff) override + { + Events.Update(diff); + + if (Events.ExecuteEvent() == EVENT_SPAWN_KALECGOS) + if (Creature* kalecgos = instance->SummonCreature(NPC_KALECGOS, KalecgosSpawnPos)) + { + kalecgos->GetMotionMaster()->MovePath(PATH_KALECGOS_FLIGHT, false); + kalecgos->AI()->Talk(SAY_KALECGOS_SPAWN); + } + } + bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -177,10 +204,12 @@ class instance_magisters_terrace : public InstanceMapScript } protected: + EventMap Events; ObjectGuid SelinGUID; ObjectGuid DelrissaGUID; ObjectGuid KaelStatue[2]; ObjectGuid EscapeOrbGUID; + ObjectGuid KalecgosGUID; uint32 DelrissaDeathCount; }; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp index e216a024468..5b90ac8ccf4 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp @@ -31,7 +31,8 @@ EndContentData */ #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "Player.h" -#include "SpellInfo.h" +#include "magisters_terrace.h" +#include "EventMap.h" /*###### ## npc_kalecgos @@ -39,30 +40,29 @@ EndContentData */ enum Spells { - SPELL_TRANSFORM_TO_KAEL = 44670, + SPELL_KALECGOS_TRANSFORM = 44670, + SPELL_TRANSFORM_VISUAL = 24085, + SPELL_CAMERA_SHAKE = 44762, SPELL_ORB_KILL_CREDIT = 46307 }; -enum Creatures +enum MovementPoints { - NPC_KAEL = 24848 //human form entry + POINT_ID_PREPARE_LANDING = 6 }; -enum Misc +enum EventIds { - POINT_ID_LAND = 1 + EVENT_KALECGOS_TRANSFORM = 1, + EVENT_KALECGOS_LANDING = 2 }; -const float afKaelLandPoint[] = {225.045f, -276.236f, -5.434f}; - #define GOSSIP_ITEM_KAEL_1 "Who are you?" #define GOSSIP_ITEM_KAEL_2 "What can we do to assist you?" #define GOSSIP_ITEM_KAEL_3 "What brings you to the Sunwell?" #define GOSSIP_ITEM_KAEL_4 "You're not alone here?" #define GOSSIP_ITEM_KAEL_5 "What would Kil'jaeden want with a mortal woman?" -// This is friendly keal that appear after used Orb. -// If we assume DB handle summon, summon appear somewhere outside the platform where Orb is class npc_kalecgos : public CreatureScript { public: @@ -115,52 +115,46 @@ public: struct npc_kalecgosAI : public ScriptedAI { - npc_kalecgosAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } + npc_kalecgosAI(Creature* creature) : ScriptedAI(creature) { } - void Initialize() + void MovementInform(uint32 type, uint32 pointId) override { - m_uiTransformTimer = 0; - } - - uint32 m_uiTransformTimer; - - void Reset() override - { - Initialize(); - - // we must assume he appear as dragon somewhere outside the platform of orb, and then move directly to here - if (me->GetEntry() != NPC_KAEL) - me->GetMotionMaster()->MovePoint(POINT_ID_LAND, afKaelLandPoint[0], afKaelLandPoint[1], afKaelLandPoint[2]); - } - - void MovementInform(uint32 uiType, uint32 uiPointId) override - { - if (uiType != POINT_MOTION_TYPE) + if (type != WAYPOINT_MOTION_TYPE) return; - if (uiPointId == POINT_ID_LAND) - m_uiTransformTimer = MINUTE*IN_MILLISECONDS; + if (pointId == POINT_ID_PREPARE_LANDING) + { + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetDisableGravity(false); + me->SetHover(false); + events.ScheduleEvent(EVENT_KALECGOS_LANDING, Seconds(2)); + } } - void UpdateAI(uint32 uiDiff) override + void UpdateAI(uint32 diff) override { - if (m_uiTransformTimer) + events.Update(diff); + + switch (events.ExecuteEvent()) { - if (m_uiTransformTimer <= uiDiff) - { + case EVENT_KALECGOS_LANDING: + DoCastAOE(SPELL_CAMERA_SHAKE); + me->SetObjectScale(0.6f); + events.ScheduleEvent(EVENT_KALECGOS_TRANSFORM, Seconds(1)); + break; + case EVENT_KALECGOS_TRANSFORM: DoCast(me, SPELL_ORB_KILL_CREDIT, true); - - // Transform and update entry, now ready for quest/read gossip - DoCast(me, SPELL_TRANSFORM_TO_KAEL, false); - me->UpdateEntry(NPC_KAEL); - - m_uiTransformTimer = 0; - } else m_uiTransformTimer -= uiDiff; + DoCast(me, SPELL_TRANSFORM_VISUAL, false); + DoCast(me, SPELL_KALECGOS_TRANSFORM, false); + me->UpdateEntry(NPC_HUMAN_KALECGOS); + break; + default: + break; } } + + private: + EventMap events; }; }; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h index 917ad0eb50b..05718dfc1dd 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h @@ -42,7 +42,9 @@ enum CreatureIds { NPC_SELIN = 24723, NPC_DELRISSA = 24560, - NPC_FEL_CRYSTAL = 24722 + NPC_FEL_CRYSTAL = 24722, + NPC_KALECGOS = 24844, + NPC_HUMAN_KALECGOS = 24848 }; enum GameObjectIds @@ -57,4 +59,19 @@ enum GameObjectIds GO_ESCAPE_ORB = 188173 }; +enum InstanceEventIds +{ + EVENT_SPAWN_KALECGOS = 16547 +}; + +enum InstanceText +{ + SAY_KALECGOS_SPAWN = 0 +}; + +enum MovementData +{ + PATH_KALECGOS_FLIGHT = 248440 +}; + #endif diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 44686e80115..77657870320 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -979,6 +979,9 @@ class go_celestial_planetarium_access : public GameObjectScript bool GossipHello(Player* player) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE)) + return true; + bool hasKey = true; if (LockEntry const* lock = sLockStore.LookupEntry(go->GetGOInfo()->GetLockId())) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 2907fb09186..e1c02e7ad99 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1186,6 +1186,7 @@ class npc_brann_bronzebeard_ulduar_intro : public CreatureScript { if (menuId == GOSSIP_MENU_BRANN_BRONZEBEARD && gossipListId == GOSSIP_OPTION_BRANN_BRONZEBEARD) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); if (Creature* loreKeeper = _instance->GetCreature(DATA_LORE_KEEPER_OF_NORGANNON)) loreKeeper->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); @@ -1238,6 +1239,7 @@ class npc_lorekeeper : public CreatureScript { if (menuId == GOSSIP_MENU_LORE_KEEPER && gossipListId == GOSSIP_OPTION_LORE_KEEPER) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); _instance->instance->LoadGrid(364, -16); // make sure leviathan is loaded @@ -1250,6 +1252,7 @@ class npc_lorekeeper : public CreatureScript { if (Creature* brann = _instance->GetCreature(DATA_BRANN_BRONZEBEARD_INTRO)) { + brann->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); delorah->GetMotionMaster()->MovePoint(0, brann->GetPositionX() - 4, brann->GetPositionY(), brann->GetPositionZ()); /// @todo delorah->AI()->Talk(xxxx, brann->GetGUID()); when reached at branz } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 059ec7de4e8..3378185c2a8 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -1636,6 +1636,9 @@ class go_mimiron_hardmode_button : public GameObjectScript bool OnGossipHello(Player* /*player*/, GameObject* go) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE)) + return true; + InstanceScript* instance = go->GetInstanceScript(); if (!instance) return false; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 66cd00489f8..fceeb108ebf 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -144,6 +144,7 @@ enum Spells // Thaladred the Darkener spells SPELL_PSYCHIC_BLOW = 10689, SPELL_SILENCE = 30225, + SPELL_REND = 36965, // Lord Sanguinar spells SPELL_BELLOWING_ROAR = 40636, // Grand Astromancer Capernian spells @@ -880,11 +881,13 @@ class boss_thaladred_the_darkener : public CreatureScript { Gaze_Timer = 100; Silence_Timer = 20000; + Rend_Timer = 4000; PsychicBlow_Timer = 10000; } uint32 Gaze_Timer; uint32 Silence_Timer; + uint32 Rend_Timer; uint32 PsychicBlow_Timer; void Reset() override @@ -938,6 +941,15 @@ class boss_thaladred_the_darkener : public CreatureScript else Silence_Timer -= diff; + //Rend_Timer + if (Rend_Timer <= diff) + { + DoCastVictim(SPELL_REND); + Rend_Timer = 4000; + } + else + Rend_Timer -= diff; + //PsychicBlow_Timer if (PsychicBlow_Timer <= diff) { diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 5e8b5ab8885..5887aded51c 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -912,6 +912,8 @@ class spell_pal_light_s_beacon : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { + if (!eventInfo.GetActionTarget()) + return false; if (eventInfo.GetActionTarget()->HasAura(SPELL_PALADIN_BEACON_OF_LIGHT, eventInfo.GetActor()->GetGUID())) return false; return true; diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 2619a5af065..5d686f6aa38 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -62,7 +62,11 @@ enum WarriorSpells SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1 = 64849, SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_2 = 64850, SPELL_WARRIOR_VIGILANCE_PROC = 50725, - SPELL_WARRIOR_VENGEANCE = 76691 + SPELL_WARRIOR_VENGEANCE = 76691, + SPELL_WARRIOR_HEROIC_LEAP_JUMP = 178368, + SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP = 159708, + SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP_BUFF = 133278, + SPELL_WARRIOR_IMPROVED_HEROIC_LEAP = 157449, }; enum WarriorSpellIcons @@ -948,6 +952,117 @@ class spell_warr_vigilance_trigger : public SpellScriptLoader } }; +// Heroic leap - 6544 +class spell_warr_heroic_leap : public SpellScriptLoader +{ +public: + spell_warr_heroic_leap() : SpellScriptLoader("spell_warr_heroic_leap") { } + + class spell_warr_heroic_leap_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warr_heroic_leap_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_HEROIC_LEAP_JUMP)) + return false; + return true; + } + + SpellCastResult CheckElevation() + { + if (WorldLocation const* dest = GetExplTargetDest()) + { + if (GetCaster()->HasUnitMovementFlag(MOVEMENTFLAG_ROOT)) + return SPELL_FAILED_ROOTED; + + if (GetCaster()->GetMap()->Instanceable()) + { + float range = GetSpellInfo()->GetMaxRange(true, GetCaster()) * 1.5f; + + PathGenerator generatedPath(GetCaster()); + generatedPath.SetPathLengthLimit(range); + + bool result = generatedPath.CalculatePath(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), false, true); + if (generatedPath.GetPathType() & PATHFIND_SHORT) + return SPELL_FAILED_OUT_OF_RANGE; + else if (!result || generatedPath.GetPathType() & PATHFIND_NOPATH) + { + result = generatedPath.CalculatePath(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), false, false); + if (generatedPath.GetPathType() & PATHFIND_SHORT) + return SPELL_FAILED_OUT_OF_RANGE; + else if (!result || generatedPath.GetPathType() & PATHFIND_NOPATH) + return SPELL_FAILED_NOPATH; + } + } + else if (dest->GetPositionZ() > GetCaster()->GetPositionZ() + 4.0f) + return SPELL_FAILED_NOPATH; + + return SPELL_CAST_OK; + } + + return SPELL_FAILED_NO_VALID_TARGETS; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (WorldLocation* dest = GetHitDest()) + GetCaster()->CastSpell(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), SPELL_WARRIOR_HEROIC_LEAP_JUMP, true); + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_warr_heroic_leap_SpellScript::CheckElevation); + OnEffectHit += SpellEffectFn(spell_warr_heroic_leap_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_warr_heroic_leap_SpellScript(); + } +}; + +// Heroic Leap (triggered by Heroic Leap (6544)) - 178368 +class spell_warr_heroic_leap_jump : public SpellScriptLoader +{ +public: + spell_warr_heroic_leap_jump() : SpellScriptLoader("spell_warr_heroic_leap_jump") { } + + class spell_warr_heroic_leap_jump_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warr_heroic_leap_jump_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP_BUFF) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_IMPROVED_HEROIC_LEAP) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_TAUNT)) + return false; + return true; + } + + void AfterJump(SpellEffIndex /*effIndex*/) + { + if (GetCaster()->HasAura(SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP)) + GetCaster()->CastSpell(GetCaster(), SPELL_WARRIOR_GLYPH_OF_HEROIC_LEAP_BUFF, true); + if (GetCaster()->HasAura(SPELL_WARRIOR_IMPROVED_HEROIC_LEAP)) + GetCaster()->GetSpellHistory()->ResetCooldown(SPELL_WARRIOR_TAUNT, true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_warr_heroic_leap_jump_SpellScript::AfterJump, EFFECT_1, SPELL_EFFECT_JUMP_DEST); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_warr_heroic_leap_jump_SpellScript(); + } +}; + void AddSC_warrior_spell_scripts() { new spell_warr_bloodthirst(); @@ -972,4 +1087,6 @@ void AddSC_warrior_spell_scripts() new spell_warr_victorious(); new spell_warr_vigilance(); new spell_warr_vigilance_trigger(); + new spell_warr_heroic_leap(); + new spell_warr_heroic_leap_jump(); } diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index 2fa1e448ff8..d21801a64ac 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -20,34 +20,39 @@ #include "Log.h" #include <boost/asio.hpp> +#include <functional> using boost::asio::ip::tcp; class AsyncAcceptor { public: - typedef void(*ManagerAcceptHandler)(tcp::socket&& newSocket); + typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); AsyncAcceptor(boost::asio::io_service& ioService, std::string const& bindIp, uint16 port) : _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)), - _socket(ioService), _closed(false) + _socket(ioService), _closed(false), _socketFactory(std::bind(&AsyncAcceptor::DefeaultSocketFactory, this)) { } - template <class T> + template<class T> void AsyncAccept(); - void AsyncAcceptManaged(ManagerAcceptHandler mgrHandler) + template<AcceptCallback acceptCallback> + void AsyncAcceptWithCallback() { - _acceptor.async_accept(_socket, [this, mgrHandler](boost::system::error_code error) + tcp::socket* socket; + uint32 threadIndex; + std::tie(socket, threadIndex) = _socketFactory(); + _acceptor.async_accept(*socket, [this, socket, threadIndex](boost::system::error_code error) { if (!error) { try { - _socket.non_blocking(true); + socket->non_blocking(true); - mgrHandler(std::move(_socket)); + acceptCallback(std::move(*socket), threadIndex); } catch (boost::system::system_error const& err) { @@ -56,7 +61,7 @@ public: } if (!_closed) - AsyncAcceptManaged(mgrHandler); + this->AsyncAcceptWithCallback<acceptCallback>(); }); } @@ -69,10 +74,15 @@ public: _acceptor.close(err); } + void SetSocketFactory(std::function<std::pair<tcp::socket*, uint32>()> func) { _socketFactory = func; } + private: + std::pair<tcp::socket*, uint32> DefeaultSocketFactory() { return std::make_pair(&_socket, 0); } + tcp::acceptor _acceptor; tcp::socket _socket; std::atomic<bool> _closed; + std::function<std::pair<tcp::socket*, uint32>()> _socketFactory; }; template<class T> diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/server/shared/Networking/MessageBuffer.h index a6ed9b31e8f..42b65be8398 100644 --- a/src/server/shared/Networking/MessageBuffer.h +++ b/src/server/shared/Networking/MessageBuffer.h @@ -105,7 +105,7 @@ public: return std::move(_storage); } - MessageBuffer& operator=(MessageBuffer& right) + MessageBuffer& operator=(MessageBuffer const& right) { if (this != &right) { diff --git a/src/server/shared/Networking/NetworkThread.h b/src/server/shared/Networking/NetworkThread.h index e183209e989..5eb2fcb2f6a 100644 --- a/src/server/shared/Networking/NetworkThread.h +++ b/src/server/shared/Networking/NetworkThread.h @@ -22,6 +22,8 @@ #include "Errors.h" #include "Log.h" #include "Timer.h" +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/deadline_timer.hpp> #include <atomic> #include <chrono> #include <memory> @@ -29,11 +31,14 @@ #include <set> #include <thread> +using boost::asio::ip::tcp; + template<class SocketType> class NetworkThread { public: - NetworkThread() : _connections(0), _stopped(false), _thread(nullptr) + NetworkThread() : _connections(0), _stopped(false), _thread(nullptr), + _acceptSocket(_io_service), _updateTimer(_io_service) { } @@ -50,6 +55,7 @@ public: void Stop() { _stopped = true; + _io_service.stop(); } bool Start() @@ -80,10 +86,12 @@ public: std::lock_guard<std::mutex> lock(_newSocketsLock); ++_connections; - _newSockets.insert(sock); + _newSockets.push_back(sock); SocketAdded(sock); } + tcp::socket* GetSocketForAccept() { return &_acceptSocket; } + protected: virtual void SocketAdded(std::shared_ptr<SocketType> /*sock*/) { } virtual void SocketRemoved(std::shared_ptr<SocketType> /*sock*/) { } @@ -95,16 +103,15 @@ protected: if (_newSockets.empty()) return; - for (typename SocketSet::const_iterator i = _newSockets.begin(); i != _newSockets.end(); ++i) + for (std::shared_ptr<SocketType> sock : _newSockets) { - if (!(*i)->IsOpen()) + if (!sock->IsOpen()) { - SocketRemoved(*i); - + SocketRemoved(sock); --_connections; } else - _Sockets.insert(*i); + _sockets.push_back(sock); } _newSockets.clear(); @@ -114,55 +121,58 @@ protected: { TC_LOG_DEBUG("misc", "Network Thread Starting"); - typename SocketSet::iterator i, t; + _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); + _updateTimer.async_wait(std::bind(&NetworkThread<SocketType>::Update, this)); + _io_service.run(); - uint32 sleepTime = 10; - uint32 tickStart = 0, diff = 0; - while (!_stopped) - { - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + TC_LOG_DEBUG("misc", "Network Thread exits"); + _newSockets.clear(); + _sockets.clear(); + } + + void Update() + { + if (_stopped) + return; - tickStart = getMSTime(); + _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); + _updateTimer.async_wait(std::bind(&NetworkThread<SocketType>::Update, this)); - AddNewSockets(); + AddNewSockets(); - for (i = _Sockets.begin(); i != _Sockets.end();) + _sockets.erase(std::remove_if(_sockets.begin(), _sockets.end(), [this](std::shared_ptr<SocketType> sock) + { + if (!sock->Update()) { - if (!(*i)->Update()) - { - if ((*i)->IsOpen()) - (*i)->CloseSocket(); - - SocketRemoved(*i); - - --_connections; - _Sockets.erase(i++); - } - else - ++i; - } + if (sock->IsOpen()) + sock->CloseSocket(); - diff = GetMSTimeDiffToNow(tickStart); - sleepTime = diff > 10 ? 0 : 10 - diff; - } + SocketRemoved(sock); - TC_LOG_DEBUG("misc", "Network Thread exits"); - _newSockets.clear(); - _Sockets.clear(); + --_connections; + return true; + } + + return false; + }), _sockets.end()); } private: - typedef std::set<std::shared_ptr<SocketType> > SocketSet; + typedef std::vector<std::shared_ptr<SocketType>> SocketContainer; std::atomic<int32> _connections; std::atomic<bool> _stopped; std::thread* _thread; - SocketSet _Sockets; + SocketContainer _sockets; std::mutex _newSocketsLock; - SocketSet _newSockets; + SocketContainer _newSockets; + + boost::asio::io_service _io_service; + tcp::socket _acceptSocket; + boost::asio::deadline_timer _updateTimer; }; #endif // NetworkThread_h__ diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index 34ee50eb84e..07f427652aa 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -21,15 +21,11 @@ #include "MessageBuffer.h" #include "Log.h" #include <atomic> -#include <vector> -#include <mutex> #include <queue> #include <memory> #include <functional> #include <type_traits> #include <boost/asio/ip/tcp.hpp> -#include <boost/asio/write.hpp> -#include <boost/asio/read.hpp> using boost::asio::ip::tcp; @@ -63,14 +59,10 @@ public: return false; #ifndef TC_SOCKET_USE_IOCP - std::unique_lock<std::mutex> guard(_writeLock); - if (!guard) + if (_isWritingAsync || _writeQueue.empty()) return true; - if (_isWritingAsync || (!_writeBuffer.GetActiveSize() && _writeQueue.empty())) - return true; - - for (; WriteHandler(guard);) + for (; HandleQueue();) ; #endif @@ -98,14 +90,12 @@ public: std::bind(&Socket<T>::ReadHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } - void QueuePacket(MessageBuffer&& buffer, std::unique_lock<std::mutex>& guard) + void QueuePacket(MessageBuffer&& buffer) { _writeQueue.push(std::move(buffer)); #ifdef TC_SOCKET_USE_IOCP - AsyncProcessQueue(guard); -#else - (void)guard; + AsyncProcessQueue(); #endif } @@ -135,7 +125,7 @@ protected: virtual void ReadHandler() = 0; - bool AsyncProcessQueue(std::unique_lock<std::mutex>&) + bool AsyncProcessQueue() { if (_isWritingAsync) return false; @@ -157,19 +147,12 @@ protected: void SetNoDelay(bool enable) { boost::system::error_code err; - _socket.set_option(boost::asio::ip::tcp::no_delay(enable), err); + _socket.set_option(tcp::no_delay(enable), err); if (err) TC_LOG_DEBUG("network", "Socket::SetNoDelay: failed to set_option(boost::asio::ip::tcp::no_delay) for %s - %d (%s)", GetRemoteIpAddress().to_string().c_str(), err.value(), err.message().c_str()); } - std::mutex _writeLock; - std::queue<MessageBuffer> _writeQueue; -#ifndef TC_SOCKET_USE_IOCP - MessageBuffer _writeBuffer; -#endif - - boost::asio::io_service& io_service() { return _socket.get_io_service(); } private: void ReadHandlerInternal(boost::system::error_code error, size_t transferredBytes) @@ -190,15 +173,13 @@ private: { if (!error) { - std::unique_lock<std::mutex> deleteGuard(_writeLock); - _isWritingAsync = false; _writeQueue.front().ReadCompleted(transferedBytes); if (!_writeQueue.front().GetActiveSize()) _writeQueue.pop(); if (!_writeQueue.empty()) - AsyncProcessQueue(deleteGuard); + AsyncProcessQueue(); else if (_closing) CloseSocket(); } @@ -210,48 +191,15 @@ private: void WriteHandlerWrapper(boost::system::error_code /*error*/, std::size_t /*transferedBytes*/) { - std::unique_lock<std::mutex> guard(_writeLock); _isWritingAsync = false; - WriteHandler(guard); + HandleQueue(); } - bool WriteHandler(std::unique_lock<std::mutex>& guard) + bool HandleQueue() { if (!IsOpen()) return false; - std::size_t bytesToSend = _writeBuffer.GetActiveSize(); - - if (bytesToSend == 0) - return HandleQueue(guard); - - boost::system::error_code error; - std::size_t bytesWritten = _socket.write_some(boost::asio::buffer(_writeBuffer.GetReadPointer(), bytesToSend), error); - - if (error) - { - if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) - return AsyncProcessQueue(guard); - - return false; - } - else if (bytesWritten == 0) - return false; - else if (bytesWritten < bytesToSend) - { - _writeBuffer.ReadCompleted(bytesWritten); - _writeBuffer.Normalize(); - return AsyncProcessQueue(guard); - } - - // now bytesWritten == bytesToSend - _writeBuffer.Reset(); - - return HandleQueue(guard); - } - - bool HandleQueue(std::unique_lock<std::mutex>& guard) - { if (_writeQueue.empty()) return false; @@ -265,7 +213,7 @@ private: if (error) { if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) - return AsyncProcessQueue(guard); + return AsyncProcessQueue(); _writeQueue.pop(); return false; @@ -278,7 +226,7 @@ private: else if (bytesSent < bytesToSend) // now n > 0 { queuedMessage.ReadCompleted(bytesSent); - return AsyncProcessQueue(guard); + return AsyncProcessQueue(); } _writeQueue.pop(); @@ -293,6 +241,7 @@ private: uint16 _remotePort; MessageBuffer _readBuffer; + std::queue<MessageBuffer> _writeQueue; std::atomic<bool> _closed; std::atomic<bool> _closing; diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h index 4037c85baa1..b14aac4ca47 100644 --- a/src/server/shared/Networking/SocketMgr.h +++ b/src/server/shared/Networking/SocketMgr.h @@ -90,20 +90,14 @@ public: _threads[i].Wait(); } - virtual void OnSocketOpen(tcp::socket&& sock) + virtual void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) { - size_t min = 0; - - for (int32 i = 1; i < _threadCount; ++i) - if (_threads[i].GetConnectionCount() < _threads[min].GetConnectionCount()) - min = i; - try { std::shared_ptr<SocketType> newSocket = std::make_shared<SocketType>(std::move(sock)); newSocket->Start(); - _threads[min].AddSocket(newSocket); + _threads[threadIndex].AddSocket(newSocket); } catch (boost::system::system_error const& err) { @@ -113,6 +107,23 @@ public: int32 GetNetworkThreadCount() const { return _threadCount; } + uint32 SelectThreadWithMinConnections() const + { + uint32 min = 0; + + for (int32 i = 1; i < _threadCount; ++i) + if (_threads[i].GetConnectionCount() < _threads[min].GetConnectionCount()) + min = i; + + return min; + } + + std::pair<tcp::socket*, uint32> GetSocketForAccept() + { + uint32 threadIndex = SelectThreadWithMinConnections(); + return std::make_pair(_threads[threadIndex].GetSocketForAccept(), threadIndex); + } + protected: SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(1) { diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h index 1f9fcacb772..b8c6ee1c36e 100644 --- a/src/server/shared/Packets/ByteBuffer.h +++ b/src/server/shared/Packets/ByteBuffer.h @@ -33,7 +33,6 @@ #include <time.h> #include <cmath> #include <type_traits> -#include <boost/asio/buffer.hpp> class MessageBuffer; diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index 8de3f95f68a..22a8cc72c3c 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -75,12 +75,10 @@ typedef struct } map_id; map_id *map_ids; -uint16 *areas; uint16 *LiqType; #define MAX_PATH_LENGTH 128 char output_path[MAX_PATH_LENGTH]; char input_path[MAX_PATH_LENGTH]; -uint32 maxAreaId = 0; // ************************************************** // Extractor options @@ -318,35 +316,6 @@ uint32 ReadMapDBC() return map_count; } -void ReadAreaTableDBC() -{ - printf("Read AreaTable.dbc file..."); - HANDLE dbcFile; - if (!CascOpenFile(CascStorage, "DBFilesClient\\AreaTable.dbc", CASC_LOCALE_NONE, 0, &dbcFile)) - { - printf("Fatal error: Cannot find AreaTable.dbc in archive! %s\n", HumanReadableCASCError(GetLastError())); - exit(1); - } - - DBCFile dbc(dbcFile); - if(!dbc.open()) - { - printf("Fatal error: Invalid AreaTable.dbc file format!\n"); - exit(1); - } - - size_t area_count = dbc.getRecordCount(); - maxAreaId = dbc.getMaxId(); - areas = new uint16[maxAreaId + 1]; - memset(areas, 0xFF, sizeof(uint16) * (maxAreaId + 1)); - - for (uint32 x = 0; x < area_count; ++x) - areas[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3); - - CascCloseFile(dbcFile); - printf("Done! (" SZFMTD " areas loaded)\n", area_count); -} - void ReadLiquidTypeTableDBC() { printf("Read LiquidType.dbc file..."); @@ -382,7 +351,7 @@ void ReadLiquidTypeTableDBC() // Map file format data static char const* MAP_MAGIC = "MAPS"; -static char const* MAP_VERSION_MAGIC = "v1.5"; +static char const* MAP_VERSION_MAGIC = "v1.8"; static char const* MAP_AREA_MAGIC = "AREA"; static char const* MAP_HEIGHT_MAGIC = "MHGT"; static char const* MAP_LIQUID_MAGIC = "MLIQ"; @@ -411,9 +380,10 @@ struct map_areaHeader uint16 gridArea; }; -#define MAP_HEIGHT_NO_HEIGHT 0x0001 -#define MAP_HEIGHT_AS_INT16 0x0002 -#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_HAS_FLIGHT_BOUNDS 0x0008 struct map_heightHeader { @@ -458,7 +428,7 @@ float selectUInt16StepStore(float maxDiff) return 65535 / maxDiff; } // Temporary grid data store -uint16 area_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; +uint16 area_ids[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]; float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]; @@ -473,14 +443,17 @@ bool liquid_show[ADT_GRID_SIZE][ADT_GRID_SIZE]; float liquid_height[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]; uint8 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID][8]; -bool TransformToHighRes(uint16 holes, uint8 hiResHoles[8]) +int16 flight_box_max[3][3]; +int16 flight_box_min[3][3]; + +bool TransformToHighRes(uint16 lowResHoles, uint8 hiResHoles[8]) { for (uint8 i = 0; i < 8; i++) { for (uint8 j = 0; j < 8; j++) { int32 holeIdxL = (i / 2) * 4 + (j / 2); - if (((holes >> holeIdxL) & 1) == 1) + if (((lowResHoles >> holeIdxL) & 1) == 1) hiResHoles[i] |= (1 << j); } } @@ -502,7 +475,7 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int map.buildMagic = build; // Get area flags data - memset(area_flags, 0xFF, sizeof(area_flags)); + memset(area_ids, 0, sizeof(area_ids)); memset(V9, 0, sizeof(V9)); memset(V8, 0, sizeof(V8)); @@ -513,14 +486,14 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int memset(holes, 0, sizeof(holes)); bool hasHoles = false; + bool hasFlightBox = false; for (std::multimap<std::string, FileChunk*>::const_iterator itr = adt.chunks.lower_bound("MCNK"); itr != adt.chunks.upper_bound("MCNK"); ++itr) { adt_MCNK* mcnk = itr->second->As<adt_MCNK>(); // Area data - if (mcnk->areaid <= maxAreaId && areas[mcnk->areaid] != 0xFFFF) - area_flags[mcnk->iy][mcnk->ix] = areas[mcnk->areaid]; + area_ids[mcnk->iy][mcnk->ix] = mcnk->areaid; // Height // Height values for triangles stored in order: @@ -728,16 +701,24 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int } } + if (FileChunk* chunk = adt.GetChunk("MFBO")) + { + adt_MFBO* mfbo = chunk->As<adt_MFBO>(); + memcpy(flight_box_max, &mfbo->max, sizeof(flight_box_max)); + memcpy(flight_box_min, &mfbo->min, sizeof(flight_box_min)); + hasFlightBox = true; + } + //============================================ // Try pack area data //============================================ bool fullAreaData = false; - uint32 areaflag = area_flags[0][0]; - for (int y=0;y<ADT_CELLS_PER_GRID;y++) + uint32 areaId = area_ids[0][0]; + for (int y = 0; y < ADT_CELLS_PER_GRID; ++y) { - for(int x=0;x<ADT_CELLS_PER_GRID;x++) + for (int x = 0; x < ADT_CELLS_PER_GRID; ++x) { - if(area_flags[y][x]!=areaflag) + if (area_ids[y][x] != areaId) { fullAreaData = true; break; @@ -754,12 +735,12 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int if (fullAreaData) { areaHeader.gridArea = 0; - map.areaMapSize+=sizeof(area_flags); + map.areaMapSize += sizeof(area_ids); } else { areaHeader.flags |= MAP_AREA_NO_AREA; - areaHeader.gridArea = static_cast<uint16>(areaflag); + areaHeader.gridArea = static_cast<uint16>(areaId); } //============================================ @@ -819,6 +800,12 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int if (CONF_allow_float_to_int && (maxHeight - minHeight) < CONF_flat_height_delta_limit) heightHeader.flags |= MAP_HEIGHT_NO_HEIGHT; + if (hasFlightBox) + { + heightHeader.flags |= MAP_HEIGHT_HAS_FLIGHT_BOUNDS; + map.heightMapSize += sizeof(flight_box_max) + sizeof(flight_box_min); + } + // Try store as packed in uint16 or uint8 values if (!(heightHeader.flags & MAP_HEIGHT_NO_HEIGHT)) { @@ -966,8 +953,8 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int outFile.write(reinterpret_cast<const char*>(&map), sizeof(map)); // Store area data outFile.write(reinterpret_cast<const char*>(&areaHeader), sizeof(areaHeader)); - if (!(areaHeader.flags&MAP_AREA_NO_AREA)) - outFile.write(reinterpret_cast<const char*>(area_flags), sizeof(area_flags)); + if (!(areaHeader.flags & MAP_AREA_NO_AREA)) + outFile.write(reinterpret_cast<const char*>(area_ids), sizeof(area_ids)); // Store height data outFile.write(reinterpret_cast<const char*>(&heightHeader), sizeof(heightHeader)); @@ -990,6 +977,12 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int } } + if (heightHeader.flags & MAP_HEIGHT_HAS_FLIGHT_BOUNDS) + { + outFile.write(reinterpret_cast<char*>(flight_box_max), sizeof(flight_box_max)); + outFile.write(reinterpret_cast<char*>(flight_box_min), sizeof(flight_box_min)); + } + // Store liquid data if need if (map.liquidMapOffset) { @@ -1042,7 +1035,6 @@ void ExtractMaps(uint32 build) uint32 map_count = ReadMapDBC(); - ReadAreaTableDBC(); ReadLiquidTypeTableDBC(); std::string path = output_path; @@ -1098,7 +1090,6 @@ void ExtractMaps(uint32 build) } printf("\n"); - delete[] areas; delete[] map_ids; } diff --git a/src/tools/map_extractor/adt.h b/src/tools/map_extractor/adt.h index 0c3b3780c16..bb6980507b7 100644 --- a/src/tools/map_extractor/adt.h +++ b/src/tools/map_extractor/adt.h @@ -219,6 +219,22 @@ struct adt_MH2O }; +struct adt_MFBO +{ + union + { + uint32 fcc; + char fcc_txt[4]; + }; + uint32 size; + struct plane + { + int16 coords[9]; + }; + plane max; + plane min; +}; + #pragma pack(pop) #endif diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp index 39bbe5b1412..067683577f4 100644 --- a/src/tools/map_extractor/loadlib.cpp +++ b/src/tools/map_extractor/loadlib.cpp @@ -96,7 +96,8 @@ u_map_fcc InterestingChunks[] = { { 'K', 'N', 'C', 'M' } }, { { 'T', 'V', 'C', 'M' } }, { { 'O', 'M', 'W', 'M' } }, - { { 'Q', 'L', 'C', 'M' } } + { { 'Q', 'L', 'C', 'M' } }, + { { 'O', 'B', 'F', 'M' } } }; bool IsInterestingChunk(u_map_fcc const& fcc) diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp index 209c047a5fe..5dd6bed30b0 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.cpp +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -80,7 +80,7 @@ struct map_liquidHeader namespace MMAP { - char const* MAP_VERSION_MAGIC = "v1.5"; + char const* MAP_VERSION_MAGIC = "v1.8"; TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ } TerrainBuilder::~TerrainBuilder() { } |