aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/common
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-06-06 16:48:21 +0200
committerShauren <shauren.trinity@gmail.com>2019-06-08 17:09:24 +0200
commitfc330fd8ff0115804d9c4b53a1f810c00dd63de9 (patch)
treecfa10998fed66779834bf0b7a9b8b799d33d91d4 /dep/CascLib/src/common
parent82c7b6c5688495d90c4ee5995a4ff74039348296 (diff)
Dep/CascLib: Update to ladislav-zezula/CascLib@a1197edf0b3bd4d52c3f39be7fa7b44bb0b98012
Diffstat (limited to 'dep/CascLib/src/common')
-rw-r--r--dep/CascLib/src/common/Array.h208
-rw-r--r--dep/CascLib/src/common/Common.cpp709
-rw-r--r--dep/CascLib/src/common/Common.h334
-rw-r--r--dep/CascLib/src/common/Csv.cpp370
-rw-r--r--dep/CascLib/src/common/Csv.h105
-rw-r--r--dep/CascLib/src/common/Directory.cpp14
-rw-r--r--dep/CascLib/src/common/Directory.h2
-rw-r--r--dep/CascLib/src/common/DumpContext.cpp153
-rw-r--r--dep/CascLib/src/common/DumpContext.h38
-rw-r--r--dep/CascLib/src/common/DynamicArray.cpp101
-rw-r--r--dep/CascLib/src/common/DynamicArray.h37
-rw-r--r--dep/CascLib/src/common/FileStream.cpp95
-rw-r--r--dep/CascLib/src/common/FileStream.h8
-rw-r--r--dep/CascLib/src/common/FileTree.cpp684
-rw-r--r--dep/CascLib/src/common/FileTree.h116
-rw-r--r--dep/CascLib/src/common/ListFile.cpp293
-rw-r--r--dep/CascLib/src/common/ListFile.h33
-rw-r--r--dep/CascLib/src/common/Map.cpp287
-rw-r--r--dep/CascLib/src/common/Map.h360
-rw-r--r--dep/CascLib/src/common/RootHandler.cpp115
-rw-r--r--dep/CascLib/src/common/RootHandler.h149
21 files changed, 2885 insertions, 1326 deletions
diff --git a/dep/CascLib/src/common/Array.h b/dep/CascLib/src/common/Array.h
new file mode 100644
index 00000000000..1dc96b7be8e
--- /dev/null
+++ b/dep/CascLib/src/common/Array.h
@@ -0,0 +1,208 @@
+/*****************************************************************************/
+/* Array.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 */
+/* 10.08.18 1.00 Lad CLASS-ified, renamed to Array.h */
+/*****************************************************************************/
+
+#ifndef __CASC_ARRAY_H__
+#define __CASC_ARRAY_H__
+
+//-----------------------------------------------------------------------------
+// Structures
+
+class CASC_ARRAY
+{
+ public:
+
+ CASC_ARRAY()
+ {
+ m_pItemArray = NULL;
+ m_ItemCountMax = 0;
+ m_ItemCount = 0;
+ m_ItemSize = 0;
+ }
+
+ ~CASC_ARRAY()
+ {
+ Free();
+ }
+
+ // Creates an array with a custom element type
+ template<typename TYPE>
+ int Create(size_t ItemCountMax)
+ {
+ return Create(sizeof(TYPE), ItemCountMax);
+ }
+
+ // Creates an array with a custom element size
+ int Create(size_t ItemSize, size_t ItemCountMax)
+ {
+ // Create the array
+ if ((m_pItemArray = CASC_ALLOC(BYTE, ItemSize * ItemCountMax)) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ m_ItemCountMax = ItemCountMax;
+ m_ItemCount = 0;
+ m_ItemSize = ItemSize;
+ return ERROR_SUCCESS;
+ }
+
+ // Inserts one or more items; returns pointer to the first inserted item
+ void * Insert(size_t NewItemCount)
+ {
+ void * pNewItems;
+
+ // Try to enlarge the buffer, if needed
+ if (!EnlargeArray(m_ItemCount + NewItemCount))
+ return NULL;
+ pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize);
+
+ // Increment the size of the array
+ m_ItemCount += NewItemCount;
+
+ // Return pointer to the new item
+ return pNewItems;
+ }
+
+ // Inserts one or more items; returns pointer to the first inserted item
+ void * Insert(const void * NewItems, size_t NewItemCount)
+ {
+ void * pNewItem = Insert(NewItemCount);
+
+ // Copy the item(s) to the array, if any
+ if (pNewItem && NewItems)
+ memcpy(pNewItem, NewItems, (NewItemCount * m_ItemSize));
+ return pNewItem;
+ }
+
+ // Returns an item at a given index
+ void * ItemAt(size_t ItemIndex)
+ {
+ return (ItemIndex < m_ItemCount) ? (m_pItemArray + (ItemIndex * m_ItemSize)) : NULL;
+ }
+
+ void * LastItem()
+ {
+ return m_pItemArray + (m_ItemCount * m_ItemSize);
+ }
+
+ // Inserts an item at a given index. If there is not enough items in the array,
+ // the array will be enlarged. Should any gaps to be created, the function will zero them
+ void * InsertAt(size_t ItemIndex)
+ {
+ LPBYTE pbLastItem;
+ LPBYTE pbNewItem;
+
+ // Make sure we have array large enough
+ if(!EnlargeArray(ItemIndex + 1))
+ return NULL;
+
+ // Get the items range
+ pbLastItem = m_pItemArray + (m_ItemCount * m_ItemSize);
+ pbNewItem = m_pItemArray + (ItemIndex * m_ItemSize);
+ m_ItemCount = CASCLIB_MAX(m_ItemCount, ItemIndex+1);
+
+ // If we inserted an item past the current end, we need to clear the items in-between
+ if (pbNewItem > pbLastItem)
+ {
+ memset(pbLastItem, 0, (pbNewItem - pbLastItem));
+ m_ItemCount = ItemIndex + 1;
+ }
+
+ return pbNewItem;
+ }
+
+ // Returns index of an item
+ size_t IndexOf(const void * pItem)
+ {
+ LPBYTE pbItem = (LPBYTE)pItem;
+
+ assert((m_pItemArray <= pbItem) && (pbItem <= m_pItemArray + (m_ItemCount * m_ItemSize)));
+ assert(((pbItem - m_pItemArray) % m_ItemSize) == 0);
+
+ return ((pbItem - m_pItemArray) / m_ItemSize);
+ }
+
+ void * ItemArray()
+ {
+ return m_pItemArray;
+ }
+
+ size_t ItemCount()
+ {
+ return m_ItemCount;
+ }
+
+ size_t ItemCountMax()
+ {
+ return m_ItemCountMax;
+ }
+
+ size_t ItemSize()
+ {
+ return m_ItemSize;
+ }
+
+ bool IsInitialized()
+ {
+ return (m_pItemArray && m_ItemCountMax);
+ }
+
+ // Invalidates the entire array, but keeps memory allocated
+ void Reset()
+ {
+ memset(m_pItemArray, 0, m_ItemCountMax * m_ItemSize);
+ m_ItemCount = 0;
+ }
+
+ // Frees the array
+ void Free()
+ {
+ CASC_FREE(m_pItemArray);
+ m_ItemCountMax = m_ItemCount = m_ItemSize = 0;
+ }
+
+ protected:
+
+ bool EnlargeArray(size_t NewItemCount)
+ {
+ LPBYTE NewItemArray;
+ size_t ItemCountMax;
+
+ // We expect the array to be already allocated
+ assert(m_pItemArray != NULL);
+ assert(m_ItemCountMax != 0);
+
+ // Shall we enlarge the table?
+ if (NewItemCount > m_ItemCountMax)
+ {
+ // Calculate new table size
+ ItemCountMax = m_ItemCountMax;
+ while (ItemCountMax < NewItemCount)
+ ItemCountMax = ItemCountMax << 1;
+
+ // Allocate new table
+ NewItemArray = CASC_REALLOC(BYTE, m_pItemArray, (ItemCountMax * m_ItemSize));
+ if (NewItemArray == NULL)
+ return false;
+
+ // Set the new table size
+ m_ItemCountMax = ItemCountMax;
+ m_pItemArray = NewItemArray;
+ }
+
+ return true;
+ }
+
+ LPBYTE m_pItemArray; // Pointer to item array
+ size_t m_ItemCountMax; // Maximum item count
+ size_t m_ItemCount; // Current item count
+ size_t m_ItemSize; // Size of an item
+};
+
+#endif // __CASC_ARRAY__
diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp
index a0455b7c659..d545f52f84a 100644
--- a/dep/CascLib/src/common/Common.cpp
+++ b/dep/CascLib/src/common/Common.cpp
@@ -65,40 +65,230 @@ unsigned char IntToHexChar[] = "0123456789abcdef";
// GetLastError/SetLastError support for non-Windows platform
#ifndef PLATFORM_WINDOWS
-static int nLastError = ERROR_SUCCESS;
+static DWORD dwLastError = ERROR_SUCCESS;
-int GetLastError()
+DWORD GetLastError()
{
- return nLastError;
+ return dwLastError;
}
-void SetLastError(int nError)
+void SetLastError(DWORD dwErrCode)
{
- nLastError = nError;
+ dwLastError = dwErrCode;
}
#endif
//-----------------------------------------------------------------------------
-// String manipulation
+// Overloaded "new" and "delete" operators
-void CopyString(char * szTarget, const char * szSource, size_t cchLength)
+void * operator new(size_t size)
{
- memcpy(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ return CASC_ALLOC(BYTE, size);
}
-void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength)
+void * operator new[](size_t size)
{
- mbstowcs(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ return CASC_ALLOC(BYTE, size);
}
-void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength)
+void operator delete(void * ptr)
{
- wcstombs(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ CASC_FREE(ptr);
}
+void operator delete[](void * ptr)
+{
+ CASC_FREE(ptr);
+}
+
+// For some reason, VS2015 needs this
+void operator delete(void * ptr, size_t)
+{
+ CASC_FREE(ptr);
+}
+
+//-----------------------------------------------------------------------------
+// Linear data stream manipulation
+
+LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(DWORD)) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrValue[0] = *(PDWORD)pbDataPtr;
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(DWORD);
+}
+
+LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(DWORD)) > pbDataEnd)
+ return NULL;
+
+ // Convert data from Little endian to
+ PtrValue[0] = ConvertBytesToInteger_4(pbDataPtr);
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(DWORD);
+}
+
+LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput)
+{
+ // Is there enough data?
+ if((pbDataPtr + nLength) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ memcpy(pbOutput, pbDataPtr, nLength);
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + nLength;
+}
+
+LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(CONTENT_KEY)) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrCKey[0] = (PCONTENT_KEY)pbDataPtr;
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(CONTENT_KEY);
+}
+
+LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount)
+{
+ size_t ArraySize = ItemSize * ItemCount;
+
+ // Is there enough data?
+ if((pbDataPtr + ArraySize) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrArray[0] = pbDataPtr;
+
+ // Return the pointer to data following after the array
+ return pbDataPtr + ArraySize;
+}
+
+//-----------------------------------------------------------------------------
+// String copying and conversion
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = strlen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ // Copy the string
+ memcpy(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = wcslen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ wcstombs(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = strlen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ mbstowcs(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = wcslen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ memcpy(szTarget, szSource, cchToCopy * sizeof(wchar_t));
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Safe version of s(w)printf
+
+size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...)
+{
+ char * buffend;
+ va_list argList;
+
+ // Start the argument list
+ va_start(argList, format);
+
+#ifdef PLATFORM_WINDOWS
+ StringCchVPrintfExA(buffer, nCount, &buffend, NULL, 0, format, argList);
+#else
+ buffend = buffer + vsnprintf(buffer, nCount, format, argList);
+#endif
+
+ // End the argument list
+ va_end(argList);
+ return (buffend - buffer);
+}
+
+size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...)
+{
+ wchar_t * buffend;
+ va_list argList;
+
+ // Start the argument list
+ va_start(argList, format);
+
+#ifdef PLATFORM_WINDOWS
+ StringCchVPrintfExW(buffer, nCount, &buffend, NULL, 0, format, argList);
+#else
+ buffend = buffer + vswprintf(buffer, nCount, format, argList);
+#endif
+
+ // End the argument list
+ va_end(argList);
+ return (buffend - buffer);
+}
+
+//-----------------------------------------------------------------------------
+// String allocation
+
char * CascNewStr(const char * szString, size_t nCharsToReserve)
{
char * szNewString = NULL;
@@ -137,102 +327,144 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
return szNewString;
}
-TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd)
+template <typename XCHAR>
+TCHAR * AppendPathFragment(TCHAR * szBuffer, TCHAR * szBufferEnd, const XCHAR * szPath, char chSeparator, bool bFirstFragment = false)
{
- TCHAR * szNewString = NULL;
-
- // Only if the entry is valid
- if(szBegin != NULL && szEnd > szBegin)
+ // The "Path" must not be empty
+ if(szPath && szPath[0])
{
- // Allocate and copy the string
- szNewString = CASC_ALLOC(TCHAR, (szEnd - szBegin + 1));
- if(szNewString != NULL)
- CopyString(szNewString, szBegin, (szEnd - szBegin));
+ // Append the path separator after the first fragment
+ if(szBuffer < szBufferEnd && bFirstFragment == false)
+ {
+ if(szBuffer[-1] != chSeparator)
+ {
+ *szBuffer++ = chSeparator;
+ }
+ }
+
+ // Copy the sub path
+ while(szBuffer < szBufferEnd && szPath[0] != 0)
+ {
+ // If there is a path separator, we skip it (all of them) and put single separator there
+ if(szPath[0] == '\\' || szPath[0] == '/')
+ {
+ while(szPath[0] == '\\' || szPath[0] == '/')
+ szPath++;
+ *szBuffer++ = chSeparator;
+ }
+ else
+ {
+ *szBuffer++ = *szPath++;
+ }
+ }
+
+ // Append end of string
+ szBuffer[0] = 0;
}
- // Return the string
- return szNewString;
+ return szBuffer;
}
-TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
+TCHAR * GetLastPathPart(TCHAR * szWorkPath)
{
- TCHAR * szFullPath = NULL;
- TCHAR * szPathPtr;
- size_t nLength1 = 0;
- size_t nLength2 = 0;
+ size_t nLength = _tcslen(szWorkPath);
- // Calculate lengths of each part
- if(szDirectory != NULL)
- {
- // Get the length of the directory
- nLength1 = _tcslen(szDirectory);
+ // Go one character back
+ if(nLength > 0)
+ nLength--;
- // Cut all ending backslashes
- while(nLength1 > 0 && (szDirectory[nLength1 - 1] == _T('\\') || szDirectory[nLength1 - 1] == _T('/')))
- nLength1--;
- }
+ // Cut ending (back)slashes, if any
+ while(nLength > 0 && (szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/')))
+ nLength--;
- if(szSubDir != NULL)
+ // Cut the last path part
+ while(nLength > 0)
{
- // Cut all leading backslashes
- while(szSubDir[0] == _T(PATH_SEPARATOR))
- szSubDir++;
-
- // Get the length of the subdir
- nLength2 = _tcslen(szSubDir);
-
- // Cut all ending backslashes
- while(nLength2 > 0 && szSubDir[nLength2 - 1] == _T(PATH_SEPARATOR))
- nLength2--;
- }
-
- // Allocate space for the full path
- szFullPath = szPathPtr = CASC_ALLOC(TCHAR, nLength1 + nLength2 + 2);
- if(szFullPath != NULL)
- {
- // Copy the directory
- if(szDirectory != NULL && nLength1 != 0)
+ // End of path?
+ if(szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/'))
{
- memcpy(szPathPtr, szDirectory, (nLength1 * sizeof(TCHAR)));
- szPathPtr += nLength1;
+ return szWorkPath + nLength;
}
- // Copy the sub-directory
- if(szSubDir != NULL && nLength2 != 0)
- {
- // Append backslash to the previous one
- if(szPathPtr > szFullPath)
- *szPathPtr++ = _T(PATH_SEPARATOR);
+ // Go one character back
+ nLength--;
+ }
- // Copy the string
- memcpy(szPathPtr, szSubDir, (nLength2 * sizeof(TCHAR)));
- szPathPtr += nLength2;
- }
+ return NULL;
+}
- // Terminate the string
- szPathPtr[0] = 0;
- }
+bool CutLastPathPart(TCHAR * szWorkPath)
+{
+ // Get the last part of the path
+ szWorkPath = GetLastPathPart(szWorkPath);
+ if(szWorkPath == NULL)
+ return false;
- return szFullPath;
+ szWorkPath[0] = 0;
+ return true;
}
-TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength)
+TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
{
+ TCHAR * szFullPathEnd;
TCHAR * szFullPath = NULL;
- TCHAR * szSubDir;
+ TCHAR * szPathPtr;
+ size_t nLength1 = (szDirectory != NULL) ? _tcslen(szDirectory) : 0;
+ size_t nLength2 = (szSubDir != NULL) ? _tcslen(szSubDir) : 0;
- // Create the subdir string
- szSubDir = CASC_ALLOC(TCHAR, nLength + 1);
- if(szSubDir != NULL)
+ // Allocate the entire buffer
+ szFullPath = szPathPtr = CASC_ALLOC(TCHAR, nLength1 + nLength2 + 2);
+ szFullPathEnd = szFullPath + nLength1 + nLength2 + 1;
+ if(szFullPath != NULL)
{
- CopyString(szSubDir, szString, nLength);
- szFullPath = CombinePath(szPath, szSubDir);
- CASC_FREE(szSubDir);
+ szPathPtr = AppendPathFragment(szPathPtr, szFullPathEnd, szDirectory, PATH_SEP_CHAR, true);
+ szPathPtr = AppendPathFragment(szPathPtr, szFullPathEnd, szSubDir, PATH_SEP_CHAR);
}
return szFullPath;
}
+size_t CombineFilePath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szPath, const TCHAR * szSubPath1, const TCHAR * szSubPath2)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+
+ // Append all three parts and return length
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szPath, PATH_SEP_CHAR, true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath1, PATH_SEP_CHAR);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath2, PATH_SEP_CHAR);
+ return (szBuffer - szSaveBuffer);
+}
+
+size_t CombineUrlPath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szHost, const TCHAR * szSubPath1, const TCHAR * szSubPath2)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+
+ // Append all three parts and return length
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHost, '/', true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath1, '/');
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath2, '/');
+ return (szBuffer - szSaveBuffer);
+}
+
+size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+ char szHashSubPath[0x80];
+ char szHashText[MD5_STRING_SIZE+1];
+
+ // Prepare the subpath
+ StringFromBinary(pbEKey, MD5_HASH_SIZE, szHashText);
+ CascStrPrintf(szHashSubPath, _countof(szHashSubPath), "%02x/%02x/%s", pbEKey[0], pbEKey[1], szHashText);
+
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubDir, '/', true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHashSubPath, '/');
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szExtension, '/', true);
+ return (szBuffer - szSaveBuffer);
+}
+
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
{
char * szNormNameEnd = szNormName + cchMaxChars;
@@ -257,19 +489,26 @@ size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName,
return NormalizeFileName(AsciiToLowerTable_Slash, szNormName, szFileName, cchMaxChars);
}
-ULONGLONG CalcFileNameHash(const char * szFileName)
+ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength)
{
- char szNormName[MAX_PATH+1];
uint32_t dwHashHigh = 0;
uint32_t dwHashLow = 0;
+
+ // Calculate the HASH value of the normalized file name
+ hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow);
+ return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
+}
+
+ULONGLONG CalcFileNameHash(const char * szFileName)
+{
+ char szNormName[MAX_PATH+1];
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;
+ // Calculate hash from the normalized name
+ return CalcNormNameHash(szNormName, nLength);
}
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue)
@@ -373,13 +612,16 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
{
char * szSaveBuffer = szBuffer;
- // Convert the string to the array of MD5
- // Copy the blob data as text
- for(size_t i = 0; i < cbBinary; i++)
+ // Verify the binary pointer
+ if(pbBinary && cbBinary)
{
- *szBuffer++ = IntToHexChar[pbBinary[0] >> 0x04];
- *szBuffer++ = IntToHexChar[pbBinary[0] & 0x0F];
- pbBinary++;
+ // Convert the string to the array of MD5
+ // Copy the blob data as text
+ for(size_t i = 0; i < cbBinary; i++)
+ {
+ *szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
+ *szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
+ }
}
// Terminate the string
@@ -395,39 +637,51 @@ char * StringFromMD5(LPBYTE md5, char * szBuffer)
//-----------------------------------------------------------------------------
// File name utilities
-const wchar_t * GetPlainFileName(const wchar_t * szFileName)
+bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId)
{
- const wchar_t * szPlainName = szFileName;
+ BYTE BinaryValue[4];
- while(*szFileName != 0)
+ // 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')
{
- if(*szFileName == '\\' || *szFileName == '/')
- szPlainName = szFileName + 1;
- szFileName++;
+ // Then, 8 hexadecimal digits must follow
+ if(ConvertStringToBinary(szFileName + 4, 8, BinaryValue) == ERROR_SUCCESS)
+ {
+ // Must be followed by an extension or end-of-string
+ if(szFileName[0x0C] == 0 || szFileName[0x0C] == '.')
+ {
+ FileDataId = ConvertBytesToInteger_4(BinaryValue);
+ return (FileDataId != CASC_INVALID_ID);
+ }
+ }
}
- return szPlainName;
+ return false;
}
-const char * GetPlainFileName(const char * szFileName)
+bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer)
{
- const char * szPlainName = szFileName;
+ size_t nLength = strlen(szFileName);
- while(*szFileName != 0)
+ if(nLength == MD5_STRING_SIZE)
{
- if(*szFileName == '\\' || *szFileName == '/')
- szPlainName = szFileName + 1;
- szFileName++;
+ if(ConvertStringToBinary(szFileName, MD5_STRING_SIZE, PtrKeyBuffer) == ERROR_SUCCESS)
+ {
+ return true;
+ }
}
- return szPlainName;
+ return false;
}
-bool CheckWildCard(const char * szString, const char * szWildCard)
+bool CascCheckWildCard(const char * szString, const char * szWildCard)
{
const char * szWildCardPtr;
- for(;;)
+ while(szWildCard && szWildCard[0])
{
// If there is '?' in the wildcard, we skip one char
while(szWildCard[0] == '?')
@@ -455,7 +709,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] == AsciiToUpperTable_BkSlash[szString[0]])
{
- if(CheckWildCard(szString, szWildCardPtr))
+ if(CascCheckWildCard(szString, szWildCardPtr))
return true;
}
}
@@ -476,244 +730,43 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
return (szString[0] == 0) ? true : false;
}
}
+ return true;
}
//-----------------------------------------------------------------------------
// Hashing functions
-bool IsValidMD5(LPBYTE pbMd5)
+bool CascIsValidMD5(LPBYTE pbMd5)
{
- BYTE BitSummary = 0;
+ PDWORD Int32Array = (PDWORD)pbMd5;
- // The MD5 is considered invalid of it is zeroed
- BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07];
- BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F];
- return (BitSummary != 0);
+ // The MD5 is considered invalid if it is zeroed
+ return (Int32Array[0] | Int32Array[1] | Int32Array[2] | Int32Array[3]) ? true : false;
}
-bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)
+bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)
{
- hash_state md5_state;
+ MD5_CTX md5_ctx;
BYTE md5_digest[MD5_HASH_SIZE];
// Don't verify the block if the MD5 is not valid.
- if(!IsValidMD5(expected_md5))
+ if(!CascIsValidMD5(expected_md5))
return true;
// Calculate the MD5 of the data block
- md5_init(&md5_state);
- md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock);
- md5_done(&md5_state, md5_digest);
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock);
+ MD5_Final(md5_digest, &md5_ctx);
// Does the MD5's match?
return (memcmp(md5_digest, expected_md5, MD5_HASH_SIZE) == 0);
}
-void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash)
-{
- hash_state md5_state;
-
- md5_init(&md5_state);
- md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock);
- md5_done(&md5_state, md5_hash);
-}
-
-//-----------------------------------------------------------------------------
-// We have our own qsort implementation, optimized for using array of pointers
-
-#define STKSIZ (8*sizeof(void*) - 2)
-
-#define SWAP_ENTRIES(index1, index2) \
-{ \
- temp = base[index1]; \
- base[index1] = base[index2]; \
- base[index2] = temp; \
-}
-
-void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context)
+void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash)
{
- size_t lo, hi; /* ends of sub-array currently sorting */
- size_t mid; /* points to middle of subarray */
- size_t loguy, higuy; /* traveling pointers for partition step */
- size_t size; /* size of the sub-array */
- size_t lostk[STKSIZ], histk[STKSIZ];
- void * temp;
- int stkptr; /* stack for saving sub-array to be processed */
-
- /* validation section */
- assert(base != NULL);
- assert(compare != NULL);
-
- if (num < 2)
- return; /* nothing to do */
-
- stkptr = 0; /* initialize stack */
-
- lo = 0;
- hi = (num-1); /* initialize limits */
-
- /* this entry point is for pseudo-recursion calling: setting
- lo and hi and jumping to here is like recursion, but stkptr is
- preserved, locals aren't, so we preserve stuff on the stack */
-recurse:
-
- size = (hi - lo) + 1; /* number of el's to sort */
-
- /* First we pick a partitioning element. The efficiency of the
- algorithm demands that we find one that is approximately the median
- of the values, but also that we select one fast. We choose the
- median of the first, middle, and last elements, to avoid bad
- performance in the face of already sorted data, or data that is made
- up of multiple sorted runs appended together. Testing shows that a
- median-of-three algorithm provides better performance than simply
- picking the middle element for the latter case. */
-
- mid = lo + (size / 2); /* find middle element */
-
- /* Sort the first, middle, last elements into order */
- if (compare(context, base[lo], base[mid]) > 0) {
- SWAP_ENTRIES(lo, mid);
- }
- if (compare(context, base[lo], base[hi]) > 0) {
- SWAP_ENTRIES(lo, hi);
- }
- if (compare(context, base[mid], base[hi]) > 0) {
- SWAP_ENTRIES(mid, hi);
- }
-
- /* We now wish to partition the array into three pieces, one consisting
- of elements <= partition element, one of elements equal to the
- partition element, and one of elements > than it. This is done
- below; comments indicate conditions established at every step. */
-
- loguy = lo;
- higuy = hi;
-
- /* Note that higuy decreases and loguy increases on every iteration,
- so loop must terminate. */
- for (;;) {
- /* lo <= loguy < hi, lo < higuy <= hi,
- A[i] <= A[mid] for lo <= i <= loguy,
- A[i] > A[mid] for higuy <= i < hi,
- A[hi] >= A[mid] */
-
- /* The doubled loop is to avoid calling comp(mid,mid), since some
- existing comparison funcs don't work when passed the same
- value for both pointers. */
-
- if (mid > loguy) {
- do {
- loguy ++;
- } while (loguy < mid && compare(context, base[loguy], base[mid]) <= 0);
- }
- if (mid <= loguy) {
- do {
- loguy ++;
- } while (loguy <= hi && compare(context, base[loguy], base[mid]) <= 0);
- }
-
- /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
- either loguy > hi or A[loguy] > A[mid] */
-
- do {
- higuy --;
- } while (higuy > mid && compare(context, base[higuy], base[mid]) > 0);
-
- /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
- either higuy == lo or A[higuy] <= A[mid] */
-
- if (higuy < loguy)
- break;
-
- /* if loguy > hi or higuy == lo, then we would have exited, so
- A[loguy] > A[mid], A[higuy] <= A[mid],
- loguy <= hi, higuy > lo */
-
- SWAP_ENTRIES(loguy, higuy);
-
- /* If the partition element was moved, follow it. Only need
- to check for mid == higuy, since before the swap,
- A[loguy] > A[mid] implies loguy != mid. */
-
- if (mid == higuy)
- mid = loguy;
-
- /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
- of loop is re-established */
- }
+ MD5_CTX md5_ctx;
- /* A[i] <= A[mid] for lo <= i < loguy,
- A[i] > A[mid] for higuy < i < hi,
- A[hi] >= A[mid]
- higuy < loguy
- implying:
- higuy == loguy-1
- or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
-
- /* Find adjacent elements equal to the partition element. The
- doubled loop is to avoid calling comp(mid,mid), since some
- existing comparison funcs don't work when passed the same value
- for both pointers. */
-
- higuy ++;
- if (mid < higuy) {
- do {
- higuy --;
- } while (higuy > mid && compare(context, base[higuy], base[mid]) == 0);
- }
- if (mid >= higuy) {
- do {
- higuy --;
- } while (higuy > lo && compare(context, base[higuy], base[mid]) == 0);
- }
-
- /* OK, now we have the following:
- higuy < loguy
- lo <= higuy <= hi
- A[i] <= A[mid] for lo <= i <= higuy
- A[i] == A[mid] for higuy < i < loguy
- A[i] > A[mid] for loguy <= i < hi
- A[hi] >= A[mid] */
-
- /* We've finished the partition, now we want to sort the subarrays
- [lo, higuy] and [loguy, hi].
- We do the smaller one first to minimize stack usage.
- We only sort arrays of length 2 or more.*/
-
- if ( higuy - lo >= hi - loguy ) {
- if (lo < higuy) {
- lostk[stkptr] = lo;
- histk[stkptr] = higuy;
- ++stkptr;
- } /* save big recursion for later */
-
- if (loguy < hi) {
- lo = loguy;
- goto recurse; /* do small recursion */
- }
- }
- else {
- if (loguy < hi) {
- lostk[stkptr] = loguy;
- histk[stkptr] = hi;
- ++stkptr; /* save big recursion for later */
- }
-
- if (lo < higuy) {
- hi = higuy;
- goto recurse; /* do small recursion */
- }
- }
-
- /* We have sorted the array, except for any pending sorts on the stack.
- Check if there are any, and do them. */
-
- --stkptr;
- if (stkptr >= 0) {
- lo = lostk[stkptr];
- hi = histk[stkptr];
- goto recurse; /* pop subarray from stack */
- }
- else
- return; /* all subarrays done */
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock);
+ MD5_Final(md5_hash, &md5_ctx);
}
diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h
index 484276a3bd7..c7f189b91ac 100644
--- a/dep/CascLib/src/common/Common.h
+++ b/dep/CascLib/src/common/Common.h
@@ -21,6 +21,76 @@
#define ALIGN_TO_SIZE(x, a) (((x) + (a)-1) & ~((a)-1))
#endif
+// Prevent problems with CRT "min" and "max" functions,
+// as they are not defined on all platforms
+#define CASCLIB_MIN(a, b) ((a < b) ? a : b)
+#define CASCLIB_MAX(a, b) ((a > b) ? a : b)
+#define CASCLIB_UNUSED(p) ((void)(p))
+
+//-----------------------------------------------------------------------------
+// Common structures
+
+// Structure for static content key (CKey) and encoded key (EKey)
+// The CKey is a MD5 hash of the file data.
+// The EKey is (shortened) MD5 hash of the file header, which contains MD5 hashes of all the logical blocks of the file.
+typedef struct _CONTENT_KEY
+{
+ BYTE Value[MD5_HASH_SIZE]; // MD5 of the file
+
+} CONTENT_KEY, *PCONTENT_KEY, ENCODED_KEY, *PENCODED_KEY;
+
+// Helper structure for merging file paths
+typedef struct _PATH_BUFFER
+{
+ char * szBegin;
+ char * szPtr;
+ char * szEnd;
+} PATH_BUFFER, *PPATH_BUFFER;
+
+//-----------------------------------------------------------------------------
+// Basic structure used by all CascLib objects to describe a single entry
+// in the CASC storage. Each entry represents one physical file
+// in the storage. Note that the file may be present under several file names.
+
+// Flags for CASC_CKEY_ENTRY::Flags
+#define CASC_CE_FILE_IS_LOCAL 0x00000001 // The file is available locally. Keep this flag to have value of 1
+#define CASC_CE_HAS_CKEY 0x00000002 // The CKey is present in the entry
+#define CASC_CE_HAS_EKEY 0x00000004 // The EKey is present, at least partial one
+#define CASC_CE_HAS_EKEY_PARTIAL 0x00000008 // The EKey is only partial, padded by zeros. Always used with CASC_CE_HAS_EKEY
+#define CASC_CE_IN_ENCODING 0x00000010 // Present in the ENCODING manifest
+#define CASC_CE_IN_DOWNLOAD 0x00000020 // Present in the DOWNLOAD manifest
+#define CASC_CE_FOLDER_ENTRY 0x00000040 // This CKey entry is a folder
+
+// In-memory representation of a single entry.
+struct CASC_CKEY_ENTRY
+{
+ CASC_CKEY_ENTRY()
+ {
+ Init();
+ }
+
+ void Init(void)
+ {
+ memset(this, 0, sizeof(CASC_CKEY_ENTRY));
+ StorageOffset = CASC_INVALID_OFFS64;
+ EncodedSize = CASC_INVALID_SIZE;
+ ContentSize = CASC_INVALID_SIZE;
+ }
+
+ BYTE CKey[MD5_HASH_SIZE]; // Content key of the full length
+ BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the full length
+ ULONGLONG StorageOffset; // Linear offset over the entire storage. 0 if not present
+ ULONGLONG TagBitMask; // Bitmap for the tags. 0 ig tags are not supported
+ DWORD EncodedSize; // Encoded size of the file. 0 if not supported
+ DWORD ContentSize; // Content size of the file. 0 if not supported
+ DWORD Flags; // See CASC_CE_XXX
+ USHORT RefCount; // This is the number of file names referencing this entry
+ BYTE Priority; // Download priority
+ BYTE Alignment;
+
+};
+typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY;
+
//-----------------------------------------------------------------------------
// Conversion tables
@@ -29,23 +99,198 @@ extern unsigned char AsciiToUpperTable_BkSlash[256];
extern unsigned char IntToHexChar[];
//-----------------------------------------------------------------------------
-// String manipulation
+// Memory management
+//
+// We use our own macros for allocating/freeing memory. If you want
+// to redefine them, please keep the following rules:
+//
+// - The memory allocation must return NULL if not enough memory
+// (i.e not to throw exception)
+// - The allocating function does not need to fill the allocated buffer with zeros
+// - The reallocating function must support NULL as the previous block
+// - Memory freeing function must check for NULL pointer and do nothing if so
+//
+
+#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
+#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
+
+template <typename T>
+void CASC_FREE(T *& ptr)
+{
+ if (ptr != NULL)
+ free(ptr);
+ ptr = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Overloaded "new" and "delete" operators
+
+void * operator new(size_t size);
+void * operator new[](size_t size);
+void operator delete(void * ptr);
+void operator delete[](void * ptr);
+void operator delete(void * ptr, size_t); // For some reason, VS2015 needs this
+
+//-----------------------------------------------------------------------------
+// Big endian number manipulation
+
+inline DWORD ConvertBytesToInteger_2(LPBYTE ValueAsBytes)
+{
+ USHORT Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[3];
+
+ return Value;
+}
+
+// Converts the variable-size big-endian into integer
+inline DWORD ConvertBytesToInteger_X(LPBYTE ValueAsBytes, DWORD dwByteSize)
+{
+ DWORD Value = 0;
+
+ if(dwByteSize > 0)
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ if(dwByteSize > 1)
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ if(dwByteSize > 2)
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ if(dwByteSize > 3)
+ Value = (Value << 0x08) | ValueAsBytes[3];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[3];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[0];
+
+ return Value;
+}
+
+// Read the 40-bit big-endian offset into ULONGLONG
+inline ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes)
+{
+ ULONGLONG Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[3];
+ Value = (Value << 0x08) | ValueAsBytes[4];
+
+ return Value;
+}
+
+inline 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;
+}
+
+inline void ConvertIntegerToBytes_4_LE(DWORD Value, LPBYTE ValueAsBytes)
+{
+ ValueAsBytes[0] = (Value >> 0x00) & 0xFF;
+ ValueAsBytes[1] = (Value >> 0x08) & 0xFF;
+ ValueAsBytes[2] = (Value >> 0x10) & 0xFF;
+ ValueAsBytes[3] = (Value >> 0x18) & 0xFF;
+}
+
+// Faster than memset(Buffer, 0, 0x10)
+inline void ZeroMemory16(void * Buffer)
+{
+ PDWORD PtrBuffer = (PDWORD)Buffer;
+
+ PtrBuffer[0] = 0;
+ PtrBuffer[1] = 0;
+ PtrBuffer[2] = 0;
+ PtrBuffer[3] = 0;
+}
+
+// Faster than memcpy(Target, Source, 0x10)
+inline void CopyMemory16(void * Target, void * Source)
+{
+ PDWORD PtrTarget = (PDWORD)Target;
+ PDWORD PtrSource = (PDWORD)Source;
+
+ PtrTarget[0] = PtrSource[0];
+ PtrTarget[1] = PtrSource[1];
+ PtrTarget[2] = PtrSource[2];
+ PtrTarget[3] = PtrSource[3];
+}
+
+//-----------------------------------------------------------------------------
+// Linear data stream 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);
+LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
+LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
+LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput);
+LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey);
+LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount);
-char * CascNewStr(const char * szString, size_t nCharsToReserve);
-wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve);
+#define CaptureArray(pbDataPtr, pbDataEnd, PtrArray, type, count) CaptureArray_(pbDataPtr, pbDataEnd, PtrArray, sizeof(type), count)
-TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd);
+//-----------------------------------------------------------------------------
+// String copying and conversion
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1);
+void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1);
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1);
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1);
+
+//-----------------------------------------------------------------------------
+// Safe version of s(w)printf
+
+size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...);
+size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...);
+
+//-----------------------------------------------------------------------------
+// String allocation
+
+char * CascNewStr(const char * szString, size_t nCharsToReserve = 0);
+wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve = 0);
TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir);
-TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength);
+size_t CombineFilePath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szPath, const TCHAR * szSubPath1, const TCHAR * szSubPath2 = NULL);
+size_t CombineUrlPath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szHost, const TCHAR * szSubPath1, const TCHAR * szSubPath2 = NULL);
+TCHAR * GetLastPathPart(TCHAR * szWorkPath);
+bool CutLastPathPart(TCHAR * szWorkPath);
+size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey);
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
+ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength);
ULONGLONG CalcFileNameHash(const char * szFileName);
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
@@ -56,20 +301,81 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer);
char * StringFromMD5(LPBYTE md5, char * szBuffer);
//-----------------------------------------------------------------------------
+// Structure query key
+
+struct QUERY_KEY
+{
+ QUERY_KEY()
+ {
+ pbData = NULL;
+ cbData = 0;
+ }
+
+ ~QUERY_KEY()
+ {
+ CASC_FREE(pbData);
+ cbData = 0;
+ }
+
+ LPBYTE pbData;
+ size_t cbData;
+};
+typedef QUERY_KEY *PQUERY_KEY;
+
+//-----------------------------------------------------------------------------
// File name utilities
-bool CheckWildCard(const char * szString, const char * szWildCard);
-const wchar_t * GetPlainFileName(const wchar_t * szFileName);
-const char * GetPlainFileName(const char * szFileName);
+// Retrieves the pointer to plain name
+template <typename XCHAR>
+const XCHAR * GetPlainFileName(const XCHAR * szFileName)
+{
+ const XCHAR * szPlainName = szFileName;
+
+ while(*szFileName != 0)
+ {
+ if(*szFileName == '\\' || *szFileName == '/')
+ szPlainName = szFileName + 1;
+ szFileName++;
+ }
+
+ return szPlainName;
+}
+
+// Retrieves the pointer to file extension
+template <typename XCHAR>
+const XCHAR * GetFileExtension(const XCHAR * szFileName)
+{
+ const XCHAR * szExtension = NULL;
+
+ // We need to start searching from the plain name
+ // Avoid: C:\$RECYCLE.BIN\File.ext
+ szFileName = GetPlainFileName(szFileName);
+
+ // Find the last dot in the plain file name
+ while(szFileName[0] != 0)
+ {
+ if(szFileName[0] == '.')
+ szExtension = szFileName;
+ szFileName++;
+ }
+
+ // If not found, return the end of the file name
+ return (XCHAR *)((szExtension != NULL) ? szExtension : szFileName);
+}
+
+bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId);
+bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer);
+
+bool CascCheckWildCard(const char * szString, const char * szWildCard);
//-----------------------------------------------------------------------------
// Hashing functions
ULONGLONG HashStringJenkins(const char * szFileName);
-bool IsValidMD5(LPBYTE pbMd5);
-void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
-bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
+bool CascIsValidMD5(LPBYTE pbMd5);
+void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
+bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
//-----------------------------------------------------------------------------
// Scanning a directory
diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp
new file mode 100644
index 00000000000..24b654daa86
--- /dev/null
+++ b/dep/CascLib/src/common/Csv.cpp
@@ -0,0 +1,370 @@
+/*****************************************************************************/
+/* Csv.cpp Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Implementation for the CSV handler class */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+static const CASC_CSV_LINE NullLine;
+#define NullColumn NullLine.Columns[0];
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static char * NextLine(char * szLine)
+{
+ // Find the end of the line
+ while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D)
+ szLine++;
+
+ // Terminate the line
+ while(szLine[0] == 0x0A || szLine[0] == 0x0D)
+ *szLine++ = 0;
+
+ // If there's an end-of-string, it's over
+ return (szLine[0] != 0) ? szLine : NULL;
+}
+
+static char * NextColumn(char * szColumn)
+{
+ // Find the end of the column
+ while(szColumn[0] != 0 && szColumn[0] != '|')
+ szColumn++;
+
+ // Terminate the column
+ if (szColumn[0] == '|')
+ {
+ *szColumn++ = 0;
+ return szColumn;
+ }
+
+ // If there's an end-of-string, it's over
+ return NULL;
+}
+
+static size_t CalcHashValue(const char * szString)
+{
+ size_t dwHash = 0x7EEE7EEE;
+
+ // Hash the string itself
+ while(szString[0] != 0)
+ {
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[0];
+ szString++;
+ }
+
+ // Return the hash limited by the table size
+ return dwHash;
+}
+
+static BYTE HashToIndex(size_t dwHashValue)
+{
+ return (BYTE)(dwHashValue & (CSV_HASH_TABLE_SIZE - 1));
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV line
+
+CASC_CSV_LINE::CASC_CSV_LINE()
+{
+ m_pParent = NULL;
+ m_nColumns = 0;
+}
+
+CASC_CSV_LINE::~CASC_CSV_LINE()
+{}
+
+bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine)
+{
+ char * szCsvColumn;
+ size_t nHdrColumns = 0;
+ size_t nColumns = 0;
+
+ // Reset the number of column to zero
+ m_pParent = pParent;
+ m_nColumns = 0;
+
+ // Parse each column
+ while(szCsvLine != NULL && nColumns < CSV_MAX_COLUMNS)
+ {
+ // Get current line and the next one
+ szCsvColumn = szCsvLine;
+ szCsvLine = NextColumn(szCsvLine);
+
+ // Save the current line
+ Columns[nColumns].szValue = szCsvColumn;
+ Columns[nColumns].nLength = strlen(szCsvColumn);
+ nColumns++;
+ }
+
+ // Columns overflow
+ if(nColumns >= CSV_MAX_COLUMNS)
+ {
+ assert(false);
+ return false;
+ }
+
+ // If the parent has header lines, then the number of columns must match
+ // In the case of mismatched column count, ignore the line
+ nHdrColumns = pParent->GetHeaderColumns();
+ if(nHdrColumns != 0 && nColumns != nHdrColumns)
+ return false;
+
+ // All OK
+ m_nColumns = nColumns;
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](const char * szColumnName) const
+{
+ size_t nIndex;
+
+ // The column must have a hash table
+ if(m_pParent != NULL)
+ {
+ nIndex = m_pParent->GetColumnIndex(szColumnName);
+ if(nIndex != CSV_INVALID_INDEX && nIndex < m_nColumns)
+ {
+ return Columns[nIndex];
+ }
+ }
+
+ return NullColumn;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](size_t nIndex) const
+{
+ return (nIndex < m_nColumns) ? Columns[nIndex] : NullColumn;
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV object
+
+CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader)
+{
+ // Initialize the class variables
+ memset(HashTable, 0xFF, sizeof(HashTable));
+ m_szCsvFile = NULL;
+ m_szCsvPtr = NULL;
+ m_nCsvFile = 0;
+ m_nLines = 0;
+ m_bHasHeader = bHasHeader;
+
+ // Nonzero number of lines means a finite CSV handler. The CSV will be loaded at once.
+ // Zero means that the user needs to call LoadNextLine() and then the line data
+ if(nLinesMax != 0)
+ {
+ m_pLines = new CASC_CSV_LINE[nLinesMax];
+ m_nLinesMax = nLinesMax;
+ m_bHasAllLines = true;
+ }
+ else
+ {
+ m_pLines = new CASC_CSV_LINE[1];
+ m_nLinesMax = 1;
+ m_bHasAllLines = false;
+ }
+}
+
+CASC_CSV::~CASC_CSV()
+{
+ if(m_pLines != NULL)
+ delete[] m_pLines;
+ if(m_szCsvFile != NULL)
+ delete [] m_szCsvFile;
+ m_szCsvFile = NULL;
+}
+
+int CASC_CSV::Load(const TCHAR * szFileName)
+{
+ DWORD cbFileData = 0;
+ int nError = ERROR_SUCCESS;
+
+ m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
+ if (m_szCsvFile != NULL)
+ {
+ // There is one extra byte reserved by LoadFileToMemory, so we can put zero there
+ m_szCsvFile[cbFileData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbFileData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+ else
+ {
+ nError = GetLastError();
+ }
+
+ return nError;
+}
+
+int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
+{
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ m_szCsvFile = new char[cbData + 1];
+ if (m_szCsvFile != NULL)
+ {
+ // Copy the entire data and terminate them with zero
+ memcpy(m_szCsvFile, pbData, cbData);
+ m_szCsvFile[cbData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+
+ return nError;
+}
+
+bool CASC_CSV::LoadNextLine()
+{
+ bool bResult = false;
+
+ // Only endless CSV handler can do this
+ if(m_bHasAllLines == false)
+ {
+ // A few checks
+ assert(m_pLines != NULL);
+ assert(m_nLinesMax == 1);
+
+ // Reset the current line and load it
+ bResult = LoadNextLine(m_pLines[0]);
+ m_nLines = (bResult) ? 1 : 0;
+ }
+
+ return bResult;
+}
+
+bool CASC_CSV::InitializeHashTable()
+{
+ // Create the hash table of HeaderText -> ColumnIndex
+ for(size_t i = 0; i < Header.GetColumnCount(); i++)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(Header[i].szValue));
+ size_t nHashIndex = nStartIndex;
+
+ // Go as long as there is not a free space
+ while(HashTable[nHashIndex] != 0xFF)
+ {
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+
+ // Set the slot
+ HashTable[nHashIndex] = (BYTE)i;
+ }
+
+ return true;
+}
+
+bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line)
+{
+ // Overwatch ROOT's header begins with "#"
+ if(m_szCsvPtr == m_szCsvFile && m_szCsvPtr[0] == '#')
+ m_szCsvPtr++;
+
+ // Parse the entire line
+ while(m_szCsvPtr != NULL && m_szCsvPtr[0] != 0)
+ {
+ char * szCsvLine = m_szCsvPtr;
+
+ // Get the next line
+ m_szCsvPtr = NextLine(m_szCsvPtr);
+
+ // Initialize the line
+ if (Line.SetLine(this, szCsvLine))
+ return true;
+ }
+
+ // End-of-file found
+ return false;
+}
+
+bool CASC_CSV::ParseCsvData()
+{
+ // Sanity checks
+ assert(m_nLines == 0);
+
+ // If we have header, then parse it and set the pointer to the next line
+ if(m_bHasHeader)
+ {
+ // Load the current line to the header
+ if (!LoadNextLine(Header))
+ return false;
+
+ // Initialize the hash table
+ if(!InitializeHashTable())
+ return false;
+ }
+
+ // Are we supposed to load all lines?
+ if(m_bHasAllLines)
+ {
+ // Parse each line
+ for(size_t i = 0; i < m_nLinesMax; i++)
+ {
+ if(!LoadNextLine(m_pLines[i]))
+ break;
+ m_nLines++;
+ }
+ }
+
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV::operator[](const char * szColumnName) const
+{
+ if (m_pLines == NULL || m_nLines == 0)
+ return NullColumn;
+ return m_pLines[0][GetColumnIndex(szColumnName)];
+}
+
+const CASC_CSV_LINE & CASC_CSV::operator[](size_t nIndex) const
+{
+ if (m_pLines == NULL || nIndex >= m_nLines)
+ return NullLine;
+ return m_pLines[nIndex];
+}
+
+size_t CASC_CSV::GetHeaderColumns() const
+{
+ return (m_bHasHeader) ? Header.GetColumnCount() : 0;
+}
+
+size_t CASC_CSV::GetColumnIndex(const char * szColumnName) const
+{
+ if(m_bHasHeader)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(szColumnName));
+ size_t nHashIndex = nStartIndex;
+ size_t nIndex;
+
+ // Go as long as there is not a free space
+ while((nIndex = HashTable[nHashIndex]) != 0xFF)
+ {
+ // Compare the string
+ if(!strcmp(Header[nIndex].szValue, szColumnName))
+ return nIndex;
+
+ // Move to the next column
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+ }
+
+ return CSV_INVALID_INDEX;
+}
+
diff --git a/dep/CascLib/src/common/Csv.h b/dep/CascLib/src/common/Csv.h
new file mode 100644
index 00000000000..2138255e74e
--- /dev/null
+++ b/dep/CascLib/src/common/Csv.h
@@ -0,0 +1,105 @@
+/*****************************************************************************/
+/* Csv.h Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Interface for the CSV handler class */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#ifndef __CSV_H__
+#define __CSV_H__
+
+//-----------------------------------------------------------------------------
+// Defines
+
+#define CSV_INVALID_INDEX ((size_t)(-1))
+#define CSV_ZERO ((size_t)(0)) // Use Csv[0][CSV_ZERO] instead of ambiguous Csv[0][0]
+#define CSV_MAX_COLUMNS 0x10
+#define CSV_HASH_TABLE_SIZE 0x80
+
+//-----------------------------------------------------------------------------
+// Class for CSV line
+
+class CASC_CSV;
+
+struct CASC_CSV_COLUMN
+{
+ CASC_CSV_COLUMN()
+ {
+ szValue = NULL;
+ nLength = 0;
+ }
+
+ const char * szValue;
+ size_t nLength;
+};
+
+class CASC_CSV_LINE
+{
+ public:
+
+ CASC_CSV_LINE();
+ ~CASC_CSV_LINE();
+
+ bool SetLine(CASC_CSV * pParent, char * szLine);
+
+ const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;
+ const CASC_CSV_COLUMN & operator[](size_t nIndex) const;
+
+ size_t GetColumnCount() const
+ {
+ return m_nColumns;
+ }
+
+ protected:
+
+ friend class CASC_CSV;
+
+ CASC_CSV * m_pParent;
+ CASC_CSV_COLUMN Columns[CSV_MAX_COLUMNS];
+ size_t m_nColumns;
+};
+
+class CASC_CSV
+{
+ public:
+
+ CASC_CSV(size_t nLinesMax, bool bHasHeader);
+ ~CASC_CSV();
+
+ int Load(LPBYTE pbData, size_t cbData);
+ int Load(const TCHAR * szFileName);
+ bool LoadNextLine();
+
+ const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;
+ const CASC_CSV_LINE & operator[](size_t nIndex) const;
+
+ size_t GetHeaderColumns() const;
+ size_t GetColumnIndex(const char * szColumnName) const;
+
+ size_t GetLineCount() const
+ {
+ return m_nLines;
+ }
+
+ protected:
+
+ bool InitializeHashTable();
+ bool LoadNextLine(CASC_CSV_LINE & Line);
+ bool ParseCsvData();
+
+ CASC_CSV_LINE * m_pLines;
+ CASC_CSV_LINE Header;
+ BYTE HashTable[CSV_HASH_TABLE_SIZE];
+ char * m_szCsvFile;
+ char * m_szCsvPtr;
+ size_t m_nCsvFile;
+ size_t m_nLinesMax;
+ size_t m_nLines;
+ bool m_bHasHeader;
+ bool m_bHasAllLines;
+};
+
+#endif // __CSV_H__
diff --git a/dep/CascLib/src/common/Directory.cpp b/dep/CascLib/src/common/Directory.cpp
index c4091095859..463881f3f9c 100644
--- a/dep/CascLib/src/common/Directory.cpp
+++ b/dep/CascLib/src/common/Directory.cpp
@@ -38,6 +38,20 @@ bool DirectoryExists(const TCHAR * szDirectory)
return false;
}
+bool MakeDirectory(const TCHAR * szDirectory)
+{
+#ifdef PLATFORM_WINDOWS
+
+ BOOL bResult = CreateDirectory(szDirectory, NULL);
+ return (bResult) ? true : false;
+
+#else
+
+ return (mkdir(szDirectory, 0755) == 0);
+
+#endif
+}
+
int ScanIndexDirectory(
const TCHAR * szIndexPath,
INDEX_FILE_FOUND pfnOnFileFound,
diff --git a/dep/CascLib/src/common/Directory.h b/dep/CascLib/src/common/Directory.h
index ae97dca3d76..739f0612e54 100644
--- a/dep/CascLib/src/common/Directory.h
+++ b/dep/CascLib/src/common/Directory.h
@@ -18,6 +18,8 @@ typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PD
bool DirectoryExists(const TCHAR * szDirectory);
+bool MakeDirectory(const TCHAR * szDirectory);
+
int ScanIndexDirectory(
const TCHAR * szIndexPath,
INDEX_FILE_FOUND pfnOnFileFound,
diff --git a/dep/CascLib/src/common/DumpContext.cpp b/dep/CascLib/src/common/DumpContext.cpp
deleted file mode 100644
index 8783ff201ba..00000000000
--- a/dep/CascLib/src/common/DumpContext.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*****************************************************************************/
-/* 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
deleted file mode 100644
index 6f725f5b942..00000000000
--- a/dep/CascLib/src/common/DumpContext.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*****************************************************************************/
-/* 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
deleted file mode 100644
index e744fbe3912..00000000000
--- a/dep/CascLib/src/common/DynamicArray.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*****************************************************************************/
-/* 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
deleted file mode 100644
index 11cefacdcc4..00000000000
--- a/dep/CascLib/src/common/DynamicArray.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*****************************************************************************/
-/* 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 22d7fceb4c2..845e793a6fa 100644
--- a/dep/CascLib/src/common/FileStream.cpp
+++ b/dep/CascLib/src/common/FileStream.cpp
@@ -219,10 +219,24 @@ static bool BaseFile_Read(
#endif
// Increment the current file position by number of bytes read
- // If the number of bytes read doesn't match to required amount, return false
pStream->Base.File.FilePos = ByteOffset + dwBytesRead;
- if(dwBytesRead != dwBytesToRead)
- SetLastError(ERROR_HANDLE_EOF);
+
+ // If the number of bytes read doesn't match to required amount, return false
+ // However, Blizzard's CASC handlers read encoded data so that if less than expected
+ // was read, then they fill the rest with zeros
+ if(dwBytesRead < dwBytesToRead)
+ {
+ if(pStream->dwFlags & STREAM_FLAG_FILL_MISSING)
+ {
+ memset((LPBYTE)pvBuffer + dwBytesRead, 0, (dwBytesToRead - dwBytesRead));
+ dwBytesRead = dwBytesToRead;
+ }
+ else
+ {
+ SetLastError(ERROR_HANDLE_EOF);
+ }
+ }
+
return (dwBytesRead == dwBytesToRead);
}
@@ -597,22 +611,26 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
{
#ifdef PLATFORM_WINDOWS
+ INTERNET_PORT ServerPort = INTERNET_DEFAULT_HTTP_PORT;
HINTERNET hRequest;
DWORD dwTemp = 0;
bool bFileAvailable = false;
int nError = ERROR_SUCCESS;
- // Keep compiler happy
- dwStreamFlags = dwStreamFlags;
+ // Check alternate ports
+ if(dwStreamFlags & STREAM_FLAG_USE_PORT_1119)
+ {
+ ServerPort = 1119;
+ }
- // Don't connect to the internet
+ // Don't download if we are not connected to the internet
if(!InternetGetConnectedState(&dwTemp, 0))
nError = GetLastError();
// Initiate the connection to the internet
if(nError == ERROR_SUCCESS)
{
- pStream->Base.Http.hInternet = InternetOpen(_T("CascLib HTTP archive reader"),
+ pStream->Base.Http.hInternet = InternetOpen(_T("agent/2.17.2.6700"),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
@@ -631,7 +649,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);
pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,
szServerName,
- INTERNET_DEFAULT_HTTP_PORT,
+ ServerPort,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
@@ -651,26 +669,41 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
{
ULONGLONG FileTime = 0;
+ LPTSTR szEndPtr;
+ TCHAR szStatusCode[0x10];
+ DWORD dwStatusCode = 404;
DWORD dwFileSize = 0;
- DWORD dwDataSize;
+ DWORD dwDataSize = sizeof(szStatusCode);
DWORD dwIndex = 0;
- // Check if the archive has Last Modified field
- dwDataSize = sizeof(ULONGLONG);
- if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
- pStream->Base.Http.FileTime = FileTime;
+ // Check whether the file exists
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwDataSize, &dwIndex))
+ dwStatusCode = _tcstoul(szStatusCode, &szEndPtr, 10);
- // Verify if the server supports random access
- dwDataSize = sizeof(DWORD);
- if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
+ // Copare the statuc code
+ if (dwStatusCode == 200)
{
- if(dwFileSize != 0)
+ // Check if the archive has Last Modified field
+ dwDataSize = sizeof(ULONGLONG);
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
+ pStream->Base.Http.FileTime = FileTime;
+
+ // Verify if the server supports random access
+ dwDataSize = sizeof(DWORD);
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
{
- pStream->Base.Http.FileSize = dwFileSize;
- pStream->Base.Http.FilePos = 0;
- bFileAvailable = true;
+ if (dwFileSize != 0)
+ {
+ pStream->Base.Http.FileSize = dwFileSize;
+ pStream->Base.Http.FilePos = 0;
+ bFileAvailable = true;
+ }
}
}
+ else
+ {
+ nError = ERROR_FILE_NOT_FOUND;
+ }
}
InternetCloseHandle(hRequest);
}
@@ -681,6 +714,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
if(bFileAvailable == false)
{
pStream->BaseClose(pStream);
+ SetLastError(nError);
return false;
}
@@ -723,7 +757,7 @@ static bool BaseHttp_Read(
{
// Add range request to the HTTP headers
// http://www.clevercomponents.com/articles/article015/resuming.asp
- _stprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
+ CascStrPrintf(szRangeRequest, _countof(szRangeRequest), _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
// Send the request to the server
@@ -963,9 +997,7 @@ static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
static void BlockStream_Close(TBlockStream * pStream)
{
// Free the data map, if any
- if(pStream->FileBitmap != NULL)
- CASC_FREE(pStream->FileBitmap);
- pStream->FileBitmap = NULL;
+ CASC_FREE(pStream->FileBitmap);
// Call the base class for closing the stream
pStream->BaseClose(pStream);
@@ -1026,7 +1058,7 @@ static TFileStream * AllocateFileStream(
if(pStream != NULL)
{
// Zero the entire structure
- memset(pStream, 0, StreamSize);
+ memset(pStream, 0, StreamSize + FileNameSize + sizeof(TCHAR));
pStream->pMaster = pMaster;
pStream->dwFlags = dwStreamFlags;
@@ -1344,7 +1376,10 @@ static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFla
// Create new empty stream
pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
if(pStream == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
+ }
// Do we have a master stream?
if(pStream->pMaster != NULL)
@@ -1661,7 +1696,7 @@ static void PartStream_Close(TBlockStream * pStream)
// Make sure that the header is properly BSWAPed
BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER));
- sprintf(PartHeader.GameBuildNumber, "%u", (unsigned int)pStream->BuildNumber);
+ CascStrPrintf(PartHeader.GameBuildNumber, _countof(PartHeader.GameBuildNumber), "%u", (unsigned int)pStream->BuildNumber);
// Write the part header
pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER));
@@ -1755,7 +1790,6 @@ static bool PartStream_CreateMirror(TBlockStream * pStream)
return true;
}
-
static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
{
TBlockStream * pStream;
@@ -2193,9 +2227,7 @@ static void Block4Stream_Close(TBlockStream * pStream)
}
// Free the data map, if any
- if(pStream->FileBitmap != NULL)
- CASC_FREE(pStream->FileBitmap);
- pStream->FileBitmap = NULL;
+ CASC_FREE(pStream->FileBitmap);
// Do not call the BaseClose function,
// we closed all handles already
@@ -2246,7 +2278,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
for(int nSuffix = 0; nSuffix < 30; nSuffix++)
{
// Open the n-th file
- _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix);
+ CascStrPrintf(szNameBuff, (nNameLength + 4), _T("%s.%u"), pStream->szFileName, nSuffix);
if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags))
break;
@@ -2368,7 +2400,6 @@ TFileStream * FileStream_CreateFile(
// File create failed, delete the stream
CASC_FREE(pStream);
- pStream = NULL;
}
// Return the stream
diff --git a/dep/CascLib/src/common/FileStream.h b/dep/CascLib/src/common/FileStream.h
index 1c72619e393..a2b0c5b9f7d 100644
--- a/dep/CascLib/src/common/FileStream.h
+++ b/dep/CascLib/src/common/FileStream.h
@@ -28,6 +28,8 @@
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it
+#define STREAM_FLAG_FILL_MISSING 0x00000800 // If less than expected was read from the file, fill the missing part with zeros
+#define STREAM_FLAG_USE_PORT_1119 0x00001000 // For HTTP streams, use port 1119
#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
@@ -101,6 +103,12 @@ typedef void (*BLOCK_SAVEMAP)(
struct TFileStream * pStream // Pointer to a block-oriented stream
);
+typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(
+ void * pvUserData,
+ ULONGLONG ByteOffset,
+ DWORD dwTotalBytes
+ );
+
//-----------------------------------------------------------------------------
// Local structures - partial file structure and bitmap footer
diff --git a/dep/CascLib/src/common/FileTree.cpp b/dep/CascLib/src/common/FileTree.cpp
new file mode 100644
index 00000000000..f753c25f263
--- /dev/null
+++ b/dep/CascLib/src/common/FileTree.cpp
@@ -0,0 +1,684 @@
+/*****************************************************************************/
+/* FileTree.cpp Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* Common implementation of a file tree object for various ROOt file formats */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 29.05.18 1.00 Lad The first version of FileTree.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local defines
+
+#define START_ITEM_COUNT 0x4000
+
+inline DWORD GET_NODE_INT32(void * node, size_t offset)
+{
+ PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
+
+ return PtrValue[0];
+}
+
+inline void SET_NODE_INT32(void * node, size_t offset, DWORD value)
+{
+ PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
+
+ PtrValue[0] = value;
+}
+/*
+static bool CompareFileNode(void * pvObject, void * pvUserData)
+{
+ PCASC_COMPARE_CONTEXT pCtx = (PCASC_COMPARE_CONTEXT)pvUserData;
+ PCASC_FILE_TREE pFileTree = (PCASC_FILE_TREE)pCtx->pThis;
+ PCASC_FILE_NODE pFileNode = (PCASC_FILE_NODE)pvObject;
+ char szFullPath[MAX_PATH];
+
+ // First of all, the name hash must match
+ if(pFileNode->FileNameHash == pCtx->FileNameHash)
+ {
+ // Then also compare the full path name
+ pFileTree->PathAt(szFullPath, _countof(szFullPath), pFileNode);
+ if(!_stricmp(szFullPath, pCtx->szFileName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+*/
+//-----------------------------------------------------------------------------
+// Protected functions
+
+// Inserts a new file node to the file tree.
+// If the pointer to file node array changes, the function also rebuilds all maps
+PCASC_FILE_NODE CASC_FILE_TREE::InsertNew(PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Create a brand new node
+ pFileNode = InsertNew();
+ if(pFileNode != NULL)
+ {
+ // Initialize the file node's CKeyEntry
+ pFileNode->pCKeyEntry = pCKeyEntry;
+
+ // Don't insert the node into any of the arrays here.
+ // That is the caller's responsibility
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertNew()
+{
+ PCASC_FILE_NODE pFileNode;
+ void * SaveItemArray = NodeTable.ItemArray(); // We need to save the array pointers. If it changes, we must rebuild both maps
+
+ // Create a brand new node
+ pFileNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
+ if(pFileNode != NULL)
+ {
+ // Initialize the file node
+ pFileNode->FileNameHash = 0;
+ pFileNode->pCKeyEntry = NULL;
+ pFileNode->Parent = 0;
+ pFileNode->NameIndex = 0;
+ pFileNode->NameLength = 0;
+ pFileNode->Flags = 0;
+
+ // We need to supply a file data id for the new entry, otherwise the rebuilding function
+ // will use the uninitialized one
+ SetExtras(pFileNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
+
+ // If the array pointer changed or we are close to the size of the array, we need to rebuild the maps
+ if(NodeTable.ItemArray() != SaveItemArray || (NodeTable.ItemCount() * 3 / 2) > NameMap.HashTableSize())
+ {
+ // Rebuild both maps. Note that rebuilding also inserts all items to the maps, so no need to insert them here
+ if(!RebuildNameMaps())
+ {
+ pFileNode = NULL;
+ assert(false);
+ }
+ }
+ }
+
+ return pFileNode;
+}
+
+// Insert the node to the map of FileNameHash -> CASC_FILE_NODE
+bool CASC_FILE_TREE::InsertToHashTable(PCASC_FILE_NODE pFileNode)
+{
+ bool bResult = false;
+
+ // Insert the file node to the table
+ if(pFileNode->FileNameHash != 0)
+ bResult = NameMap.InsertObject(pFileNode, &pFileNode->FileNameHash);
+ return bResult;
+}
+
+// Inserts the file node to the array of file data ids
+bool CASC_FILE_TREE::InsertToIdTable(PCASC_FILE_NODE pFileNode)
+{
+ PCASC_FILE_NODE * RefElement;
+ DWORD FileDataId = CASC_INVALID_ID;
+
+ if(FileDataIds.IsInitialized())
+ {
+ // Retrieve the file data id
+ GetExtras(pFileNode, &FileDataId, NULL, NULL);
+ if(FileDataId != CASC_INVALID_ID)
+ {
+ // Sanity check
+ assert(FileDataId < 0x10000000);
+
+ // Insert the element to the array
+ RefElement = (PCASC_FILE_NODE *)FileDataIds.InsertAt(FileDataId);
+ if(RefElement != NULL)
+ {
+ RefElement[0] = pFileNode;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CASC_FILE_TREE::SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd)
+{
+ char * szNodeName;
+ size_t nLength = (szPlainNameEnd - szPlainName);
+
+ // Insert all chars to the name array
+ szNodeName = (char *)NameTable.Insert(nLength);
+ if(szNodeName != NULL)
+ {
+ // Copy the plain name to the node. Do not include the string terminator
+ memcpy(szNodeName, szPlainName, nLength);
+
+ // Supply the file name to the file node
+ pFileNode->NameIndex = (DWORD)NameTable.IndexOf(szNodeName);
+ pFileNode->NameLength = (USHORT)nLength;
+ return true;
+ }
+
+ return false;
+}
+
+bool CASC_FILE_TREE::SetKeyLength(DWORD aKeyLength)
+{
+ if(aKeyLength > MD5_HASH_SIZE)
+ return false;
+ KeyLength = aKeyLength;
+ return true;
+}
+
+DWORD CASC_FILE_TREE::GetNextFileDataId()
+{
+ if(FileDataIds.IsInitialized())
+ return (DWORD)(FileDataIds.ItemCount() + 1);
+ return CASC_INVALID_ID;
+}
+
+bool CASC_FILE_TREE::RebuildNameMaps()
+{
+ PCASC_FILE_NODE pFileNode;
+ size_t nMaxItems = NodeTable.ItemCountMax();
+
+ // Free the map of "FullName -> CASC_FILE_NODE"
+ NameMap.Free();
+
+ // Create new map map "FullName -> CASC_FILE_NODE"
+ if(NameMap.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_NODE, FileNameHash)) != ERROR_SUCCESS)
+ return false;
+
+ // Reset the entire array, but keep the buffer allocated
+ FileDataIds.Reset();
+
+ // Parse all items and insert them to the map
+ for(size_t i = 0; i < NodeTable.ItemCount(); i++)
+ {
+ // Retrieve the n-th object
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
+ if(pFileNode != NULL)
+ {
+ // Insert it to the map "FileNameHash -> CASC_FILE_NODE"
+ if(pFileNode->FileNameHash != 0)
+ InsertToHashTable(pFileNode);
+
+ // Insert it to the array "FileDataId -> CASC_FILE_NODE"
+ if(FileDataIds.IsInitialized())
+ InsertToIdTable(pFileNode);
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+int CASC_FILE_TREE::Create(DWORD Flags)
+{
+ PCASC_FILE_NODE pRootNode;
+ size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues);
+ int nError;
+
+ // Initialize the file tree
+ memset(this, 0, sizeof(CASC_FILE_TREE));
+ KeyLength = MD5_HASH_SIZE;
+
+ // Shall we use the data ID in the tree node?
+ if(Flags & FTREE_FLAG_USE_DATA_ID)
+ {
+ // Set the offset of the file data id in the entry
+ FileDataIdOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+
+ // Create the array for FileDataId -> CASC_FILE_NODE
+ nError = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+
+ // Shall we use the locale ID in the tree node?
+ if(Flags & FTREE_FLAG_USE_LOCALE_FLAGS)
+ {
+ LocaleFlagsOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+ }
+
+ if(Flags & FTREE_FLAG_USE_CONTENT_FLAGS)
+ {
+ ContentFlagsOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+ }
+
+ // Align the file node size to 8 bytes
+ FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8);
+
+ // Initialize the dynamic array
+ nError = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Create the dynamic array that will hold the node names
+ nError = NameTable.Create<char>(START_ITEM_COUNT);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Insert the first "root" node, without name
+ pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
+ if(pRootNode != NULL)
+ {
+ // Initialize the node
+ memset(pRootNode, 0, NodeTable.ItemSize());
+ pRootNode->Parent = CASC_INVALID_INDEX;
+ pRootNode->NameIndex = CASC_INVALID_INDEX;
+ pRootNode->Flags = CFN_FLAG_FOLDER;
+ SetExtras(pRootNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
+ }
+ }
+ }
+
+ // Create both maps
+ if(!RebuildNameMaps())
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ return nError;
+}
+
+void CASC_FILE_TREE::Free()
+{
+ // Free both arrays
+ NodeTable.Free();
+ NameTable.Free();
+ FileDataIds.Free();
+
+ // Free the name map
+ NameMap.Free();
+
+ // Zero the object
+ memset(this, 0, sizeof(CASC_FILE_TREE));
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ CASC_COMPARE_CONTEXT CmpCtx;
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(szFileName != NULL && szFileName[0] != 0);
+ assert(pCKeyEntry != NULL);
+
+ // Calculate the file name hash
+ CmpCtx.FileNameHash = CalcFileNameHash(szFileName);
+// CmpCtx.szFileName = szFileName;
+// CmpCtx.pThis = this;
+
+ // Do nothing if the file name is there already.
+// pFileNode = (PCASC_FILE_NODE)NameMap.FindObjectEx(CompareFileNode, &CmpCtx);
+ pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&CmpCtx.FileNameHash);
+ if(pFileNode == NULL)
+ {
+ // Insert new item
+ pFileNode = InsertNew(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ // Supply the name hash
+ pFileNode->FileNameHash = CmpCtx.FileNameHash;
+
+ // Set the file data id and the extra values
+ SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
+
+ // Insert the file node to the hash map
+ InsertToHashTable(pFileNode);
+
+ // Also make sure that it's in the file data id table, if the table is initialized
+ InsertToIdTable(pFileNode);
+
+ // Set the file name of the new file node. This also increments the number of references
+ SetNodeFileName(pFileNode, szFileName);
+
+ // If we created a new node, we need to increment the reference count
+ assert(pCKeyEntry->RefCount != 0xFFFF);
+ pCKeyEntry->RefCount++;
+ }
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(FileDataIds.IsInitialized());
+ assert(FileDataId != CASC_INVALID_ID);
+ assert(FileNameHash != 0);
+ assert(pCKeyEntry != NULL);
+
+ // Insert the node to the tree by file data id
+ pFileNode = InsertById(pCKeyEntry, FileDataId, LocaleFlags, ContentFlags);
+ if(pFileNode != NULL)
+ {
+ // Supply the name hash
+ pFileNode->FileNameHash = FileNameHash;
+
+ // Insert the file node to the hash map
+ InsertToHashTable(pFileNode);
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(FileDataIds.IsInitialized());
+ assert(FileDataId != CASC_INVALID_ID);
+ assert(pCKeyEntry != NULL);
+
+ // Check whether the file data id exists in the array of file data ids
+ if((pFileNode = FindById(FileDataId)) == NULL)
+ {
+ // Insert the new file node
+ pFileNode = InsertNew(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ // Set the file data id and the extra values
+ SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
+
+ // Insert the file node to the FileDataId array
+ InsertToIdTable(pFileNode);
+
+ // Increment the number of references
+ pCKeyEntry->RefCount++;
+ }
+ }
+
+ // Return the new or old node
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::ItemAt(size_t nItemIndex)
+{
+ return (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex)
+{
+ PCASC_FILE_NODE pFileNode = NULL;
+
+ // If we have FileDataId, then we need to enumerate the files by FileDataId
+ if(FileDataIds.IsInitialized())
+ pFileNode = *(PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex);
+ else
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
+
+ // Construct the entire path
+ PathAt(szBuffer, cchBuffer, pFileNode);
+ return pFileNode;
+}
+
+size_t CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode)
+{
+ PCASC_FILE_NODE pParentNode;
+ const char * szNamePtr;
+ char * szSaveBuffer = szBuffer;
+ char * szBufferEnd = szBuffer + cchBuffer - 1;
+
+ if(pFileNode != NULL && pFileNode->Parent != CASC_INVALID_INDEX)
+ {
+ // Copy all parents
+ pParentNode = (PCASC_FILE_NODE)NodeTable.ItemAt(pFileNode->Parent);
+ if(pParentNode != NULL)
+ {
+ // Query the parent and move the buffer
+ szBuffer = szBuffer + PathAt(szBuffer, cchBuffer, pParentNode);
+ }
+
+ // Retrieve the node name
+ szNamePtr = (const char *)NameTable.ItemAt(pFileNode->NameIndex);
+
+ // Check whether we have enough space
+ if((szBuffer + pFileNode->NameLength) < szBufferEnd)
+ {
+ // Copy the path part
+ memcpy(szBuffer, szNamePtr, pFileNode->NameLength);
+ szBuffer += pFileNode->NameLength;
+
+ // Append backslash
+ if((pFileNode->Flags & CFN_FLAG_FOLDER) && ((szBuffer + 1) < szBufferEnd))
+ {
+ *szBuffer++ = (pFileNode->Flags & CFN_FLAG_MOUNT_POINT) ? ':' : '\\';
+ }
+ }
+ }
+
+ // Terminate buffer with zero
+ szBuffer[0] = 0;
+
+ // Return length of the copied string
+ return (szBuffer - szSaveBuffer);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId, PCASC_FIND_DATA pFindData)
+{
+ PCASC_FILE_NODE pFileNode = NULL;
+ ULONGLONG FileNameHash;
+
+ // Can we search by FileDataId?
+ if(FileDataIds.IsInitialized() && (FileDataId != CASC_INVALID_ID || IsFileDataIdName(szFullPath, FileDataId)))
+ {
+ pFileNode = FindById(FileDataId);
+ }
+ else
+ {
+ if(szFullPath != NULL && szFullPath[0] != 0)
+ {
+ FileNameHash = CalcFileNameHash(szFullPath);
+ pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
+ }
+ }
+
+ // Did we find anything?
+ if(pFileNode != NULL && pFindData != NULL)
+ {
+ GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
+ pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
+ pFindData->bCanOpenByDataId = (FileDataIdOffset != 0);
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ for(size_t i = 0; i < NodeTable.ItemCount(); i++)
+ {
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
+ if((pFileNode->Flags & (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT)) == 0)
+ {
+ if(pFileNode->pCKeyEntry == pCKeyEntry)
+ return pFileNode;
+ }
+ }
+
+ return NULL;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(ULONGLONG FileNameHash)
+{
+ return (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::FindById(DWORD FileDataId)
+{
+ PCASC_FILE_NODE * RefElement;
+ PCASC_FILE_NODE pFileNode = NULL;
+
+ if(FileDataId != CASC_INVALID_ID && FileDataIds.IsInitialized())
+ {
+ // Insert the element to the array
+ RefElement = (PCASC_FILE_NODE *)FileDataIds.ItemAt(FileDataId);
+ if(RefElement != NULL)
+ {
+ pFileNode = RefElement[0];
+ }
+ }
+
+ return pFileNode;
+}
+
+bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName)
+{
+ ULONGLONG FileNameHash = 0;
+ PCASC_FILE_NODE pFolderNode = NULL;
+ const char * szNodeBegin = szFileName;
+ char szPathBuffer[MAX_PATH+1];
+ size_t nFileNode = NodeTable.IndexOf(pFileNode);
+ size_t i;
+ DWORD Parent = 0;
+
+ // Sanity checks
+ assert(szFileName != NULL && szFileName[0] != 0);
+ assert(pFileNode->pCKeyEntry != NULL);
+
+ // Traverse the entire path. For each subfolder, we insert an appropriate fake entry
+ for(i = 0; szFileName[i] != 0; i++)
+ {
+ char chOneChar = szFileName[i];
+
+ // Is there a path separator?
+ // Note: Warcraft III paths may contain "mount points".
+ // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j"
+ if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':')
+ {
+ // Calculate hash of the file name up to the end of the node name
+ FileNameHash = CalcNormNameHash(szPathBuffer, i);
+
+ // If the entry is not there yet, create new one
+ if((pFolderNode = Find(FileNameHash)) == NULL)
+ {
+ // Insert new entry to the tree
+ pFolderNode = InsertNew();
+ if(pFolderNode == NULL)
+ return false;
+
+ // Populate the file entry
+ pFolderNode->FileNameHash = FileNameHash;
+ pFolderNode->Parent = Parent;
+ pFolderNode->Flags |= (chOneChar == ':') ? CFN_FLAG_MOUNT_POINT : 0;
+ pFolderNode->Flags |= CFN_FLAG_FOLDER;
+
+ // Set the node sub name to the node
+ SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
+
+ // Insert the entry to the name map
+ InsertToHashTable(pFolderNode);
+ }
+
+ // Move the parent to the current node
+ Parent = (DWORD)NodeTable.IndexOf(pFolderNode);
+
+ // Move the begin of the node after the separator
+ szNodeBegin = szFileName + i + 1;
+ }
+
+ // Copy the next character, even if it was slash/backslash before
+ szPathBuffer[i] = AsciiToUpperTable_BkSlash[chOneChar];
+ }
+
+ // If anything left, this is gonna be our node name
+ if(szNodeBegin < szFileName + i)
+ {
+ // We need to reset the file node pointer, as the file node table might have changed
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
+
+ SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
+ pFileNode->Parent = Parent;
+ }
+ return true;
+}
+
+size_t CASC_FILE_TREE::GetMaxFileIndex()
+{
+ if(FileDataIds.IsInitialized())
+ {
+ return FileDataIds.ItemCount();
+ }
+ else
+ {
+ return NodeTable.ItemCount();
+ }
+}
+
+size_t CASC_FILE_TREE::GetCount()
+{
+ return NodeTable.ItemCount();
+}
+
+size_t CASC_FILE_TREE::IndexOf(PCASC_FILE_NODE pFileNode)
+{
+ return NodeTable.IndexOf(pFileNode);
+}
+
+void CASC_FILE_TREE::GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags)
+{
+ DWORD FileDataId = CASC_INVALID_ID;
+ DWORD LocaleFlags = CASC_INVALID_ID;
+ DWORD ContentFlags = CASC_INVALID_ID;
+
+ // Retrieve the data ID, if supported
+ if(PtrFileDataId != NULL)
+ {
+ if(FileDataIdOffset != 0)
+ FileDataId = GET_NODE_INT32(pFileNode, FileDataIdOffset);
+ PtrFileDataId[0] = FileDataId;
+ }
+
+ // Retrieve the locale ID, if supported
+ if(PtrLocaleFlags != NULL)
+ {
+ if(LocaleFlagsOffset != 0)
+ LocaleFlags = GET_NODE_INT32(pFileNode, LocaleFlagsOffset);
+ PtrLocaleFlags[0] = LocaleFlags;
+ }
+
+ if(PtrContentFlags != NULL)
+ {
+ if(ContentFlagsOffset != 0)
+ ContentFlags = GET_NODE_INT32(pFileNode, ContentFlagsOffset);
+ PtrContentFlags[0] = ContentFlags;
+ }
+}
+
+void CASC_FILE_TREE::SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ // Set the file data ID, if supported
+ if(FileDataIdOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, FileDataIdOffset, FileDataId);
+ }
+
+ // Set the locale ID, if supported
+ if(LocaleFlagsOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, LocaleFlagsOffset, LocaleFlags);
+ }
+
+ // Set the locale ID, if supported
+ if(ContentFlagsOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, ContentFlagsOffset, ContentFlags);
+ }
+}
diff --git a/dep/CascLib/src/common/FileTree.h b/dep/CascLib/src/common/FileTree.h
new file mode 100644
index 00000000000..904da6c4c46
--- /dev/null
+++ b/dep/CascLib/src/common/FileTree.h
@@ -0,0 +1,116 @@
+/*****************************************************************************/
+/* FileTree.h Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* Common implementation of a file tree object for various ROOt file formats */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 29.05.18 1.00 Lad The first version of FileTree.h */
+/*****************************************************************************/
+
+#ifndef __FILETREE_H__
+#define __FILETREE_H__
+
+//-----------------------------------------------------------------------------
+// Structures
+
+#define FTREE_FLAG_USE_DATA_ID 0x0001 // The FILE_NODE also contains file data ID
+#define FTREE_FLAG_USE_LOCALE_FLAGS 0x0002 // The FILE_NODE also contains file locale flags
+#define FTREE_FLAG_USE_CONTENT_FLAGS 0x0004 // The FILE_NODE also contains content flags
+
+#define CFN_FLAG_FOLDER 0x0001 // This item is a folder
+#define CFN_FLAG_MOUNT_POINT 0x0002 // This item is a mount point.
+
+// Common structure for holding a single folder/file node
+typedef struct _CASC_FILE_NODE
+{
+ ULONGLONG FileNameHash; // Jenkins hash of the normalized file name (uppercase, backslashes)
+ PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry.
+ DWORD Parent; // The index of a parent directory. If CASC_INVALID_INDEX, then this is the root item
+ DWORD NameIndex; // Index of the node name. If CASC_INVALID_INDEX, then this node has no name
+ USHORT NameLength; // Length of the node name (without the zero terminator)
+ USHORT Flags; // See CFE_FLAG_XXX
+
+ DWORD ExtraValues[4]; // FileDataId: Only if FTREE_FLAG_USE_DATA_ID specified at create
+ // LocaleFlags: Only if FTREE_FLAG_USE_LOCALE_FLAGS specified at create
+ // ContentFlags: Only if FTREE_FLAG_USE_CONTENT_FLAGS specified at create
+} CASC_FILE_NODE, *PCASC_FILE_NODE;
+
+// Common structure for comparing a file node
+typedef struct _CASC_COMPARE_CONTEXT
+{
+ ULONGLONG FileNameHash;
+ const char * szFileName;
+ void * pThis;
+} CASC_COMPARE_CONTEXT, *PCASC_COMPARE_CONTEXT;
+
+// Main structure for the file tree
+class CASC_FILE_TREE
+{
+ public:
+
+ // Initializes/destroys the entire tree
+ int Create(DWORD Flags = 0);
+ void Free();
+
+ // Inserts a new node to the tree; either with name or nameless
+ PCASC_FILE_NODE InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId = CASC_INVALID_ID, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+ PCASC_FILE_NODE InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+ PCASC_FILE_NODE InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+
+ // Returns an item at the given index. The PathAt also builds the full path of the node
+ PCASC_FILE_NODE ItemAt(size_t nItemIndex);
+ PCASC_FILE_NODE PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex);
+ size_t PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode);
+
+ // Finds a file using its full path, FileDataId or CKey/EKey
+ PCASC_FILE_NODE Find(const char * szFullPath, DWORD FileDataId, struct _CASC_FIND_DATA * pFindData);
+ PCASC_FILE_NODE Find(PCASC_CKEY_ENTRY pCKeyEntry);
+ PCASC_FILE_NODE Find(ULONGLONG FileNameHash);
+ PCASC_FILE_NODE FindById(DWORD FileDataId);
+
+ // Assigns a file name to the node
+ bool SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName);
+
+ // Returns the number of items in the tree
+ size_t GetMaxFileIndex();
+ size_t GetCount();
+
+ // Returns the index of an item in the tree
+ size_t IndexOf(PCASC_FILE_NODE pFileNode);
+
+ // Retrieves the extra values from the node (if supported)
+ void GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags);
+ void SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags);
+
+ // Change the length of the key
+ bool SetKeyLength(DWORD KeyLength);
+
+ // Retrieve the maximum FileDataId ever inserted
+ DWORD GetNextFileDataId();
+
+ protected:
+
+ PCASC_FILE_NODE InsertNew(PCASC_CKEY_ENTRY pCKeyEntry);
+ PCASC_FILE_NODE InsertNew();
+ bool InsertToHashTable(PCASC_FILE_NODE pFileNode);
+ bool InsertToIdTable(PCASC_FILE_NODE pFileNode);
+
+ bool SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd);
+ bool RebuildNameMaps();
+
+ CASC_ARRAY NodeTable; // Dynamic array that holds all CASC_FILE_NODEs
+ CASC_ARRAY NameTable; // Dynamic array that holds all node names
+
+ CASC_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE
+ CASC_MAP NameMap; // Map of FileNameHash -> CASC_FILE_NODE
+
+ size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE
+ size_t LocaleFlagsOffset; // If nonzero, this is the offset of the "LocaleFlags" field in the CASC_FILE_NODE
+ size_t ContentFlagsOffset; // If nonzero, this is the offset of the "ContentFlags" field in the CASC_FILE_NODE
+ DWORD KeyLength; // Actual length of the key supported by the root handler
+};
+
+typedef CASC_FILE_TREE * PCASC_FILE_TREE;
+
+#endif // __FILETREE_H__
diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp
index dc9a88bf44c..351ea226188 100644
--- a/dep/CascLib/src/common/ListFile.cpp
+++ b/dep/CascLib/src/common/ListFile.cpp
@@ -15,11 +15,14 @@
//-----------------------------------------------------------------------------
// Listfile cache structure
+#define LISTFILE_FLAG_USES_FILEDATAID 0x0001 // A new CSV format, containing FileDataId; FullFileName
+
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
+ char * pBegin; // The begin of the listfile cache
+ char * pPos; // Current position in the cache
+ char * pEnd; // The last character in the file cache
+ DWORD Flags;
// Followed by the cache (variable length)
@@ -28,7 +31,7 @@ typedef struct _LISTFILE_CACHE
//-----------------------------------------------------------------------------
// Creating the listfile cache for the given amount of data
-static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
+static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize)
{
PLISTFILE_CACHE pCache;
@@ -40,12 +43,81 @@ static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
pCache->pBegin =
pCache->pPos = (char *)(pCache + 1);
pCache->pEnd = pCache->pBegin + dwFileSize;
+ pCache->Flags = 0;
}
// Return the cache
return pCache;
}
+static char * ListFile_SkipSpaces(PLISTFILE_CACHE pCache)
+{
+ // Skip newlines, spaces, tabs and another non-printable stuff
+ while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
+ pCache->pPos++;
+
+ // Remember the begin of the line
+ return pCache->pPos;
+}
+
+static void ListFile_CheckFormat(PLISTFILE_CACHE pCache)
+{
+ // Only if the listfile is greatger than 2 MB
+ if((pCache->pEnd - pCache->pBegin) > 0x100000)
+ {
+ char * szPtr = pCache->pBegin;
+ size_t nDigitCount = 0;
+
+ // Calculate the amount of digits
+ while(nDigitCount <= 20 && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9')
+ nDigitCount++;
+
+ // There must be a semicolon after
+ if(nDigitCount <= 10 && szPtr[nDigitCount] == ';')
+ {
+ pCache->Flags |= LISTFILE_FLAG_USES_FILEDATAID;
+ }
+ }
+}
+
+static int ListFile_GetFileDataId(PLISTFILE_CACHE pCache, PDWORD PtrFileDataId)
+{
+ char * szLineBegin = ListFile_SkipSpaces(pCache);
+ char * szLineEnd;
+ DWORD dwNewInt32 = 0;
+ DWORD dwInt32 = 0;
+
+ // Set the limit for loading the number
+ szLineEnd = CASCLIB_MIN((szLineBegin + 20), pCache->pEnd);
+
+ // Extract decimal digits from the string
+ while(szLineBegin < szLineEnd && '0' <= szLineBegin[0] && szLineBegin[0] <= '9')
+ {
+ // Check integer overflow
+ dwNewInt32 = (dwInt32 * 10) + (szLineBegin[0] - '0');
+ if(dwNewInt32 < dwInt32)
+ return ERROR_BAD_FORMAT;
+
+ dwInt32 = dwNewInt32;
+ szLineBegin++;
+ }
+
+ // There must still be some space
+ if(szLineBegin < szLineEnd)
+ {
+ // There must be a semicolon after the decimal integer
+ // The decimal integer must be smaller than 10 MB (files)
+ if(szLineBegin[0] != ';' || dwInt32 >= 0xA00000)
+ return ERROR_BAD_FORMAT;
+
+ pCache->pPos = szLineBegin + 1;
+ PtrFileDataId[0] = dwInt32;
+ return ERROR_SUCCESS;
+ }
+
+ return ERROR_NO_MORE_FILES;
+}
+
//-----------------------------------------------------------------------------
// Functions for parsing an external listfile
@@ -65,13 +137,16 @@ void * ListFile_OpenExternal(const TCHAR * szListFile)
{
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
- pCache = CreateListFileCache((DWORD)FileSize);
+ pCache = ListFile_CreateCache((DWORD)FileSize);
if(pCache != NULL)
{
- if(!FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
+ if(FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
{
- ListFile_Free(pCache);
- pCache = NULL;
+ ListFile_CheckFormat(pCache);
+ }
+ else
+ {
+ CASC_FREE(pCache);
}
}
}
@@ -89,7 +164,7 @@ void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer)
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
- pCache = CreateListFileCache(cbBuffer);
+ pCache = ListFile_CreateCache(cbBuffer);
if(pCache != NULL)
memcpy(pCache->pBegin, pbBuffer, cbBuffer);
@@ -105,7 +180,7 @@ bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5)
assert(pCache->pPos == pCache->pBegin);
// Verify the MD5 hash for the entire block
- return VerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
+ return CascVerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
}
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd)
@@ -116,17 +191,15 @@ size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const
char * szLineEnd;
// Skip newlines, spaces, tabs and another non-printable stuff
- while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
- pCache->pPos++;
-
// Remember the begin of the line
- szLineBegin = pCache->pPos;
+ szLineBegin = ListFile_SkipSpaces(pCache);
// Copy the remaining characters
while(pCache->pPos < pCache->pEnd)
{
// If we have found a newline, stop loading
- if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A)
+ // Note: the 0x85 char came from Overwatch build 24919
+ if(pCache->pPos[0] == 0x0A || pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x85)
break;
// Blizzard listfiles can also contain information about patch:
@@ -169,14 +242,28 @@ size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars
return nLength;
}
-size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars)
+size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId)
{
+ PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
size_t nLength = 0;
int nError = ERROR_SUCCESS;
// Check for parameters
for(;;)
{
+ DWORD FileDataId = CASC_INVALID_ID;
+
+ // If this is a CSV-format listfile, we need to extract the FileDataId
+ // Lines that contain bogus data, invalid numbers or too big values will be skipped
+ if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID)
+ {
+ nError = ListFile_GetFileDataId(pCache, &FileDataId);
+ if(nError == ERROR_NO_MORE_FILES)
+ break;
+ if(nError != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
+ continue;
+ }
+
// Read the (next) line
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
if(nLength == 0)
@@ -185,12 +272,9 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer,
break;
}
- // If some mask entered, check it
- if(CheckWildCard(szBuffer, szMask))
- {
- nError = ERROR_SUCCESS;
- break;
- }
+ // Give the file data id and return true
+ PtrFileDataId[0] = FileDataId;
+ return nLength;
}
if(nError != ERROR_SUCCESS)
@@ -198,163 +282,22 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer,
return nLength;
}
-void ListFile_Free(void * pvListFile)
+LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize)
{
- if(pvListFile != NULL)
- {
- CASC_FREE(pvListFile);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Functions for creating a listfile map
-
-#define LISTMAP_INITIAL 0x100000
-
-static PLISTFILE_MAP ListMap_Create()
-{
- PLISTFILE_MAP pListMap;
- size_t cbToAllocate;
-
- // Create buffer for the listfile
- // Note that because the listfile is quite big and CASC_REALLOC
- // is a costly operation, we want to have as few reallocs as possible.
- cbToAllocate = sizeof(LISTFILE_MAP) + LISTMAP_INITIAL;
- pListMap = (PLISTFILE_MAP)CASC_ALLOC(BYTE, cbToAllocate);
- if(pListMap != NULL)
- {
- // Fill the listfile buffer
- memset(pListMap, 0, sizeof(LISTFILE_MAP));
- pListMap->cbBufferMax = LISTMAP_INITIAL;
- }
-
- return pListMap;
-}
-
-static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szFileName, size_t nLength)
-{
- PLISTFILE_ENTRY pListEntry;
- size_t cbToAllocate;
- size_t cbEntrySize;
-
- // Make sure there is enough space in the list map
- cbEntrySize = sizeof(LISTFILE_ENTRY) + nLength;
- cbEntrySize = ALIGN_TO_SIZE(cbEntrySize, 8);
- if((pListMap->cbBuffer + cbEntrySize) > pListMap->cbBufferMax)
- {
- cbToAllocate = sizeof(LISTFILE_MAP) + (pListMap->cbBufferMax * 3) / 2;
- pListMap = (PLISTFILE_MAP)CASC_REALLOC(BYTE, pListMap, cbToAllocate);
- if(pListMap == NULL)
- return NULL;
-
- pListMap->cbBufferMax = (pListMap->cbBufferMax * 3) / 2;
- }
-
- // Get the pointer to the first entry
- pListEntry = (PLISTFILE_ENTRY)((LPBYTE)(pListMap + 1) + pListMap->cbBuffer);
- pListEntry->FileNameHash = CalcFileNameHash(szFileName);
- pListEntry->cbEntrySize = (DWORD)cbEntrySize;
-
- // Copy the file name to the entry
- memcpy(pListEntry->szFileName, szFileName, nLength);
- pListEntry->szFileName[nLength] = 0;
-
- // Move the next entry
- pListMap->cbBuffer += cbEntrySize;
- pListMap->nEntries++;
- return pListMap;
-}
-
-static PLISTFILE_MAP ListMap_Finish(PLISTFILE_MAP pListMap)
-{
- PLISTFILE_ENTRY pListEntry;
- PCASC_MAP pMap;
- LPBYTE pbEntry;
-
- // Sanity check
- assert(pListMap->pNameMap == NULL);
-
- // Create the map
- pListMap->pNameMap = pMap = Map_Create((DWORD)pListMap->nEntries, sizeof(ULONGLONG), 0);
- if(pListMap->pNameMap == NULL)
- {
- ListFile_FreeMap(pListMap);
- return NULL;
- }
-
- // Fill the map
- pbEntry = (LPBYTE)(pListMap + 1);
- for(size_t i = 0; i < pListMap->nEntries; i++)
- {
- // Get the listfile entry
- pListEntry = (PLISTFILE_ENTRY)pbEntry;
- pbEntry += pListEntry->cbEntrySize;
-
- // Insert the entry to the map
- Map_InsertObject(pMap, pListEntry, &pListEntry->FileNameHash);
- }
-
- return pListMap;
-}
-
-PLISTFILE_MAP ListFile_CreateMap(const TCHAR * szListFile)
-{
- PLISTFILE_MAP pListMap = NULL;
- void * pvListFile;
- char szFileName[MAX_PATH+1];
- size_t nLength;
+ PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
+ LPBYTE pbData = NULL;
+ DWORD cbData = 0;
- // Only if the listfile name has been given
- if(szListFile != NULL)
+ // Get data from the list file cache
+ if (pvListFile != NULL)
{
- // Create map for the listfile
- pListMap = ListMap_Create();
- if(pListMap != NULL)
- {
- // Open the external listfile
- pvListFile = ListFile_OpenExternal(szListFile);
- if(pvListFile != NULL)
- {
- // Go through the entire listfile and insert each name to the map
- while((nLength = ListFile_GetNext(pvListFile, "*", szFileName, MAX_PATH)) != 0)
- {
- // Insert the file name to the map
- pListMap = ListMap_InsertName(pListMap, szFileName, nLength);
- if(pListMap == NULL)
- break;
- }
-
- if(pListMap == NULL)
- {
- // Finish the listfile map
- pListMap = ListMap_Finish(pListMap);
- }
-
- // Free the listfile
- ListFile_Free(pvListFile);
- }
- }
+ pbData = (LPBYTE)pCache->pBegin;
+ cbData = (DWORD)(pCache->pEnd - pCache->pBegin);
}
- // Return the created map
- return pListMap;
-}
-
-const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash)
-{
- PLISTFILE_ENTRY pListEntry = NULL;
-
- if(pListMap != NULL)
- pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash, NULL);
- return (pListEntry != NULL) ? pListEntry->szFileName : "";
+ // Give the data to the caller
+ if (PtrDataSize != NULL)
+ PtrDataSize[0] = cbData;
+ return pbData;
}
-void ListFile_FreeMap(PLISTFILE_MAP pListMap)
-{
- if(pListMap != NULL)
- {
- if(pListMap->pNameMap != NULL)
- Map_Free(pListMap->pNameMap);
- CASC_FREE(pListMap);
- }
-}
diff --git a/dep/CascLib/src/common/ListFile.h b/dep/CascLib/src/common/ListFile.h
index 84bec3ed751..1333b52c2b5 100644
--- a/dep/CascLib/src/common/ListFile.h
+++ b/dep/CascLib/src/common/ListFile.h
@@ -12,28 +12,6 @@
#define __LISTFILE_H__
//-----------------------------------------------------------------------------
-// Structures
-
-typedef struct _LISTFILE_ENTRY
-{
- ULONGLONG FileNameHash; // Hash of the file name
- DWORD cbEntrySize; // Length of this entry, in bytes
- char szFileName[1]; // File name, aligned to 8-byte boundary
-
-} LISTFILE_ENTRY, *PLISTFILE_ENTRY;
-
-typedef struct _LISTFILE_MAP
-{
- PCASC_MAP pNameMap; // Map of hash-to-name
- size_t cbBufferMax; // Total size of the buffer, in bytes
- size_t cbBuffer; // Current size of the buffer, in bytes
- size_t nEntries; // Number of entries
-
- // First LISTFILE_ENTRY starts here
-
-} LISTFILE_MAP, *PLISTFILE_MAP;
-
-//-----------------------------------------------------------------------------
// Functions for parsing an external listfile
void * ListFile_OpenExternal(const TCHAR * szListFile);
@@ -41,14 +19,7 @@ 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);
-
-//-----------------------------------------------------------------------------
-// Functions for creating a listfile map
-
-PLISTFILE_MAP ListFile_CreateMap(const TCHAR * szListFile);
-const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash);
-void ListFile_FreeMap(PLISTFILE_MAP pListMap);
+size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId);
+LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize);
#endif // __LISTFILE_H__
diff --git a/dep/CascLib/src/common/Map.cpp b/dep/CascLib/src/common/Map.cpp
deleted file mode 100644
index aa1e2a2ada1..00000000000
--- a/dep/CascLib/src/common/Map.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*****************************************************************************/
-/* Map.cpp Copyright (c) Ladislav Zezula 2014 */
-/*---------------------------------------------------------------------------*/
-/* Implementation of map for CascLib */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 10.06.14 1.00 Lad The first version of Map.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "../CascLib.h"
-#include "../CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-// 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;
-
- // 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];
-
- // 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;
-
- // 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 CompareObject_Key(PCASC_MAP pMap, void * pvObject, void * pvKey)
-{
- LPBYTE pbObjectKey = (LPBYTE)pvObject + pMap->KeyOffset;
-
- 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)
- {
- if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
- return false;
-
- szExistingString++;
- szString++;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset)
-{
- PCASC_MAP pMap;
- size_t cbToAllocate;
- size_t dwTableSize;
-
- // Calculate the size of the table
- dwTableSize = (dwMaxItems * 3 / 2) | 0x01;
-
- // Allocate new map for the objects
- cbToAllocate = sizeof(CASC_MAP) + (dwTableSize * sizeof(void *));
- pMap = (PCASC_MAP)CASC_ALLOC(LPBYTE, cbToAllocate);
- if(pMap != NULL)
- {
- memset(pMap, 0, cbToAllocate);
- pMap->KeyLength = dwKeyLength;
- pMap->TableSize = dwTableSize;
- pMap->KeyOffset = dwKeyOffset;
- }
-
- // Return the allocated map
- return pMap;
-}
-
-size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray)
-{
- size_t nIndex = 0;
-
- // Verify pointer to the map
- if(pMap != NULL && ppvArray != NULL)
- {
- // Enumerate all items in main table
- for(size_t i = 0; i < pMap->TableSize; i++)
- {
- // Is that cell valid?
- if(pMap->HashTable[i] != NULL)
- {
- ppvArray[nIndex++] = pMap->HashTable[i];
- }
- }
-
- return pMap->ItemCount;
- }
-
- return 0;
-}
-
-void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex)
-{
- void * pvObject;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Construct the main index
- dwHashIndex = CalcHashIndex_Key(pMap, pvKey);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- pvObject = pMap->HashTable[dwHashIndex];
-
- // Compare the hash
- if(CompareObject_Key(pMap, pvObject, pvKey))
- {
- if(PtrIndex != NULL)
- PtrIndex[0] = dwHashIndex;
- return pvObject;
- }
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
- }
-
- // Not found, sorry
- return NULL;
-}
-
-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)
-{
- const char * szExistingString;
- const char * szStringEnd = NULL;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Limit check
- 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_String(pMap, szString, szStringEnd);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- szExistingString = (const char *)pMap->HashTable[dwHashIndex];
-
- // Check if hash being inserted conflicts with an existing hash
- if(CompareObject_String(pMap, szExistingString, szString, szStringEnd))
- return false;
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
-
- // Insert at that position
- pMap->HashTable[dwHashIndex] = (void *)szString;
- pMap->ItemCount++;
- return true;
- }
-
- // Failed
- 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)
- {
- CASC_FREE(pMap);
- }
-}
diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h
index 2b590578105..13799eb0528 100644
--- a/dep/CascLib/src/common/Map.h
+++ b/dep/CascLib/src/common/Map.h
@@ -8,35 +8,355 @@
/* 10.06.14 1.00 Lad The first version of Map.h */
/*****************************************************************************/
-#ifndef __HASHTOPTR_H__
-#define __HASHTOPTR_H__
+#ifndef __CASC_MAP_H__
+#define __CASC_MAP_H__
//-----------------------------------------------------------------------------
// Structures
-#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object
+#define MIN_HASH_TABLE_SIZE 0x00000100 // The smallest size of the hash table.
+#define MAX_HASH_TABLE_SIZE 0x00800000 // The largest size of the hash table. Should be enough for any game.
-typedef struct _CASC_MAP
+typedef int (*PFNCOMPAREFUNC)(const void * pvObjectKey, const void * pvKey, size_t nKeyLength);
+typedef DWORD (*PFNHASHFUNC)(void * pvKey, size_t nKeyLength);
+
+typedef enum _KEY_TYPE
+{
+ KeyIsHash, // Use when the key is already a hash. If specified, the map will not hash the key
+ KeyIsArbitrary, // The key is an arbitrary array of bytes. The map will hash it to get a map index
+ KeyIsString // Use when the key is a string
+} KEY_TYPE, *PKEY_TYPE;
+
+#define KEY_LENGTH_STRING 0xFFFFFFFF
+
+//-----------------------------------------------------------------------------
+// Hashing functions
+
+// Used when the key is a hash already - no need to hash it again
+inline DWORD CalcHashValue_Hash(void * pvKey, size_t /* nKeyLength */)
+{
+ // Get the hash directly as value
+ return ConvertBytesToInteger_4((LPBYTE)pvKey);
+}
+
+// Calculates hash value from a key
+inline DWORD CalcHashValue_Key(void * pvKey, size_t nKeyLength)
{
- size_t TableSize;
- size_t ItemCount; // Number of items in the map
- 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
+ LPBYTE pbKey = (LPBYTE)pvKey;
+ DWORD dwHash = 0x7EEE7EEE;
-} CASC_MAP, *PCASC_MAP;
+ // Construct the hash from the key
+ for(DWORD i = 0; i < nKeyLength; i++)
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[i];
-typedef bool (*MAP_COMPARE)(PCASC_MAP pMap, void * pvObject, void * pvKey);
+ // Return the hash limited by the table size
+ return dwHash;
+}
+
+// Calculates hash value from a string
+inline DWORD CalcHashValue_String(const char * szString, const char * szStringEnd)
+{
+ LPBYTE pbKeyEnd = (LPBYTE)szStringEnd;
+ LPBYTE pbKey = (LPBYTE)szString;
+ DWORD dwHash = 0x7EEE7EEE;
+
+ // 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;
+}
//-----------------------------------------------------------------------------
-// Functions
+// Map implementation
+
+class CASC_MAP
+{
+ public:
+
+ CASC_MAP()
+ {
+ PfnCalcHashValue = NULL;
+ m_HashTable = NULL;
+ m_HashTableSize = 0;
+ m_ItemCount = 0;
+ m_KeyOffset = 0;
+ m_KeyLength = 0;
+ m_bKeyIsHash = false;
+ }
+
+ ~CASC_MAP()
+ {
+ Free();
+ }
+
+ int Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash)
+ {
+ // Set the class variables
+ m_KeyLength = CASCLIB_MAX(KeyLength, 8);
+ m_KeyOffset = KeyOffset;
+ m_ItemCount = 0;
+
+ // Setup the hashing function
+ switch(KeyType)
+ {
+ case KeyIsHash:
+ PfnCalcHashValue = CalcHashValue_Hash;
+ break;
+
+ case KeyIsArbitrary:
+ PfnCalcHashValue = CalcHashValue_Key;
+ break;
+
+ case KeyIsString:
+ PfnCalcHashValue = NULL;
+ break;
+
+ default:
+ assert(false);
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ // Calculate the hash table size. Take 133% of the item count and round it up to the next power of two
+ // This will make the hash table indexes somewhat more resilient against count changes and will make
+ // e.g. file order in the file tree more stable.
+ m_HashTableSize = GetNearestPowerOfTwo(MaxItems * 4 / 3);
+ if(m_HashTableSize == 0)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate new map for the objects
+ m_HashTable = (void **)CASC_ALLOC(void *, m_HashTableSize);
+ if(m_HashTable == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Initialize the map object
+ memset(m_HashTable, 0, m_HashTableSize * sizeof(void *));
+ return ERROR_SUCCESS;
+ }
+
+ void * FindObject(void * pvKey, PDWORD PtrIndex = NULL)
+ {
+ void * pvObject;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Construct the hash index
+ dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
+
+ // Search the hash table
+ while((pvObject = m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Compare the hash
+ if(CompareObject_Key(pvObject, pvKey))
+ {
+ if(PtrIndex != NULL)
+ PtrIndex[0] = dwHashIndex;
+ return pvObject;
+ }
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+ }
+
+ // Not found, sorry
+ return NULL;
+ }
+
+ bool InsertObject(void * pvNewObject, void * pvKey)
+ {
+ void * pvExistingObject;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Limit check
+ if((m_ItemCount + 1) >= m_HashTableSize)
+ return false;
+
+ // Construct the hash index
+ dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
+
+ // Search the hash table
+ while((pvExistingObject = m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Check if hash being inserted conflicts with an existing hash
+ if(CompareObject_Key(pvExistingObject, pvKey))
+ return false;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+
+ // Insert at that position
+ m_HashTable[dwHashIndex] = pvNewObject;
+ m_ItemCount++;
+ return true;
+ }
+
+ // Failed
+ return false;
+ }
+
+ const char * FindString(const char * szString, const char * szStringEnd)
+ {
+ const char * szExistingString;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Construct the main index
+ dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
+
+ // Search the hash table
+ while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Compare the hash
+ if(CompareObject_String(szExistingString, szString, szStringEnd))
+ return szExistingString;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+ }
+
+ // Not found, sorry
+ return NULL;
+ }
+
+ bool InsertString(const char * szString, bool bCutExtension)
+ {
+ const char * szExistingString;
+ const char * szStringEnd = NULL;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Limit check
+ if((m_ItemCount + 1) >= m_HashTableSize)
+ return false;
+
+ // Retrieve the length of the string without extension
+ if(bCutExtension)
+ szStringEnd = GetFileExtension(szString);
+ else
+ szStringEnd = szString + strlen(szString);
+
+ // Construct the hash index
+ dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
+
+ // Search the hash table
+ while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Check if hash being inserted conflicts with an existing hash
+ if(CompareObject_String(szExistingString, szString, szStringEnd))
+ return false;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+
+ // Insert at that position
+ m_HashTable[dwHashIndex] = (void *)szString;
+ m_ItemCount++;
+ return true;
+ }
+
+ // Failed
+ return false;
+ }
+
+ void * ItemAt(size_t nIndex)
+ {
+ assert(nIndex < m_HashTableSize);
+ return m_HashTable[nIndex];
+ }
+
+ size_t HashTableSize()
+ {
+ return m_HashTableSize;
+ }
+
+ size_t ItemCount()
+ {
+ return m_ItemCount;
+ }
+
+ bool IsInitialized()
+ {
+ return (m_HashTable && m_HashTableSize);
+ }
+
+ void Free()
+ {
+ PfnCalcHashValue = NULL;
+ CASC_FREE(m_HashTable);
+ m_HashTableSize = 0;
+ }
+
+ protected:
+
+ DWORD HashToIndex(DWORD HashValue)
+ {
+ return HashValue & (m_HashTableSize - 1);
+ }
+
+ bool CompareObject_Key(void * pvObject, void * pvKey)
+ {
+ LPBYTE pbObjectKey = (LPBYTE)pvObject + m_KeyOffset;
+ return (memcmp(pbObjectKey, pvKey, m_KeyLength) == 0);
+ }
+
+ bool CompareObject_String(const char * szExistingString, const char * szString, const char * szStringEnd)
+ {
+ // Compare the whole part, case insensitive
+ while(szString < szStringEnd)
+ {
+ if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
+ return false;
+
+ szExistingString++;
+ szString++;
+ }
+
+ return true;
+ }
+
+ size_t GetNearestPowerOfTwo(size_t MaxItems)
+ {
+ size_t PowerOfTwo;
+
+ // Round the hash table size up to the nearest power of two
+ for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo < MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1)
+ {
+ if(PowerOfTwo > MaxItems)
+ {
+ return PowerOfTwo;
+ }
+ }
+
+ // If the hash table is too big, we cannot create the map
+ assert(false);
+ return 0;
+ }
-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 * 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);
+ PFNHASHFUNC PfnCalcHashValue;
+ void ** m_HashTable; // Hash table
+ size_t m_HashTableSize; // Size of the hash table, in entries. Always a power of two.
+ size_t m_ItemCount; // Number of objects in the map
+ size_t m_KeyOffset; // How far is the hash from the begin of the objects (in bytes)
+ size_t m_KeyLength; // Length of the hash key, in bytes
+ bool m_bKeyIsHash; // If set, then it means that the key is a hash of some sort.
+ // Will improve performance, as we will not hash a hash :-)
+};
-#endif // __HASHTOPTR_H__
+#endif // __CASC_MAP_H__
diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp
index 4baeb41a421..89b693f55cb 100644
--- a/dep/CascLib/src/common/RootHandler.cpp
+++ b/dep/CascLib/src/common/RootHandler.cpp
@@ -13,76 +13,105 @@
#include "../CascCommon.h"
//-----------------------------------------------------------------------------
-// Common support
+// Constructor and destructor - TFileTreeRoot
-int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey)
+TFileTreeRoot::TFileTreeRoot(DWORD FileTreeFlags) : TRootHandler()
{
- if(pRootHandler == NULL || pRootHandler->Insert == NULL || pbEncodingKey == NULL)
- return ERROR_NOT_SUPPORTED;
+ // Initialize the file tree
+ FileTree.Create(FileTreeFlags);
+}
- return pRootHandler->Insert(pRootHandler, szFileName, pbEncodingKey);
+TFileTreeRoot::~TFileTreeRoot()
+{
+ // Free the file tree
+ FileTree.Free();
+ dwFeatures = 0;
}
-LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags, PDWORD PtrFileDataId)
+//-----------------------------------------------------------------------------
+// Virtual functions - TFileTreeRoot
+
+int TFileTreeRoot::Insert(
+ const char * szFileName,
+ PCASC_CKEY_ENTRY pCKeyEntry)
{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return NULL;
+ PCASC_FILE_NODE pFileNode;
- return pRootHandler->Search(pRootHandler, pSearch, PtrFileSize, PtrLocaleFlags, PtrFileDataId);
+ pFileNode = FileTree.InsertByName(pCKeyEntry, szFileName, FileTree.GetNextFileDataId());
+ return (pFileNode != NULL) ? ERROR_SUCCESS : ERROR_CAN_NOT_COMPLETE;
}
-void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch)
+PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, const char * szFileName)
{
- // Check if the root structure is valid at all
- if(pRootHandler != NULL)
- {
- pRootHandler->EndSearch(pRootHandler, pSearch);
- }
+ PCASC_FILE_NODE pFileNode;
+ ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
+
+ pFileNode = FileTree.Find(FileNameHash);
+ return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL;
}
-LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName)
+PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, DWORD FileDataId)
{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return NULL;
+ PCASC_FILE_NODE pFileNode;
- return pRootHandler->GetKey(pRootHandler, szFileName);
+ pFileNode = FileTree.FindById(FileDataId);
+ return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL;
}
-void RootHandler_Dump(TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel)
+PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
- TDumpContext * dc;
+ PCASC_FILE_NODE pFileNode;
+ size_t nMaxFileIndex = FileTree.GetMaxFileIndex();
- // Only if the ROOT provider suports the dump option
- if(hs->pRootHandler != NULL && hs->pRootHandler->Dump != NULL)
+ // Are we still inside the root directory range?
+ while(pSearch->nFileIndex < nMaxFileIndex)
{
- // Create the dump file
- dc = CreateDumpContext(hs, szNameFormat);
- if(dc != NULL)
+ //BREAKIF(pSearch->nFileIndex >= 2823765);
+
+ // Retrieve the file item
+ pFileNode = FileTree.PathAt(pFindData->szFileName, MAX_PATH, pSearch->nFileIndex++);
+ if(pFileNode != NULL)
{
- // Dump the content and close the file
- hs->pRootHandler->Dump(hs, dc, pbRootHandler, cbRootHandler, szListFile, nDumpLevel);
- dump_close(dc);
+ // Ignore folders and mount points
+ if(!(pFileNode->Flags & CFN_FLAG_FOLDER))
+ {
+ // Check the wildcard
+ if (CascCheckWildCard(pFindData->szFileName, pSearch->szMask))
+ {
+ // Retrieve the extra values (FileDataId, file size and locale flags)
+ FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
+
+ // Supply the bCanOpenByDataId variable
+ pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
+ pFindData->bCanOpenByDataId = (pFindData->dwFileDataId != CASC_INVALID_ID);
+
+ // Return the found CKey entry
+ return pFileNode->pCKeyEntry;
+ }
+ }
}
}
+
+ // No more entries
+ return NULL;
}
-void RootHandler_Close(TRootHandler * pRootHandler)
+bool TFileTreeRoot::GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, PCASC_FILE_FULL_INFO pFileInfo)
{
- // Check if the root structure is allocated at all
- if(pRootHandler != NULL)
+ PCASC_FILE_NODE pFileNode;
+
+ // Can't do much if the root key is NULL
+ if(pCKeyEntry != NULL)
{
- pRootHandler->Close(pRootHandler);
+ pFileNode = FileTree.Find(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ FileTree.GetExtras(pFileNode, &pFileInfo->FileDataId, &pFileInfo->LocaleFlags, &pFileInfo->ContentFlags);
+ pFileInfo->FileNameHash = pFileNode->FileNameHash;
+ return true;
+ }
}
-}
-
-DWORD RootHandler_GetFileId(TRootHandler * pRootHandler, const char * szFileName)
-{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return 0;
- return pRootHandler->GetFileId(pRootHandler, szFileName);
+ return false;
}
diff --git a/dep/CascLib/src/common/RootHandler.h b/dep/CascLib/src/common/RootHandler.h
index d1b66e501d8..4e56650b034 100644
--- a/dep/CascLib/src/common/RootHandler.h
+++ b/dep/CascLib/src/common/RootHandler.h
@@ -15,82 +15,97 @@
// Defines
#define CASC_MNDX_ROOT_SIGNATURE 0x58444E4D // 'MNDX'
+#define CASC_TVFS_ROOT_SIGNATURE 0x53465654 // 'TVFS'
#define CASC_DIABLO3_ROOT_SIGNATURE 0x8007D0C4
-#define CASC_OVERWATCH_ROOT_SIGNATURE 0x35444D23 // '#MD5'
+#define CASC_WOW82_ROOT_SIGNATURE 0x4D465354 // 'TSFM', WoW since 8.2
-#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
+#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)
- PDWORD PtrFileDataId // Pointer to FileDataID (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
- );
-
-typedef DWORD(*ROOT_GETFILEID)(
-struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- const char * szFileName // Pointer to the name of a file
- );
+// Class for generic 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
- ROOT_GETFILEID GetFileId; // Returns File Id for a given Filename
-
- DWORD dwRootFlags; // Root flags - see the ROOT_FLAG_XXX
+ public:
+
+ TRootHandler()
+ {
+ dwFeatures = 0;
+ }
+
+ virtual ~TRootHandler()
+ {}
+
+ // Inserts new file name to the root handler
+ // szFileName - Pointer to the file name
+ // pCKeyEntry - Pointer to the CASC_CKEY_ENTRY for the file
+ virtual int Insert(const char * /* szFileName */, PCASC_CKEY_ENTRY /* pCKeyEntry */)
+ {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ // Searches the file by file name
+ // hs - Pointer to the storage structure
+ // szFileName - Pointer to the file name
+ virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, const char * /* szFileName */)
+ {
+ return NULL;
+ }
+
+ // Searches the file by file data id
+ // hs - Pointer to the storage structure
+ // FileDataId - File data id
+ virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, DWORD /* FileDataId */)
+ {
+ return NULL;
+ }
+
+ // Performs find-next-file operation
+ // pSearch - Pointer to the initialized search structure
+ // pFindData - Pointer to output structure that will contain the information
+ virtual PCASC_CKEY_ENTRY Search(struct TCascSearch * /* pSearch */, struct _CASC_FIND_DATA * /* pFindData */)
+ {
+ return NULL;
+ }
+
+ // Returns advanced info from the root file entry.
+ // pCKeyEntry - CKey/EKey, depending on which type the root handler provides
+ // pFileInfo - Pointer to CASC_FILE_FULL_INFO structure
+ virtual bool GetInfo(PCASC_CKEY_ENTRY /* pCKeyEntry */, struct _CASC_FILE_FULL_INFO * /* pFileInfo */)
+ {
+ return false;
+ }
+
+ DWORD GetFeatures()
+ {
+ return dwFeatures;
+ }
+
+ protected:
+
+ DWORD dwFeatures; // CASC features. See CASC_FEATURE_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, PDWORD PtrFileDataId);
-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);
-DWORD RootHandler_GetFileId(TRootHandler * pRootHandler, const char * szFileName);
+// Class for root handler that has basic mapping of FileName -> CASC_FILE_NODE
+
+struct TFileTreeRoot : public TRootHandler
+{
+ TFileTreeRoot(DWORD FileTreeFlags);
+ virtual ~TFileTreeRoot();
+
+ int Insert(const char * szFileName, PCASC_CKEY_ENTRY pCKeyEntry);
+
+ PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, const char * szFileName);
+ PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, DWORD FileDataId);
+ PCASC_CKEY_ENTRY Search(struct TCascSearch * pSearch, struct _CASC_FIND_DATA * pFindData);
+ bool GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, struct _CASC_FILE_FULL_INFO * pFileInfo);
+
+ protected:
+
+ CASC_FILE_TREE FileTree;
+};
#endif // __ROOT_HANDLER_H__