mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Tools/Extractors: Updated map extractor
This commit is contained in:
@@ -39,5 +39,5 @@ if(SERVERS)
|
||||
endif()
|
||||
|
||||
if(TOOLS)
|
||||
add_subdirectory(StormLib)
|
||||
add_subdirectory(CascLib)
|
||||
endif()
|
||||
|
||||
42
dep/CascLib/CMakeLists.txt
Normal file
42
dep/CascLib/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
set(HEADER_FILES
|
||||
src/CascCommon.h
|
||||
src/CascLib.h
|
||||
src/CascMndxRoot.h
|
||||
src/CascPort.h
|
||||
src/common/Common.h
|
||||
src/common/FileStream.h
|
||||
src/common/ListFile.h
|
||||
src/common/Map.h
|
||||
src/jenkins/lookup.h
|
||||
)
|
||||
|
||||
set(SRC_FILES
|
||||
src/common/Common.cpp
|
||||
src/common/Directory.cpp
|
||||
src/common/FileStream.cpp
|
||||
src/common/ListFile.cpp
|
||||
src/common/Map.cpp
|
||||
src/jenkins/lookup3.c
|
||||
src/CascBuildCfg.cpp
|
||||
src/CascCommon.cpp
|
||||
src/CascDecompress.cpp
|
||||
src/CascDumpData.cpp
|
||||
src/CascFindFile.cpp
|
||||
src/CascMndxRoot.cpp
|
||||
src/CascOpenFile.cpp
|
||||
src/CascOpenStorage.cpp
|
||||
src/CascReadFile.cpp
|
||||
)
|
||||
|
||||
set(TOMCRYPT_FILES
|
||||
src/libtomcrypt/src/hashes/hash_memory.c
|
||||
src/libtomcrypt/src/hashes/md5.c
|
||||
src/libtomcrypt/src/misc/crypt_argchk.c
|
||||
src/libtomcrypt/src/misc/crypt_hash_descriptor.c
|
||||
src/libtomcrypt/src/misc/crypt_hash_is_valid.c
|
||||
src/libtomcrypt/src/misc/crypt_libc.c
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/dep)
|
||||
|
||||
add_library(casc STATIC ${SRC_FILES} ${TOMCRYPT_FILES})
|
||||
21
dep/CascLib/LICENSE
Normal file
21
dep/CascLib/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Ladislav Zezula
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
4
dep/CascLib/README.md
Normal file
4
dep/CascLib/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
CascLib
|
||||
=======
|
||||
|
||||
An open-source implementation of library for reading CASC storage from Blizzard games since 2014
|
||||
10
dep/CascLib/doc/history.txt
Normal file
10
dep/CascLib/doc/history.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
StormLib history
|
||||
|
||||
================
|
||||
|
||||
|
||||
Version 1.00
|
||||
|
||||
|
||||
|
||||
- Created
|
||||
922
dep/CascLib/src/CascBuildCfg.cpp
Normal file
922
dep/CascLib/src/CascBuildCfg.cpp
Normal file
@@ -0,0 +1,922 @@
|
||||
/*****************************************************************************/
|
||||
/* CascBuildCfg.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Build configuration for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool inline IsValueSeparator(LPBYTE pbVarValue)
|
||||
{
|
||||
return ((0 <= pbVarValue[0] && pbVarValue[0] <= 0x20) || (pbVarValue[0] == '|'));
|
||||
}
|
||||
|
||||
static bool IsCharDigit(BYTE OneByte)
|
||||
{
|
||||
return ('0' <= OneByte && OneByte <= '9');
|
||||
}
|
||||
|
||||
static void FreeCascBlob(PQUERY_KEY pBlob)
|
||||
{
|
||||
if(pBlob != NULL)
|
||||
{
|
||||
if(pBlob->pbData != NULL)
|
||||
CASC_FREE(pBlob->pbData);
|
||||
|
||||
pBlob->pbData = NULL;
|
||||
pBlob->cbData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType)
|
||||
{
|
||||
size_t nLength;
|
||||
|
||||
// Check the variable name
|
||||
nLength = strlen(szVarName);
|
||||
if((size_t)(szLineEnd - szLineBegin) > nLength)
|
||||
{
|
||||
// Check the variable name
|
||||
if(!_strnicmp(szLineBegin, szVarName, nLength))
|
||||
{
|
||||
// Skip variable name and the exclamation mark
|
||||
szLineBegin += nLength;
|
||||
if(szLineBegin < szLineEnd && szLineBegin[0] == '!')
|
||||
{
|
||||
// Skip the exclamation mark
|
||||
szLineBegin++;
|
||||
|
||||
// Check the variable type
|
||||
nLength = strlen(szVarType);
|
||||
if((size_t)(szLineEnd - szLineBegin) > nLength)
|
||||
{
|
||||
// Check the variable name
|
||||
if(!_strnicmp(szLineBegin, szVarType, nLength))
|
||||
{
|
||||
// Skip variable type and the doublecolon
|
||||
szLineBegin += nLength;
|
||||
return (szLineBegin < szLineEnd && szLineBegin[0] == ':');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd)
|
||||
{
|
||||
while(szLineBegin < szLineEnd)
|
||||
{
|
||||
if(szLineBegin[0] == '|')
|
||||
return szLineBegin + 1;
|
||||
|
||||
szLineBegin++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
|
||||
{
|
||||
TCHAR * szIndexPath;
|
||||
|
||||
// Cpmbine the index path
|
||||
szIndexPath = CombinePath(hs->szDataPath, szSubDir);
|
||||
if(DirectoryExists(szIndexPath))
|
||||
{
|
||||
hs->szIndexPath = szIndexPath;
|
||||
return hs->szIndexPath;
|
||||
}
|
||||
|
||||
delete [] szIndexPath;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator)
|
||||
{
|
||||
// Put the separator, if any
|
||||
if(chSeparator != 0)
|
||||
*szBuffer++ = chSeparator;
|
||||
|
||||
// Copy the blob data as text
|
||||
for(DWORD i = 0; i < cbData; i++)
|
||||
{
|
||||
*szBuffer++ = IntToHexChar[pbData[0] >> 0x04];
|
||||
*szBuffer++ = IntToHexChar[pbData[0] & 0x0F];
|
||||
pbData++;
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*szBuffer = 0;
|
||||
|
||||
// Return new buffer position
|
||||
return szBuffer;
|
||||
}
|
||||
|
||||
static int StringBlobToBinaryBlob(
|
||||
PQUERY_KEY pBlob,
|
||||
LPBYTE pbBlobBegin,
|
||||
LPBYTE pbBlobEnd)
|
||||
{
|
||||
// Sanity checks
|
||||
assert(pBlob != NULL && pBlob->pbData != NULL);
|
||||
|
||||
// Reset the blob length
|
||||
pBlob->cbData = 0;
|
||||
|
||||
// Convert the blob
|
||||
while(pbBlobBegin < pbBlobEnd)
|
||||
{
|
||||
BYTE DigitOne;
|
||||
BYTE DigitTwo;
|
||||
|
||||
DigitOne = (BYTE)(AsciiToUpperTable[pbBlobBegin[0]] - '0');
|
||||
if(DigitOne > 9)
|
||||
DigitOne -= 'A' - '9' - 1;
|
||||
|
||||
DigitTwo = (BYTE)(AsciiToUpperTable[pbBlobBegin[1]] - '0');
|
||||
if(DigitTwo > 9)
|
||||
DigitTwo -= 'A' - '9' - 1;
|
||||
|
||||
if(DigitOne > 0x0F || DigitTwo > 0x0F || pBlob->cbData >= MAX_CASC_KEY_LENGTH)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
pBlob->pbData[pBlob->cbData++] = (DigitOne << 0x04) | DigitTwo;
|
||||
pbBlobBegin += 2;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static LPBYTE FindNextSeparator(PQUERY_KEY pFileBlob, LPBYTE pbFilePtr)
|
||||
{
|
||||
LPBYTE pbFileBegin = pFileBlob->pbData;
|
||||
LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData;
|
||||
|
||||
if(pbFileBegin <= pbFilePtr && pbFilePtr < pbFileEnd)
|
||||
{
|
||||
while(pbFilePtr < pbFileEnd && pbFilePtr[0] != '|')
|
||||
pbFilePtr++;
|
||||
|
||||
return pbFilePtr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool GetNextFileLine(PQUERY_KEY pFileBlob, LPBYTE * ppbLineBegin, LPBYTE * ppbLineEnd)
|
||||
{
|
||||
LPBYTE pbLineBegin = *ppbLineBegin;
|
||||
LPBYTE pbLineEnd = *ppbLineEnd;
|
||||
LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData;
|
||||
|
||||
// If there was a previous line, skip all end-of-line chars
|
||||
if(pbLineEnd != NULL)
|
||||
{
|
||||
// Go to the next line
|
||||
while(pbLineEnd < pbFileEnd && (pbLineEnd[0] == 0x0A || pbLineEnd[0] == 0x0D))
|
||||
pbLineEnd++;
|
||||
pbLineBegin = pbLineEnd;
|
||||
|
||||
// If there is no more data, return false
|
||||
if(pbLineEnd >= pbFileEnd)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip all spaces before the line begins
|
||||
while(pbLineBegin < pbFileEnd && (pbLineBegin[0] == 0x09 || pbLineBegin[0] == 0x20))
|
||||
pbLineBegin++;
|
||||
pbLineEnd = pbLineBegin;
|
||||
|
||||
// Go to the end of the line
|
||||
while(pbLineEnd < pbFileEnd && pbLineEnd[0] != 0x0A && pbLineEnd[0] != 0x0D)
|
||||
pbLineEnd++;
|
||||
|
||||
// Give the results to the caller
|
||||
*ppbLineBegin = pbLineBegin;
|
||||
*ppbLineEnd = pbLineEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
static LPBYTE CheckLineVariable(LPBYTE pbLineBegin, LPBYTE pbLineEnd, const char * szVarName)
|
||||
{
|
||||
size_t nLineLength = (size_t)(pbLineEnd - pbLineBegin);
|
||||
size_t nNameLength = strlen(szVarName);
|
||||
|
||||
// If the line longer than the variable name?
|
||||
if(nLineLength > nNameLength)
|
||||
{
|
||||
if(!_strnicmp((const char *)pbLineBegin, szVarName, nNameLength))
|
||||
{
|
||||
// Skip the variable name
|
||||
pbLineBegin += nNameLength;
|
||||
|
||||
// Skip the separator(s)
|
||||
while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin))
|
||||
pbLineBegin++;
|
||||
|
||||
// Check if there is "="
|
||||
if(pbLineBegin >= pbLineEnd || pbLineBegin[0] != '=')
|
||||
return NULL;
|
||||
pbLineBegin++;
|
||||
|
||||
// Skip the separator(s)
|
||||
while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin))
|
||||
pbLineBegin++;
|
||||
|
||||
// Check if there is "="
|
||||
if(pbLineBegin >= pbLineEnd)
|
||||
return NULL;
|
||||
|
||||
// Return the begin of the variable
|
||||
return pbLineBegin;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue)
|
||||
{
|
||||
const char * szLinePtr = szLineBegin;
|
||||
|
||||
// Sanity checks
|
||||
assert(pVarBlob->pbData == NULL);
|
||||
assert(pVarBlob->cbData == 0);
|
||||
|
||||
// Check length of the variable
|
||||
while(szLinePtr < szLineEnd && szLinePtr[0] != '|')
|
||||
szLinePtr++;
|
||||
|
||||
// Allocate space for the blob
|
||||
if(bHexaValue)
|
||||
{
|
||||
// Initialize the blob
|
||||
pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2);
|
||||
return StringBlobToBinaryBlob(pVarBlob, (LPBYTE)szLineBegin, (LPBYTE)szLinePtr);
|
||||
}
|
||||
|
||||
// Initialize the blob
|
||||
pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1);
|
||||
pVarBlob->cbData = (size_t)(szLinePtr - szLineBegin);
|
||||
|
||||
// Check for success
|
||||
if(pVarBlob->pbData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Copy the string
|
||||
memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData);
|
||||
pVarBlob->pbData[pVarBlob->cbData] = 0;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey)
|
||||
{
|
||||
// Get to the end of the file name
|
||||
szFileName = szFileName + _tcslen(szFileName);
|
||||
|
||||
// Append the "config" directory
|
||||
_tcscat(szFileName, _T("/config"));
|
||||
szFileName += 7;
|
||||
|
||||
// Append the first level directory
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/'));
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/'));
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/'));
|
||||
}
|
||||
|
||||
static DWORD GetBlobCount(LPBYTE pbLineBegin, LPBYTE pbLineEnd)
|
||||
{
|
||||
DWORD dwBlobCount = 0;
|
||||
|
||||
// Until we find an end of the line
|
||||
while(pbLineBegin < pbLineEnd)
|
||||
{
|
||||
// Skip the blob
|
||||
while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin) == false)
|
||||
pbLineBegin++;
|
||||
|
||||
// Increment the number of blobs
|
||||
dwBlobCount++;
|
||||
|
||||
// Skip the separator
|
||||
while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin))
|
||||
pbLineBegin++;
|
||||
}
|
||||
|
||||
return dwBlobCount;
|
||||
}
|
||||
|
||||
static int LoadBlobArray(
|
||||
PQUERY_KEY pBlob,
|
||||
DWORD dwMaxBlobs,
|
||||
LPBYTE pbLineBegin,
|
||||
LPBYTE pbLineEnd,
|
||||
LPBYTE pbBuffer,
|
||||
DWORD dwBufferSize)
|
||||
{
|
||||
LPBYTE pbBlobBegin = pbLineBegin;
|
||||
LPBYTE pbBlobEnd = pbLineBegin;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Sanity check
|
||||
assert(pbBuffer != NULL);
|
||||
|
||||
// Until we find an end of the line
|
||||
while(pbBlobBegin < pbLineEnd)
|
||||
{
|
||||
// Convert the blob from string to binary
|
||||
if(dwBufferSize < MAX_CASC_KEY_LENGTH)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Find the end of the text blob
|
||||
while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd) == false)
|
||||
pbBlobEnd++;
|
||||
|
||||
// Convert the blob from ANSI to binary
|
||||
pBlob->pbData = pbBuffer;
|
||||
nError = StringBlobToBinaryBlob(pBlob, pbBlobBegin, pbBlobEnd);
|
||||
if(nError != ERROR_SUCCESS || dwMaxBlobs == 1)
|
||||
break;
|
||||
|
||||
// Move the blob, buffer, and limits
|
||||
dwBufferSize -= MAX_CASC_KEY_LENGTH;
|
||||
pbBuffer += MAX_CASC_KEY_LENGTH;
|
||||
dwMaxBlobs--;
|
||||
pBlob++;
|
||||
|
||||
// Skip the separator
|
||||
while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd))
|
||||
pbBlobEnd++;
|
||||
pbBlobBegin = pbBlobEnd;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadSingleBlob(PQUERY_KEY pBlob, LPBYTE pbBlobBegin, LPBYTE pbBlobEnd)
|
||||
{
|
||||
LPBYTE pbBuffer;
|
||||
size_t nLength = (pbBlobEnd - pbBlobBegin) / 2;
|
||||
|
||||
// Check maximum size
|
||||
if(nLength > MAX_CASC_KEY_LENGTH)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Allocate the blob buffer
|
||||
pbBuffer = CASC_ALLOC(BYTE, MAX_CASC_KEY_LENGTH);
|
||||
if(pbBuffer == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
return LoadBlobArray(pBlob, 1, pbBlobBegin, pbBlobEnd, pbBuffer, MAX_CASC_KEY_LENGTH);
|
||||
}
|
||||
|
||||
static PQUERY_KEY LoadMultipleBlobs(LPBYTE pbLineBegin, LPBYTE pbLineEnd, DWORD * pdwBlobCount)
|
||||
{
|
||||
PQUERY_KEY pBlobArray = NULL;
|
||||
LPBYTE pbBuffer = NULL;
|
||||
DWORD dwBlobCount = GetBlobCount(pbLineBegin, pbLineEnd);
|
||||
int nError;
|
||||
|
||||
// Only if there is at least 1 blob
|
||||
if(dwBlobCount != 0)
|
||||
{
|
||||
// Allocate the array of blobs
|
||||
pBlobArray = CASC_ALLOC(QUERY_KEY, dwBlobCount);
|
||||
if(pBlobArray != NULL)
|
||||
{
|
||||
// Zero the blob array
|
||||
memset(pBlobArray, 0, dwBlobCount * sizeof(QUERY_KEY));
|
||||
|
||||
// Allocate buffer for the blobs
|
||||
pbBuffer = CASC_ALLOC(BYTE, dwBlobCount * MAX_CASC_KEY_LENGTH);
|
||||
if(pbBuffer != NULL)
|
||||
{
|
||||
// Zero the buffer
|
||||
memset(pbBuffer, 0, dwBlobCount * MAX_CASC_KEY_LENGTH);
|
||||
|
||||
// Load the entire blob array
|
||||
nError = LoadBlobArray(pBlobArray, dwBlobCount, pbLineBegin, pbLineEnd, pbBuffer, dwBlobCount * MAX_CASC_KEY_LENGTH);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
*pdwBlobCount = dwBlobCount;
|
||||
return pBlobArray;
|
||||
}
|
||||
|
||||
// Free the buffer
|
||||
CASC_FREE(pbBuffer);
|
||||
}
|
||||
|
||||
// Free the array of blobs
|
||||
CASC_FREE(pBlobArray);
|
||||
pBlobArray = NULL;
|
||||
}
|
||||
|
||||
// Reset the blob count
|
||||
dwBlobCount = 0;
|
||||
}
|
||||
|
||||
*pdwBlobCount = dwBlobCount;
|
||||
return pBlobArray;
|
||||
}
|
||||
|
||||
static int LoadTextFile(const TCHAR * szFileName, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
ULONGLONG FileSize = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Open the agent file
|
||||
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Retrieve its size
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
|
||||
// Load the file to memory
|
||||
if(0 < FileSize && FileSize < 0x100000)
|
||||
{
|
||||
// Initialize the blob
|
||||
pFileBlob->cbData = (DWORD)FileSize;
|
||||
pFileBlob->pbData = CASC_ALLOC(BYTE, pFileBlob->cbData + 1);
|
||||
|
||||
// Load the file data into the blob
|
||||
if(pFileBlob->pbData != NULL)
|
||||
{
|
||||
FileStream_Read(pStream, NULL, pFileBlob->pbData, (DWORD)FileSize);
|
||||
pFileBlob->pbData[pFileBlob->cbData] = 0;
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
else
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
FileStream_Close(pStream);
|
||||
}
|
||||
else
|
||||
nError = GetLastError();
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int GetGameType(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd)
|
||||
{
|
||||
// Alpha build of Heroes of the Storm
|
||||
if((pbLineEnd - pbVarBegin) == 4 && !_strnicmp((const char *)pbVarBegin, "Hero", 4))
|
||||
{
|
||||
hs->dwGameInfo = CASC_GAME_HOTS;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Alpha build of World of Warcraft - Warlords of Draenor
|
||||
if((pbLineEnd - pbVarBegin) == 3 && !_strnicmp((const char *)pbVarBegin, "WoW", 3))
|
||||
{
|
||||
hs->dwGameInfo = CASC_GAME_WOW6;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// An unknown game
|
||||
assert(false);
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// "B29049"
|
||||
// "WOW-18125patch6.0.1"
|
||||
static int GetBuildNumber(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd)
|
||||
{
|
||||
DWORD dwBuildNumber = 0;
|
||||
|
||||
// Skip all non-digit characters
|
||||
while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0]) == false)
|
||||
pbVarBegin++;
|
||||
|
||||
// Convert the build number
|
||||
while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0]))
|
||||
dwBuildNumber = (dwBuildNumber * 10) + (*pbVarBegin++ - '0');
|
||||
|
||||
assert(dwBuildNumber != 0);
|
||||
hs->dwBuildNumber = dwBuildNumber;
|
||||
return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static int FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
TCHAR * szFileName;
|
||||
int nError;
|
||||
|
||||
// Construct the local file name
|
||||
szFileName = NewStr(hs->szDataPath, 8 + 3 + 3 + 32);
|
||||
if(szFileName == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Add the part where the config file path is
|
||||
AppendConfigFilePath(szFileName, pFileKey);
|
||||
|
||||
// Load the config file
|
||||
nError = LoadTextFile(szFileName, pFileBlob);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Verify the blob's MD5
|
||||
if(!VerifyDataBlockHash(pFileBlob->pbData, pFileBlob->cbData, pFileKey->pbData))
|
||||
{
|
||||
FreeCascBlob(pFileBlob);
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
CASC_FREE(szFileName);
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ParseInfoFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
QUERY_KEY CdnHost = {NULL, 0};
|
||||
QUERY_KEY CdnPath = {NULL, 0};
|
||||
const char * szLineBegin1 = NULL;
|
||||
const char * szLineBegin2 = NULL;
|
||||
const char * szLineEnd1 = NULL;
|
||||
const char * szLineEnd2 = NULL;
|
||||
const char * szFileEnd = (const char *)(pFileBlob->pbData + pFileBlob->cbData);
|
||||
const char * szFilePtr = (const char *)pFileBlob->pbData;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
|
||||
// Find the first line
|
||||
szLineBegin1 = szFilePtr;
|
||||
while(szFilePtr < szFileEnd)
|
||||
{
|
||||
// Check for the end of the line
|
||||
if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A)
|
||||
{
|
||||
szLineEnd1 = szFilePtr;
|
||||
break;
|
||||
}
|
||||
|
||||
szFilePtr++;
|
||||
}
|
||||
|
||||
// Skip the newline character(s)
|
||||
while(szFilePtr < szFileEnd && (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A))
|
||||
szFilePtr++;
|
||||
|
||||
// Find the second line
|
||||
szLineBegin2 = szFilePtr;
|
||||
while(szFilePtr < szFileEnd)
|
||||
{
|
||||
// Check for the end of the line
|
||||
if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A)
|
||||
{
|
||||
szLineEnd2 = szFilePtr;
|
||||
break;
|
||||
}
|
||||
|
||||
szFilePtr++;
|
||||
}
|
||||
|
||||
// Find the build key, CDN config key and the URL path
|
||||
while(szLineBegin1 < szLineEnd1)
|
||||
{
|
||||
// Check for variables we need
|
||||
if(IsInfoVariable(szLineBegin1, szLineEnd1, "Build Key", "HEX"))
|
||||
LoadInfoVariable(&hs->CdnBuildKey, szLineBegin2, szLineEnd2, true);
|
||||
if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Key", "HEX"))
|
||||
LoadInfoVariable(&hs->CdnConfigKey, szLineBegin2, szLineEnd2, true);
|
||||
if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Hosts", "STRING"))
|
||||
LoadInfoVariable(&CdnHost, szLineBegin2, szLineEnd2, false);
|
||||
if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Path", "STRING"))
|
||||
LoadInfoVariable(&CdnPath, szLineBegin2, szLineEnd2, false);
|
||||
|
||||
// Move both line pointers
|
||||
szLineBegin1 = SkipInfoVariable(szLineBegin1, szLineEnd1);
|
||||
if(szLineBegin1 == NULL)
|
||||
break;
|
||||
|
||||
szLineBegin2 = SkipInfoVariable(szLineBegin2, szLineEnd2);
|
||||
if(szLineBegin2 == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
// All four must be present
|
||||
if(hs->CdnBuildKey.pbData != NULL &&
|
||||
hs->CdnConfigKey.pbData != NULL &&
|
||||
CdnHost.pbData != NULL &&
|
||||
CdnPath.pbData != NULL)
|
||||
{
|
||||
// Merge the CDN host and CDN path
|
||||
hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1);
|
||||
if(hs->szUrlPath != NULL)
|
||||
{
|
||||
CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData);
|
||||
CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData);
|
||||
nError = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
FreeCascBlob(&CdnHost);
|
||||
FreeCascBlob(&CdnPath);
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ParseAgentFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
LPBYTE pbBlobBegin = pFileBlob->pbData;
|
||||
LPBYTE pbBlobEnd;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Extract the CDN build hash
|
||||
pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin);
|
||||
if(pbBlobEnd != NULL)
|
||||
{
|
||||
// Convert the string to a blob
|
||||
nError = LoadSingleBlob(&hs->CdnBuildKey, pbBlobBegin, pbBlobEnd);
|
||||
|
||||
// Move to the next part
|
||||
if(pbBlobEnd[0] == _T('|'))
|
||||
pbBlobEnd++;
|
||||
pbBlobBegin = pbBlobEnd;
|
||||
}
|
||||
|
||||
// Extract the CDN config hash
|
||||
pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin);
|
||||
if(pbBlobEnd != NULL)
|
||||
{
|
||||
// Convert the string to a blob
|
||||
nError = LoadSingleBlob(&hs->CdnConfigKey, pbBlobBegin, pbBlobEnd);
|
||||
|
||||
// Move to the next part
|
||||
if(pbBlobEnd[0] == _T('|'))
|
||||
pbBlobEnd++;
|
||||
pbBlobBegin = pbBlobEnd;
|
||||
}
|
||||
|
||||
// Skip the intermediate part
|
||||
pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin);
|
||||
if(pbBlobEnd != NULL)
|
||||
{
|
||||
// Move to the next part
|
||||
if(pbBlobEnd[0] == _T('|'))
|
||||
pbBlobEnd++;
|
||||
pbBlobBegin = pbBlobEnd;
|
||||
}
|
||||
|
||||
// Extract the URL config hash
|
||||
pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin);
|
||||
if(pbBlobEnd != NULL)
|
||||
{
|
||||
// Convert the string to a blob
|
||||
hs->szUrlPath = NewStrFromAnsi(pbBlobBegin, pbBlobEnd);
|
||||
}
|
||||
|
||||
// Verify all variables
|
||||
if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadCdnConfigFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
LPBYTE pbLineBegin = pFileBlob->pbData;
|
||||
LPBYTE pbVarBegin;
|
||||
LPBYTE pbLineEnd = NULL;
|
||||
int nError;
|
||||
|
||||
while(pbLineBegin != NULL)
|
||||
{
|
||||
// Get the next line
|
||||
if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd))
|
||||
break;
|
||||
|
||||
// Archive group
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archive-group");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
nError = LoadSingleBlob(&hs->ArchiveGroup, pbVarBegin, pbLineEnd);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Archives
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archives");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
hs->pArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->ArchiveCount);
|
||||
if(hs->pArchiveArray == NULL || hs->ArchiveCount == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch archive group
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archive-group");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->PatchArchiveGroup, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch archives
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archives");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
hs->pPatchArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->PatchArchiveCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all required fields are present
|
||||
if(hs->ArchiveGroup.pbData == NULL || hs->ArchiveGroup.cbData == 0 || hs->pArchiveArray == NULL || hs->ArchiveCount == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadCdnBuildFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
|
||||
{
|
||||
LPBYTE pbLineBegin = pFileBlob->pbData;
|
||||
LPBYTE pbVarBegin;
|
||||
LPBYTE pbLineEnd = NULL;
|
||||
|
||||
while(pbLineBegin != NULL)
|
||||
{
|
||||
// Get the next line
|
||||
if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd))
|
||||
break;
|
||||
|
||||
// Game name
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-product");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
GetGameType(hs, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Game build number
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-name");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
GetBuildNumber(hs, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Root
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "root");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->RootKey, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->PatchKey, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "download");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->DownloadKey, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Install
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "install");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->InstallKey, pbVarBegin, pbLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Encoding keys
|
||||
pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "encoding");
|
||||
if(pbVarBegin != NULL)
|
||||
{
|
||||
hs->pEncodingKeys = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->EncodingKeys);
|
||||
if(hs->pEncodingKeys == NULL || hs->EncodingKeys != 2)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
hs->EncodingKey = hs->pEncodingKeys[0];
|
||||
hs->EncodingEKey = hs->pEncodingKeys[1];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the encoding keys
|
||||
if(hs->pEncodingKeys == NULL || hs->EncodingKeys == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int LoadBuildConfiguration(TCascStorage * hs)
|
||||
{
|
||||
QUERY_KEY InfoFile = {NULL, 0};
|
||||
QUERY_KEY FileData = {NULL, 0};
|
||||
TCHAR * szAgentFile;
|
||||
TCHAR * szInfoFile;
|
||||
bool bBuildConfigComplete = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Since HOTS build 30027, the game uses build.info file for storage info
|
||||
if(bBuildConfigComplete == false)
|
||||
{
|
||||
szInfoFile = CombinePath(hs->szRootPath, _T(".build.info"));
|
||||
if(szInfoFile != NULL)
|
||||
{
|
||||
nError = LoadTextFile(szInfoFile, &InfoFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Parse the info file
|
||||
nError = ParseInfoFile(hs, &InfoFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
bBuildConfigComplete = true;
|
||||
|
||||
// Free the loaded blob
|
||||
FreeCascBlob(&InfoFile);
|
||||
}
|
||||
|
||||
CASC_FREE(szInfoFile);
|
||||
}
|
||||
}
|
||||
|
||||
// If the info file has not been loaded, try the legacy .build.db
|
||||
if(bBuildConfigComplete == false)
|
||||
{
|
||||
szAgentFile = CombinePath(hs->szRootPath, _T(".build.db"));
|
||||
if(szAgentFile != NULL)
|
||||
{
|
||||
nError = LoadTextFile(szAgentFile, &FileData);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = ParseAgentFile(hs, &FileData);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
bBuildConfigComplete = true;
|
||||
|
||||
FreeCascBlob(&FileData);
|
||||
}
|
||||
CASC_FREE(szAgentFile);
|
||||
}
|
||||
}
|
||||
|
||||
// If the .build.info and .build.db file hasn't been loaded,
|
||||
if(nError == ERROR_SUCCESS && bBuildConfigComplete == false)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Load the configuration file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey, &FileData);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = LoadCdnConfigFile(hs, &FileData);
|
||||
FreeCascBlob(&FileData);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the build file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey, &FileData);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = LoadCdnBuildFile(hs, &FileData);
|
||||
FreeCascBlob(&FileData);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the index directory
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// First, check for more common "data" subdirectory
|
||||
if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
|
||||
if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
67
dep/CascLib/src/CascCommon.cpp
Normal file
67
dep/CascLib/src/CascCommon.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/*****************************************************************************/
|
||||
/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Conversion of big-endian to integer
|
||||
|
||||
// Read the 24-bit big-endian offset into ULONGLONG
|
||||
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;
|
||||
}
|
||||
|
||||
// Read the 32-bit big-endian offset into ULONGLONG
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
374
dep/CascLib/src/CascCommon.h
Normal file
374
dep/CascLib/src/CascCommon.h
Normal file
@@ -0,0 +1,374 @@
|
||||
/*****************************************************************************/
|
||||
/* CascCommon.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascCommon.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __CASCCOMMON_H__
|
||||
#define __CASCCOMMON_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compression support
|
||||
|
||||
// Include functions from zlib
|
||||
#ifndef __SYS_ZLIB
|
||||
#include "zlib/zlib.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "CascPort.h"
|
||||
#include "common/Common.h"
|
||||
#include "common/FileStream.h"
|
||||
#include "common/ListFile.h"
|
||||
#include "common/Map.h"
|
||||
|
||||
// Headers from LibTomCrypt
|
||||
#include "libtomcrypt/src/headers/tomcrypt.h"
|
||||
|
||||
// For HashStringJenkins
|
||||
#include "jenkins/lookup.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CascLib private defines
|
||||
|
||||
#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm
|
||||
#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor
|
||||
#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID
|
||||
|
||||
#define CASC_INDEX_COUNT 0x10
|
||||
#define CASC_FILE_KEY_SIZE 0x09 // Size of the file key
|
||||
#define CASC_MAX_DATA_FILES 0x100
|
||||
#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported
|
||||
#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX'
|
||||
|
||||
#define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name
|
||||
|
||||
// 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))
|
||||
|
||||
#define CASC_PACKAGE_BUFFER 0x1000
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
struct TFileStream;
|
||||
struct _MAR_FILE;
|
||||
|
||||
typedef struct _CASC_INDEX_ENTRY
|
||||
{
|
||||
BYTE IndexKey[CASC_FILE_KEY_SIZE]; // The first 9 bytes of the encoding key
|
||||
BYTE FileOffset[5]; //
|
||||
BYTE FileSize[4];
|
||||
} CASC_INDEX_ENTRY, *PCASC_INDEX_ENTRY;
|
||||
|
||||
typedef struct _CASC_MAPPING_TABLE
|
||||
{
|
||||
TCHAR * szFileName; // Name of the key mapping file
|
||||
LPBYTE pbFileData; // Pointer to the file data
|
||||
DWORD cbFileData; // Length of the file data
|
||||
BYTE ExtraBytes; // (?) Extra bytes in the key record
|
||||
BYTE SpanSizeBytes; // Size of field with file size
|
||||
BYTE SpanOffsBytes; // Size of field with file offset
|
||||
BYTE KeyBytes; // Size of the file key
|
||||
BYTE SegmentBits; // Number of bits for the file offset (rest is archive index)
|
||||
ULONGLONG MaxFileOffset;
|
||||
|
||||
PCASC_INDEX_ENTRY pIndexEntries; // Sorted array of index entries
|
||||
DWORD nIndexEntries; // Number of index entries
|
||||
|
||||
} CASC_MAPPING_TABLE, *PCASC_MAPPING_TABLE;
|
||||
|
||||
typedef struct _CASC_FILE_FRAME
|
||||
{
|
||||
DWORD FrameArchiveOffset; // Archive file pointer corresponding to the begin of the frame
|
||||
DWORD FrameFileOffset; // File pointer corresponding to the begin of the frame
|
||||
DWORD CompressedSize; // Compressed size of the file
|
||||
DWORD FrameSize; // Size of the frame
|
||||
BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector
|
||||
} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
|
||||
|
||||
typedef struct _CASC_ENCODING_HEADER
|
||||
{
|
||||
BYTE Magic[2]; // "EN"
|
||||
BYTE field_2;
|
||||
BYTE field_3;
|
||||
BYTE field_4;
|
||||
BYTE field_5[2];
|
||||
BYTE field_7[2];
|
||||
BYTE NumSegments[4]; // Number of entries (big endian)
|
||||
BYTE field_D[4];
|
||||
BYTE field_11;
|
||||
BYTE SegmentsPos[4]; // Offset of encoding segments
|
||||
|
||||
} CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER;
|
||||
|
||||
typedef struct _CASC_ENCODING_ENTRY
|
||||
{
|
||||
USHORT KeyCount; // Number of subitems
|
||||
BYTE FileSizeBytes[4]; // File size as bytes (big-endian)
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
|
||||
|
||||
// Followed by the index keys
|
||||
// (number of items = KeyCount)
|
||||
// Followed by the index keys (number of items = KeyCount)
|
||||
} CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY;
|
||||
|
||||
typedef struct _CASC_ROOT_LOCALE_BLOCK
|
||||
{
|
||||
DWORD NumberOfFiles; // Number of entries
|
||||
DWORD Flags;
|
||||
DWORD FileLocales; // File locales (CASC_LOCALE_XXX)
|
||||
|
||||
// Followed by a block of 32-bit integers (count: NumberOfFiles)
|
||||
// Followed by the MD5 and file name hash (count: NumberOfFiles)
|
||||
|
||||
} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK;
|
||||
|
||||
typedef struct _CASC_ROOT_ENTRY
|
||||
{
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key (MD5)
|
||||
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
||||
DWORD Locales; // File locales (see CASC_LOCALE_XXX)
|
||||
DWORD Flags; // File flags
|
||||
|
||||
} CASC_ROOT_ENTRY, *PCASC_ROOT_ENTRY;
|
||||
|
||||
typedef struct _CASC_ROOT_KEY_INFO
|
||||
{
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file, obtained from root info
|
||||
ULONGLONG FileSize; // Size of the file, in bytes
|
||||
BYTE Flags; // File flags
|
||||
} CASC_ROOT_KEY_INFO, *PCASC_ROOT_KEY_INFO;
|
||||
|
||||
typedef struct _CASC_MNDX_ENTRY
|
||||
{
|
||||
DWORD Flags; // High 8 bits: Flags, low 24 bits: package index
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file
|
||||
DWORD FileSize; // Size of the file, in bytes
|
||||
|
||||
} CASC_MNDX_ENTRY, *PCASC_MNDX_ENTRY;
|
||||
|
||||
typedef struct _CASC_MNDX_INFO
|
||||
{
|
||||
bool bRootFileLoaded; // true if the root info file was properly loaded
|
||||
BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file
|
||||
DWORD HeaderVersion; // Must be <= 2
|
||||
DWORD FormatVersion;
|
||||
DWORD field_1C;
|
||||
DWORD field_20;
|
||||
DWORD MarInfoOffset; // Offset of the first MAR entry info
|
||||
DWORD MarInfoCount; // Number of the MAR info entries
|
||||
DWORD MarInfoSize; // Size of the MAR info entry
|
||||
DWORD MndxEntriesOffset;
|
||||
DWORD MndxEntriesTotal; // Total number of MNDX root entries
|
||||
DWORD MndxEntriesValid; // Number of valid MNDX root entries
|
||||
DWORD MndxEntrySize; // Size of one MNDX root entry
|
||||
struct _MAR_FILE * pMarFile1; // File name list for the packages
|
||||
struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names
|
||||
struct _MAR_FILE * pMarFile3; // File name list for complete names
|
||||
PCASC_MNDX_ENTRY pMndxEntries;
|
||||
PCASC_MNDX_ENTRY * ppValidEntries;
|
||||
|
||||
} CASC_MNDX_INFO, *PCASC_MNDX_INFO;
|
||||
|
||||
typedef struct _CASC_PACKAGE
|
||||
{
|
||||
char * szFileName; // Pointer to file name
|
||||
size_t nLength; // Length of the file name
|
||||
|
||||
} CASC_PACKAGE, *PCASC_PACKAGE;
|
||||
|
||||
typedef struct _CASC_PACKAGES
|
||||
{
|
||||
char * szNameBuffer; // Pointer to the buffer for file names
|
||||
size_t NameEntries; // Number of name entries in Names
|
||||
size_t NameBufferUsed; // Number of bytes used in the name buffer
|
||||
size_t NameBufferMax; // Total size of the name buffer
|
||||
|
||||
CASC_PACKAGE Packages[1]; // List of packages
|
||||
|
||||
} CASC_PACKAGES, *PCASC_PACKAGES;
|
||||
|
||||
typedef struct _TCascStorage
|
||||
{
|
||||
const char * szClassName; // "TCascStorage"
|
||||
const TCHAR * szIndexFormat; // Format of the index file name
|
||||
TCHAR * szRootPath; // This is the game directory
|
||||
TCHAR * szDataPath; // This is the directory where data files are
|
||||
TCHAR * szIndexPath; // This is the directory where index files are
|
||||
TCHAR * szUrlPath; // URL to the Blizzard servers
|
||||
DWORD dwRefCount; // Number of references
|
||||
DWORD dwGameInfo; // Game type
|
||||
DWORD dwBuildNumber; // Game build number
|
||||
|
||||
QUERY_KEY CdnConfigKey;
|
||||
QUERY_KEY CdnBuildKey;
|
||||
|
||||
PQUERY_KEY pArchiveArray; // Array of the archives
|
||||
QUERY_KEY ArchiveGroup; // Name of the group archive file
|
||||
DWORD ArchiveCount; // Number of archives in the array
|
||||
|
||||
PQUERY_KEY pPatchArchiveArray; // Array of the patch archives
|
||||
QUERY_KEY PatchArchiveGroup; // Name of the patch group archive file
|
||||
DWORD PatchArchiveCount; // Number of patch archives in the array
|
||||
|
||||
QUERY_KEY RootKey;
|
||||
QUERY_KEY PatchKey;
|
||||
QUERY_KEY DownloadKey;
|
||||
QUERY_KEY InstallKey;
|
||||
|
||||
PQUERY_KEY pEncodingKeys;
|
||||
QUERY_KEY EncodingKey;
|
||||
QUERY_KEY EncodingEKey;
|
||||
DWORD EncodingKeys;
|
||||
|
||||
TFileStream * DataFileArray[CASC_MAX_DATA_FILES]; // Data file handles
|
||||
|
||||
CASC_MAPPING_TABLE KeyMapping[CASC_INDEX_COUNT]; // Key mapping
|
||||
PCASC_MAP pIndexEntryMap; // Map of index entries
|
||||
|
||||
PCASC_ENCODING_HEADER pEncodingHeader; // The encoding file
|
||||
PCASC_ENCODING_ENTRY * ppEncodingEntries; // Map of encoding entries
|
||||
size_t nEncodingEntries;
|
||||
|
||||
PCASC_ROOT_ENTRY * ppRootEntries; // Sorted array of root entries
|
||||
PCASC_ROOT_ENTRY pRootEntries; // Linear array of root entries
|
||||
size_t nRootEntries; // Number of root entries
|
||||
|
||||
PCASC_MNDX_INFO pMndxInfo; // Used for storages which have MNDX/MAR file
|
||||
PCASC_PACKAGES pPackages; // Linear list of present packages
|
||||
|
||||
} TCascStorage;
|
||||
|
||||
typedef struct _TCascFile
|
||||
{
|
||||
TCascStorage * hs; // Pointer to storage structure
|
||||
TFileStream * pStream; // An open data stream
|
||||
const char * szClassName; // "TCascFile"
|
||||
|
||||
DWORD FilePointer; // Current file pointer
|
||||
|
||||
DWORD ArchiveIndex; // Index of the archive (data.###)
|
||||
DWORD HeaderOffset; // Offset of the BLTE header, relative to the begin of the archive
|
||||
DWORD FramesOffset; // Offset of the frame data, relative to the begin of the archive
|
||||
DWORD CompressedSize; // Compressed size of the file (in bytes)
|
||||
DWORD FileSize; // Size of file, in bytes
|
||||
|
||||
PCASC_FILE_FRAME pFrames; // Array of file frames
|
||||
DWORD FrameCount; // Number of the file frames
|
||||
|
||||
LPBYTE pbFileCache; // Pointer to file cache
|
||||
DWORD cbFileCache; // Size of the file cache
|
||||
DWORD CacheStart; // Starting offset in the cache
|
||||
DWORD CacheEnd; // Ending offset in the cache
|
||||
|
||||
} TCascFile;
|
||||
|
||||
class TMndxFindResult;
|
||||
|
||||
typedef struct _TCascSearch
|
||||
{
|
||||
TCascStorage * hs; // Pointer to the storage handle
|
||||
const char * szClassName;
|
||||
TCHAR * szListFile;
|
||||
void * pCache; // Listfile cache
|
||||
TMndxFindResult * pStruct1C; // Search structure for MNDX info
|
||||
char * szMask;
|
||||
char szFileName[MAX_PATH]; // Buffer for the file name
|
||||
char szNormName[MAX_PATH]; // Buffer for normalized file name
|
||||
ULONGLONG FileNameHash; // Name hash being searched right now
|
||||
size_t RootIndex; // Root index of the previously found item
|
||||
DWORD dwState; // Pointer to the state (0 = listfile, 1 = nameless, 2 = done)
|
||||
BYTE BitArray[1]; // Bit array of already-reported files
|
||||
|
||||
} TCascSearch;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 doesn't have to test the pointer to NULL
|
||||
//
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
/*
|
||||
void * DbgRealloc(void * ptr, size_t nSize);
|
||||
|
||||
#define CASC_REALLOC(type, ptr, count) (type *)DbgRealloc(ptr, ((count) * sizeof(type)))
|
||||
#define CASC_ALLOC(type, count) (type *)HeapAlloc(GetProcessHeap(), 0, ((count) * sizeof(type)))
|
||||
#define CASC_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
|
||||
*/
|
||||
|
||||
#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
|
||||
#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
|
||||
#define CASC_FREE(ptr) free(ptr)
|
||||
|
||||
#else
|
||||
|
||||
#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
|
||||
#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
|
||||
#define CASC_FREE(ptr) free(ptr)
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Big endian number manipulation
|
||||
|
||||
DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes);
|
||||
DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes);
|
||||
DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes);
|
||||
ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Build configuration reading
|
||||
|
||||
int LoadBuildConfiguration(TCascStorage * hs);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internal file functions
|
||||
|
||||
TCascStorage * IsValidStorageHandle(HANDLE hStorage);
|
||||
TCascFile * IsValidFileHandle(HANDLE hFile);
|
||||
|
||||
PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex);
|
||||
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex);
|
||||
PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey);
|
||||
|
||||
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dump data
|
||||
|
||||
#ifdef _DEBUG
|
||||
void CascDumpSparseArray(const char * szFileName, void * pvSparseArray);
|
||||
void CascDumpNameFragTable(const char * szFileName, void * pvMarFile);
|
||||
void CascDumpFileNames(const char * szFileName, void * pvMarFile);
|
||||
void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo);
|
||||
void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs);
|
||||
void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile);
|
||||
void CascDumpFile(const char * szFileName, HANDLE hFile);
|
||||
#else // _DEBUG
|
||||
#define CascDumpSparseArray(n,a) /* */
|
||||
#define CascDumpNameFragTable(n, m) /* */
|
||||
#define CascDumpFileNames(n, m) /* */
|
||||
#define CascDumpMndxRoot(n,i) /* */
|
||||
#define CascDumpIndexEntries(n,h) /* */
|
||||
#define CascDumpStorage(n,h) /* */
|
||||
#define CascDumpFile(n,h) /* */
|
||||
#endif // _DEBUG
|
||||
|
||||
#endif // __CASCCOMMON_H__
|
||||
83
dep/CascLib/src/CascDecompress.cpp
Normal file
83
dep/CascLib/src/CascDecompress.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*****************************************************************************/
|
||||
/* CascDecompress.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Decompression functions */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 02.05.14 1.00 Lad The first version of CascDecompress.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
z_stream z; // Stream information for zlib
|
||||
int nResult;
|
||||
|
||||
// Fill the stream structure for zlib
|
||||
z.next_in = pbInBuffer;
|
||||
z.avail_in = cbInBuffer;
|
||||
z.total_in = cbInBuffer;
|
||||
z.next_out = pbOutBuffer;
|
||||
z.avail_out = *pcbOutBuffer;
|
||||
z.total_out = 0;
|
||||
z.zalloc = NULL;
|
||||
z.zfree = NULL;
|
||||
|
||||
// Initialize the decompression structure
|
||||
if((nResult = inflateInit(&z)) == Z_OK)
|
||||
{
|
||||
// Call zlib to decompress the data
|
||||
nResult = inflate(&z, Z_NO_FLUSH);
|
||||
inflateEnd(&z);
|
||||
|
||||
// Give the size of the uncompressed data
|
||||
*pcbOutBuffer = z.total_out;
|
||||
}
|
||||
|
||||
// Return an error code
|
||||
return (nResult == Z_OK || nResult == Z_STREAM_END) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
LPBYTE pbOutBuffer = (LPBYTE)pvOutBuffer;
|
||||
LPBYTE pbInBuffer = (LPBYTE)pvInBuffer;
|
||||
DWORD cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
|
||||
BYTE uCompression; // Decompressions applied to the data
|
||||
|
||||
// Verify buffer sizes
|
||||
if(cbInBuffer <= 1)
|
||||
return 0;
|
||||
|
||||
// Get applied compression types and decrement data length
|
||||
uCompression = *pbInBuffer++;
|
||||
cbInBuffer--;
|
||||
|
||||
// Perform the decompressions
|
||||
switch(uCompression)
|
||||
{
|
||||
case 'N': // Uncompressed
|
||||
|
||||
assert(cbOutBuffer == cbInBuffer);
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
*pcbOutBuffer = cbOutBuffer;
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case 'Z': // ZLIB
|
||||
|
||||
return Decompress_ZLIB(pbOutBuffer, pcbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
524
dep/CascLib/src/CascDumpData.cpp
Normal file
524
dep/CascLib/src/CascDumpData.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/*****************************************************************************/
|
||||
/* CascDumpData.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System-dependent directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 07.05.14 1.00 Lad The first version of CascDumpData.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
#ifdef _DEBUG // The entire file is only valid for debug purposes
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static char * StringFromIndexKey(LPBYTE md5, char * szBuffer)
|
||||
{
|
||||
return StringFromBinary(md5, 9, szBuffer);
|
||||
}
|
||||
|
||||
static char * StringFromMD5(LPBYTE md5, char * szBuffer)
|
||||
{
|
||||
return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
|
||||
}
|
||||
|
||||
static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, const void * pvIndexEntry2)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry1 = (PCASC_INDEX_ENTRY)pvIndexEntry1;
|
||||
PCASC_INDEX_ENTRY pIndexEntry2 = (PCASC_INDEX_ENTRY)pvIndexEntry2;
|
||||
ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffset);
|
||||
ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffset);
|
||||
DWORD ArchIndex1 = (DWORD)(FileOffset1 >> 0x1E);
|
||||
DWORD ArchIndex2 = (DWORD)(FileOffset2 >> 0x1E);
|
||||
|
||||
// First, compare the archive index
|
||||
if(ArchIndex1 < ArchIndex2)
|
||||
return -1;
|
||||
if(ArchIndex1 > ArchIndex2)
|
||||
return +1;
|
||||
|
||||
// Second, compare the archive offset
|
||||
FileOffset1 &= 0x3FFFFFFF;
|
||||
FileOffset2 &= 0x3FFFFFFF;
|
||||
if(FileOffset1 < FileOffset2)
|
||||
return -1;
|
||||
if(FileOffset1 > FileOffset2)
|
||||
return +1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char ** CreateFileNameArray(TCascStorage * hs, const TCHAR * szListFile)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
char ** FileNameArray = NULL;
|
||||
void * pvListFile;
|
||||
size_t nRootIndex;
|
||||
char szFileName1[MAX_PATH+1];
|
||||
char szFileName2[MAX_PATH+1];
|
||||
|
||||
// Open the listfile stream and initialize the listfile cache
|
||||
pvListFile = ListFile_OpenExternal(szListFile);
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
// Allocate the array of file names
|
||||
FileNameArray = CASC_ALLOC(char*, hs->nRootEntries);
|
||||
if(FileNameArray != NULL)
|
||||
{
|
||||
// Zero the name array
|
||||
memset(FileNameArray, 0, hs->nRootEntries * sizeof(char *));
|
||||
|
||||
// Perform search
|
||||
while(ListFile_GetNext(pvListFile, "*", szFileName1, MAX_PATH))
|
||||
{
|
||||
// Create normalized name
|
||||
strcpy(szFileName2, szFileName1);
|
||||
NormalizeFileName_UpperBkSlash(szFileName2);
|
||||
|
||||
// Try to find the root entry
|
||||
pRootEntry = FindFirstRootEntry(hs, szFileName2, &nRootIndex);
|
||||
if(pRootEntry != NULL)
|
||||
{
|
||||
assert(nRootIndex < hs->nRootEntries);
|
||||
if(FileNameArray[nRootIndex] == NULL)
|
||||
FileNameArray[nRootIndex] = NewStr(szFileName1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the listfile cache
|
||||
ListFile_Free(pvListFile);
|
||||
}
|
||||
|
||||
return FileNameArray;
|
||||
}
|
||||
|
||||
static void FreeFileNameArray(TCascStorage * hs, char ** FileNameArray)
|
||||
{
|
||||
if(FileNameArray != NULL)
|
||||
{
|
||||
// Free all sub-entries
|
||||
for(size_t i = 0; i < hs->nRootEntries; i++)
|
||||
{
|
||||
if(FileNameArray[i] != NULL)
|
||||
CASC_FREE(FileNameArray[i]);
|
||||
}
|
||||
|
||||
// Free the array itself
|
||||
CASC_FREE(FileNameArray);
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpIndexKey(
|
||||
FILE * fp,
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbEncodingKey,
|
||||
PCASC_INDEX_ENTRY * ppIndexEntries)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
QUERY_KEY QueryKey;
|
||||
size_t EntryIndex = 0;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
QueryKey.pbData = pbEncodingKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &QueryKey);
|
||||
if(pIndexEntry != NULL)
|
||||
{
|
||||
ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
|
||||
DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
|
||||
DWORD FileSize = *(PDWORD)pIndexEntry->FileSize;
|
||||
|
||||
// Mark the index entry as dumped
|
||||
ppIndexEntries[EntryIndex] = NULL;
|
||||
|
||||
// Mask the file offset
|
||||
FileOffset &= 0x3FFFFFFF;
|
||||
fprintf(fp, " Index: %s, ArchIdx: %02x FileOffset %08x FileSize: %lx\n",
|
||||
StringFromIndexKey(pIndexEntry->IndexKey, szMd5),
|
||||
ArchIndex,
|
||||
(DWORD)FileOffset,
|
||||
FileSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(fp, " NO INDEX ENTRY\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpEncodingEntry(
|
||||
FILE * fp,
|
||||
TCascStorage * hs,
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry,
|
||||
PCASC_INDEX_ENTRY * ppIndexEntries)
|
||||
{
|
||||
LPBYTE pbEncodingKey;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
fprintf(fp, " Encoding Key: %s Key Count: %u Size: %lx\n",
|
||||
StringFromMD5(pEncodingEntry->EncodingKey, szMd5),
|
||||
pEncodingEntry->KeyCount,
|
||||
ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes));
|
||||
|
||||
// If there is a file key
|
||||
if(pEncodingEntry->KeyCount != 0)
|
||||
{
|
||||
// Get the first encoding key
|
||||
pbEncodingKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
|
||||
// Dump all encoding keys
|
||||
for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++)
|
||||
{
|
||||
fprintf(fp, " Index Key: %s\n", StringFromMD5(pbEncodingKey, szMd5));
|
||||
DumpIndexKey(fp, hs, pbEncodingKey, ppIndexEntries);
|
||||
pbEncodingKey += MD5_HASH_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(fp, " ZERO FILE KEYS\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpEncodingEntry(
|
||||
FILE * fp,
|
||||
TCascStorage * hs,
|
||||
PCASC_ROOT_ENTRY pRootEntry,
|
||||
PCASC_ENCODING_ENTRY * ppEncodingKeys,
|
||||
PCASC_INDEX_ENTRY * ppIndexEntries)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingKey;
|
||||
QUERY_KEY QueryKey;
|
||||
size_t EntryIndex = 0;
|
||||
|
||||
// Find the encoding key
|
||||
QueryKey.pbData = pRootEntry->EncodingKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingKey = FindEncodingEntry(hs, &QueryKey, &EntryIndex);
|
||||
if(pEncodingKey == NULL)
|
||||
{
|
||||
fprintf(fp, " NO ENCODING KEY\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the file key, clear the encoding key
|
||||
ppEncodingKeys[EntryIndex] = NULL;
|
||||
DumpEncodingEntry(fp, hs, pEncodingKey, ppIndexEntries);
|
||||
}
|
||||
|
||||
static void DumpRootEntries(FILE * fp, TCascStorage * hs, char ** FileNameArray)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY * ppEncodingEntries; // Array of encoding entries
|
||||
PCASC_INDEX_ENTRY * ppIndexEntries; // Complete list of key entries for all files
|
||||
const char * szFileName = NULL;
|
||||
ULONGLONG PrevNameHash = (ULONGLONG)-1;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
// Create copy of the encoding keys and file keys
|
||||
ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, hs->nEncodingEntries);
|
||||
ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, hs->pIndexEntryMap->ItemCount);
|
||||
if(ppEncodingEntries && ppIndexEntries)
|
||||
{
|
||||
// Copy all pointers
|
||||
memcpy(ppEncodingEntries, hs->ppEncodingEntries, hs->nEncodingEntries * sizeof(PCASC_ENCODING_ENTRY));
|
||||
Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
|
||||
|
||||
// Parse all entries
|
||||
for(size_t i = 0; i < hs->nRootEntries; i++)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry = hs->ppRootEntries[i];
|
||||
const char * szDuplicate = "";
|
||||
|
||||
// Check duplicates
|
||||
if(pRootEntry->FileNameHash != PrevNameHash)
|
||||
szFileName = FileNameArray[i];
|
||||
else
|
||||
szDuplicate = "(DUPLICATE) ";
|
||||
|
||||
// Dump the root entry
|
||||
fprintf(fp, "NameHash: %016llx Locales: %08lx MD5: %s %sFileName: %s\n",
|
||||
pRootEntry->FileNameHash,
|
||||
pRootEntry->Locales,
|
||||
StringFromMD5(pRootEntry->EncodingKey, szMd5),
|
||||
szDuplicate,
|
||||
szFileName);
|
||||
|
||||
DumpEncodingEntry(fp, hs, pRootEntry, ppEncodingEntries, ppIndexEntries);
|
||||
PrevNameHash = pRootEntry->FileNameHash;
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
// Dump all orphaned encoding keys
|
||||
for(size_t i = 0; i < hs->nEncodingEntries; i++)
|
||||
{
|
||||
if(ppEncodingEntries[i] != NULL)
|
||||
{
|
||||
fprintf(fp, "[NO ROOT KEY]\n");
|
||||
|
||||
DumpEncodingEntry(fp, hs, ppEncodingEntries[i], ppIndexEntries);
|
||||
ppEncodingEntries[i] = NULL;
|
||||
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CASC_FREE(ppIndexEntries);
|
||||
CASC_FREE(ppEncodingEntries);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
void CascDumpSparseArray(const char * szFileName, void * pvSparseArray)
|
||||
{
|
||||
TSparseArray * pSparseArray = (TSparseArray *)pvSparseArray;
|
||||
FILE * fp;
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
// Write header
|
||||
fprintf(fp, "## Value\n-- -----\n");
|
||||
|
||||
// Write the values
|
||||
for(DWORD i = 0; i < pSparseArray->TotalItemCount; i++)
|
||||
{
|
||||
DWORD Value = 0;
|
||||
|
||||
if(pSparseArray->IsItemPresent(i))
|
||||
{
|
||||
Value = pSparseArray->GetItemValue(i);
|
||||
fprintf(fp, "%02X %02X\n", i, Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(fp, "%02X --\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpNameFragTable(const char * szFileName, void * pMarFile)
|
||||
{
|
||||
TFileNameDatabase * pDB = ((PMAR_FILE)pMarFile)->pDatabasePtr->pDB;
|
||||
FILE * fp;
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
PNAME_FRAG pNameTable = pDB->NameFragTable.NameFragArray;
|
||||
const char * szNames = pDB->IndexStruct_174.NameFragments.CharArray;
|
||||
const char * szLastEntry;
|
||||
char szMatchType[0x100];
|
||||
|
||||
// Dump the table header
|
||||
fprintf(fp, "Indx ThisHash NextHash FragOffs\n");
|
||||
fprintf(fp, "---- -------- -------- --------\n");
|
||||
|
||||
// Dump all name entries
|
||||
for(DWORD i = 0; i < pDB->NameFragTable.ItemCount; i++)
|
||||
{
|
||||
// Reset both match types
|
||||
szMatchType[0] = 0;
|
||||
szLastEntry = "";
|
||||
|
||||
// Only if the table entry is not empty
|
||||
if(pNameTable->ItemIndex != 0xFFFFFFFF)
|
||||
{
|
||||
// Prepare the entry
|
||||
if(IS_SINGLE_CHAR_MATCH(pDB->NameFragTable, i))
|
||||
sprintf(szMatchType, "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF));
|
||||
else
|
||||
sprintf(szMatchType, "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs);
|
||||
}
|
||||
|
||||
// Dump the entry
|
||||
fprintf(fp, "0x%02X %08x %08x %08x %s%s\n", i, pNameTable->ItemIndex,
|
||||
pNameTable->NextIndex,
|
||||
pNameTable->FragOffs,
|
||||
szMatchType,
|
||||
szLastEntry);
|
||||
pNameTable++;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpFileNames(const char * szFileName, void * pvMarFile)
|
||||
{
|
||||
TMndxFindResult Struct1C;
|
||||
PMAR_FILE pMarFile = (PMAR_FILE)pvMarFile;
|
||||
FILE * fp;
|
||||
char szNameBuff[0x200];
|
||||
bool bFindResult;
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
// Set an empty path as search mask (?)
|
||||
Struct1C.SetSearchPath("", 0);
|
||||
|
||||
// Keep searching
|
||||
for(;;)
|
||||
{
|
||||
// Search the next file name
|
||||
pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult);
|
||||
|
||||
// Stop the search in case of failure
|
||||
if(!bFindResult)
|
||||
break;
|
||||
|
||||
// Printf the found file name
|
||||
memcpy(szNameBuff, Struct1C.szFoundPath, Struct1C.cchFoundPath);
|
||||
szNameBuff[Struct1C.cchFoundPath] = 0;
|
||||
fprintf(fp, "%s\n", szNameBuff);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Free the search structures
|
||||
Struct1C.FreeStruct40();
|
||||
}
|
||||
|
||||
void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo)
|
||||
{
|
||||
PCASC_MNDX_ENTRY pMndxEntry;
|
||||
FILE * fp;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n");
|
||||
for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++)
|
||||
{
|
||||
pMndxEntry = pMndxInfo->ppValidEntries[i];
|
||||
|
||||
fprintf(fp, "%04X %08X %s %08X\n", i,
|
||||
pMndxEntry->Flags,
|
||||
StringFromMD5(pMndxEntry->EncodingKey, szMd5),
|
||||
pMndxEntry->FileSize);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
|
||||
{
|
||||
PCASC_INDEX_ENTRY * ppIndexEntries;
|
||||
FILE * fp;
|
||||
size_t nIndexEntries = hs->pIndexEntryMap->ItemCount;
|
||||
char szIndexKey[0x40];
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
// Create linear aray
|
||||
ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, nIndexEntries);
|
||||
if(ppIndexEntries != NULL)
|
||||
{
|
||||
// Obtain the linear array of index entries
|
||||
Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
|
||||
|
||||
// Sort the array by archive number and archive offset
|
||||
qsort_pointer_array((void **)ppIndexEntries, nIndexEntries, CompareIndexEntries_FilePos, NULL);
|
||||
|
||||
// Parse the array
|
||||
fprintf(fp, "ArNo ArOffset FileSize IndexKey\n==== ======== ======== ================================\n");
|
||||
for(size_t i = 0; i < nIndexEntries; i++)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry = ppIndexEntries[i];
|
||||
ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
|
||||
DWORD ArchIndex = (DWORD)(ArchOffset >> 0x1E);
|
||||
DWORD FileSize;
|
||||
|
||||
FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize);
|
||||
ArchOffset &= 0x3FFFFFFF;
|
||||
|
||||
fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
|
||||
}
|
||||
|
||||
CASC_FREE(ppIndexEntries);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile)
|
||||
{
|
||||
char ** FileNameArray = NULL;
|
||||
FILE * fp;
|
||||
|
||||
// Validate the storage handle
|
||||
if(hs != NULL)
|
||||
{
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
// If we also have listfile, open it
|
||||
if(szListFile != NULL)
|
||||
FileNameArray = CreateFileNameArray(hs, szListFile);
|
||||
|
||||
// Dump all root keys
|
||||
fprintf(fp, "Root Entries\n=========\n\n");
|
||||
DumpRootEntries(fp, hs, FileNameArray);
|
||||
|
||||
FreeFileNameArray(hs, FileNameArray);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CascDumpFile(const char * szFileName, HANDLE hFile)
|
||||
{
|
||||
FILE * fp;
|
||||
DWORD dwBytesRead = 1;
|
||||
DWORD dwFilePos = CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
||||
BYTE Buffer[0x1000];
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wb");
|
||||
if(fp != NULL)
|
||||
{
|
||||
// Read data as long as we can, write as long as we can
|
||||
while(dwBytesRead != 0)
|
||||
{
|
||||
// Read from the source file
|
||||
if(!CascReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead))
|
||||
break;
|
||||
|
||||
// Write to the destination file
|
||||
if(fwrite(Buffer, 1, dwBytesRead, fp) != dwBytesRead)
|
||||
break;
|
||||
}
|
||||
|
||||
// Close the local file
|
||||
fclose(fp);
|
||||
|
||||
// Restore the file pointer
|
||||
CascSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DEBUG
|
||||
364
dep/CascLib/src/CascFindFile.cpp
Normal file
364
dep/CascLib/src/CascFindFile.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/*****************************************************************************/
|
||||
/* CascFindFile.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System-dependent directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 10.05.14 1.00 Lad The first version of CascFindFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static TCascSearch * IsValidSearchHandle(HANDLE hFind)
|
||||
{
|
||||
TCascSearch * pSearch = (TCascSearch *)hFind;
|
||||
|
||||
return (pSearch != NULL && pSearch->szClassName != NULL && !strcmp(pSearch->szClassName, "TCascSearch") && pSearch->szMask != NULL) ? pSearch : NULL;
|
||||
}
|
||||
|
||||
static void FreeSearchHandle(TCascSearch * pSearch)
|
||||
{
|
||||
// Only if the storage handle is valid
|
||||
assert(pSearch != NULL);
|
||||
|
||||
// Close (dereference) the archive handle
|
||||
if(pSearch->hs != NULL)
|
||||
CascCloseStorage((HANDLE)pSearch->hs);
|
||||
pSearch->hs = NULL;
|
||||
|
||||
// Free the file cache and frame array
|
||||
if(pSearch->szMask != NULL)
|
||||
CASC_FREE(pSearch->szMask);
|
||||
if(pSearch->szListFile != NULL)
|
||||
CASC_FREE(pSearch->szListFile);
|
||||
if(pSearch->pStruct1C != NULL)
|
||||
delete pSearch->pStruct1C;
|
||||
if(pSearch->pCache != NULL)
|
||||
ListFile_Free(pSearch->pCache);
|
||||
|
||||
// Free the structure itself
|
||||
pSearch->szClassName = NULL;
|
||||
CASC_FREE(pSearch);
|
||||
}
|
||||
/*
|
||||
DWORD dwRootEntries = 0;
|
||||
DWORD dwEncoEntries = 0;
|
||||
DWORD dwIndexEntries = 0;
|
||||
*/
|
||||
static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry, PCASC_FIND_DATA pFindData, size_t nRootIndex)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
QUERY_KEY QueryKey;
|
||||
DWORD dwByteIndex = (DWORD)(nRootIndex / 0x08);
|
||||
DWORD dwBitIndex = (DWORD)(nRootIndex & 0x07);
|
||||
|
||||
// First of all, check if that entry has been reported before
|
||||
// If the bit is set, then the file has already been reported
|
||||
// by a previous search iteration
|
||||
if(pSearch->BitArray[dwByteIndex] & (1 << dwBitIndex))
|
||||
return false;
|
||||
pSearch->BitArray[dwByteIndex] |= (1 << dwBitIndex);
|
||||
|
||||
// Increment the number of root entries
|
||||
// dwRootEntries++;
|
||||
|
||||
// Now try to find that encoding key in the array of encoding keys
|
||||
QueryKey.pbData = pRootEntry->EncodingKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL);
|
||||
if(pEncodingEntry == NULL)
|
||||
return false;
|
||||
|
||||
// dwEncoEntries++;
|
||||
|
||||
// Now try to find the index entry. Note that we take the first key
|
||||
QueryKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &QueryKey);
|
||||
if(pIndexEntry == NULL)
|
||||
return false;
|
||||
|
||||
// dwIndexEntries++;
|
||||
|
||||
// Fill the name hash and the MD5
|
||||
memcpy(pFindData->EncodingKey, pRootEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
pFindData->FileNameHash = pRootEntry->FileNameHash;
|
||||
pFindData->dwPackageIndex = 0;
|
||||
pFindData->dwLocaleFlags = pRootEntry->Locales;
|
||||
pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask)
|
||||
{
|
||||
TCascSearch * pSearch;
|
||||
size_t cbToAllocate;
|
||||
|
||||
// When using the MNDX info, do not allocate the extra bit array
|
||||
cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->nRootEntries / 8) : 0);
|
||||
pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pSearch != NULL)
|
||||
{
|
||||
// Initialize the structure
|
||||
memset(pSearch, 0, cbToAllocate);
|
||||
pSearch->szClassName = "TCascSearch";
|
||||
|
||||
// Save the search handle
|
||||
pSearch->hs = hs;
|
||||
hs->dwRefCount++;
|
||||
|
||||
// If the mask was not given, use default
|
||||
if(szMask == NULL)
|
||||
szMask = "*";
|
||||
|
||||
// Save the other variables
|
||||
if(szListFile != NULL)
|
||||
{
|
||||
pSearch->szListFile = NewStr(szListFile, 0);
|
||||
if(pSearch->szListFile == NULL)
|
||||
{
|
||||
FreeSearchHandle(pSearch);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the search mask
|
||||
pSearch->szMask = NewStr(szMask, 0);
|
||||
if(pSearch->szMask == NULL)
|
||||
{
|
||||
FreeSearchHandle(pSearch);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return pSearch;
|
||||
}
|
||||
|
||||
static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
size_t RootIndex;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Shall we get a new file name from the listfile?
|
||||
if(pSearch->FileNameHash == 0)
|
||||
{
|
||||
// Try to get next file from the listfile
|
||||
if(!ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
|
||||
break;
|
||||
|
||||
// if(!_stricmp(pSearch->szFileName, "Interface\\Glues\\MODELS\\UI_MainMenu_Warlords\\Caustic_MedFreq.blp"))
|
||||
// DebugBreak();
|
||||
|
||||
// Normalize the file name
|
||||
strcpy(pSearch->szNormName, pSearch->szFileName);
|
||||
NormalizeFileName_UpperBkSlash(pSearch->szNormName);
|
||||
|
||||
// Find the first root entry belonging to this file name
|
||||
pRootEntry = FindFirstRootEntry(pSearch->hs, pSearch->szNormName, &RootIndex);
|
||||
if(pRootEntry == NULL)
|
||||
continue;
|
||||
|
||||
// We have the name now, search all locales
|
||||
pSearch->FileNameHash = pRootEntry->FileNameHash;
|
||||
pSearch->RootIndex = RootIndex;
|
||||
}
|
||||
|
||||
// Is the root index in range?
|
||||
while(pSearch->RootIndex < hs->nRootEntries)
|
||||
{
|
||||
// Get the next root entry. If name mismatches, stop searching
|
||||
pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
|
||||
if(pRootEntry->FileNameHash != pSearch->FileNameHash)
|
||||
break;
|
||||
|
||||
// Verify whether the file exists in the storage
|
||||
if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex++))
|
||||
{
|
||||
strcpy(pFindData->szFileName, pSearch->szFileName);
|
||||
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the name hash and root index and retry search
|
||||
pSearch->FileNameHash = 0;
|
||||
}
|
||||
|
||||
// Listfile search ended
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
|
||||
// Check if there is more files with the same name hash
|
||||
while(pSearch->RootIndex < hs->nRootEntries)
|
||||
{
|
||||
// Get the pointer to the root entry
|
||||
pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
|
||||
|
||||
// Verify if that root entry exists in the CASC storage
|
||||
// Note that the file name will be there from the previous search
|
||||
if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex))
|
||||
{
|
||||
pFindData->szFileName[0] = 0;
|
||||
pFindData->szPlainName = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
pSearch->RootIndex++;
|
||||
}
|
||||
|
||||
// Nameless search ended
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
// Are we searching using the MNDX ?
|
||||
if(pSearch->hs->pMndxInfo != NULL)
|
||||
return DoStorageSearch_MNDX(pSearch, pFindData);
|
||||
|
||||
// State 0: No search done yet
|
||||
if(pSearch->dwState == 0)
|
||||
{
|
||||
// Does the search specify listfile?
|
||||
if(pSearch->szListFile != NULL)
|
||||
pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);
|
||||
|
||||
// Move the search phase to the listfile searching
|
||||
pSearch->FileNameHash = 0;
|
||||
pSearch->RootIndex = 0;
|
||||
pSearch->dwState++;
|
||||
|
||||
// If either file stream or listfile cache are invalid,
|
||||
// move to the next phase
|
||||
if(pSearch->pCache == NULL)
|
||||
pSearch->dwState++;
|
||||
}
|
||||
|
||||
// State 1: Searching the list file
|
||||
if(pSearch->dwState == 1)
|
||||
{
|
||||
if(DoStorageSearch_ListFile(pSearch, pFindData))
|
||||
return true;
|
||||
|
||||
// Move to the nameless search state
|
||||
pSearch->RootIndex = 0;
|
||||
pSearch->dwState++;
|
||||
}
|
||||
|
||||
// State 2: Searching the remaining entries
|
||||
if(pSearch->dwState == 2)
|
||||
{
|
||||
if(DoStorageSearch_Hash(pSearch, pFindData))
|
||||
return true;
|
||||
|
||||
// Move to the final search state
|
||||
pSearch->dwState++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
HANDLE WINAPI CascFindFirstFile(
|
||||
HANDLE hStorage,
|
||||
const char * szMask,
|
||||
PCASC_FIND_DATA pFindData,
|
||||
const TCHAR * szListFile)
|
||||
{
|
||||
TCascStorage * hs;
|
||||
TCascSearch * pSearch = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check parameters
|
||||
if((hs = IsValidStorageHandle(hStorage)) == NULL)
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szMask == NULL || pFindData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Allocate the structure for archive search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Clear the entire search structure
|
||||
memset(pFindData, 0, sizeof(CASC_FIND_DATA));
|
||||
|
||||
// We must have listfile for non-MNDX storages
|
||||
if(hs->pMndxInfo == NULL && szListFile == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate the search handle
|
||||
pSearch = AllocateSearchHandle(hs, szListFile, szMask);
|
||||
if(pSearch == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Perform search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!DoStorageSearch(pSearch, pFindData))
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
if(pSearch != NULL)
|
||||
FreeSearchHandle(pSearch);
|
||||
pSearch = NULL;
|
||||
}
|
||||
|
||||
return (HANDLE)pSearch;
|
||||
}
|
||||
|
||||
bool WINAPI CascFindNextFile(
|
||||
HANDLE hFind,
|
||||
PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
TCascSearch * pSearch;
|
||||
|
||||
pSearch = IsValidSearchHandle(hFind);
|
||||
if(pSearch == NULL || pFindData == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform search
|
||||
return DoStorageSearch(pSearch, pFindData);
|
||||
}
|
||||
|
||||
bool WINAPI CascFindClose(HANDLE hFind)
|
||||
{
|
||||
TCascSearch * pSearch;
|
||||
|
||||
pSearch = IsValidSearchHandle(hFind);
|
||||
if(pSearch == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
FreeSearchHandle(pSearch);
|
||||
return true;
|
||||
}
|
||||
171
dep/CascLib/src/CascLib.h
Normal file
171
dep/CascLib/src/CascLib.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*****************************************************************************/
|
||||
/* CascLib.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* CascLib library v 1.00 */
|
||||
/* */
|
||||
/* Author : Ladislav Zezula */
|
||||
/* E-mail : ladik@zezula.net */
|
||||
/* WWW : http://www.zezula.net */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __CASCLIB_H__
|
||||
#define __CASCLIB_H__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
|
||||
#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy'
|
||||
#endif
|
||||
|
||||
#include "CascPort.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define CASCLIB_VERSION 0x0100 // Current version of CascLib (1.0)
|
||||
#define CASCLIB_VERSION_STRING "1.00" // String version of CascLib version
|
||||
|
||||
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by encrypted stream when can't find file key
|
||||
#define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing
|
||||
|
||||
// Values for CascOpenStorage
|
||||
#define CASC_STOR_XXXXX 0x00000001 // Not used
|
||||
|
||||
// Values for CascOpenFile
|
||||
#define CASC_FILE_XXXXX 0x00000001 // Not used
|
||||
|
||||
// Flags for file stream
|
||||
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
|
||||
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
|
||||
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
|
||||
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
|
||||
|
||||
#define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping
|
||||
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
|
||||
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive
|
||||
#define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file
|
||||
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
|
||||
|
||||
#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_OPTIONS_MASK 0x0000FF00 // Mask for stream options
|
||||
|
||||
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
|
||||
#define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options)
|
||||
|
||||
#define CASC_LOCALE_ALL 0xFFFFFFFF
|
||||
#define CASC_LOCALE_NONE 0x00000000
|
||||
#define CASC_LOCALE_UNKNOWN1 0x00000001
|
||||
#define CASC_LOCALE_ENUS 0x00000002
|
||||
#define CASC_LOCALE_KOKR 0x00000004
|
||||
#define CASC_LOCALE_UNKNOWN8 0x00000008
|
||||
#define CASC_LOCALE_FRFR 0x00000010
|
||||
#define CASC_LOCALE_DEDE 0x00000020
|
||||
#define CASC_LOCALE_ZHCN 0x00000040
|
||||
#define CASC_LOCALE_ESES 0x00000080
|
||||
#define CASC_LOCALE_ZHTW 0x00000100
|
||||
#define CASC_LOCALE_ENGB 0x00000200
|
||||
#define CASC_LOCALE_ENCN 0x00000400
|
||||
#define CASC_LOCALE_ENTW 0x00000800
|
||||
#define CASC_LOCALE_ESMX 0x00001000
|
||||
#define CASC_LOCALE_RURU 0x00002000
|
||||
#define CASC_LOCALE_PTBR 0x00004000
|
||||
#define CASC_LOCALE_ITIT 0x00008000
|
||||
#define CASC_LOCALE_PTPT 0x00010000
|
||||
|
||||
#define MAX_CASC_KEY_LENGTH 0x10 // Maximum length of the key (equal to MD5 hash)
|
||||
|
||||
#ifndef MD5_HASH_SIZE
|
||||
#define MD5_HASH_SIZE 0x10
|
||||
#define MD5_STRING_SIZE 0x21
|
||||
#endif
|
||||
|
||||
#ifndef SHA1_DIGEST_SIZE
|
||||
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
|
||||
#endif
|
||||
|
||||
#ifndef LANG_NEUTRAL
|
||||
#define LANG_NEUTRAL 0x00 // Neutral locale
|
||||
#endif
|
||||
|
||||
// Return value for CascGetFileSize and CascSetFilePointer
|
||||
#define CASC_INVALID_SIZE 0xFFFFFFFF
|
||||
#define CASC_INVALID_POS 0xFFFFFFFF
|
||||
|
||||
// Flags for CascGetStorageInfo
|
||||
#define CASC_FEATURE_LISTFILE 0x00000001 // The storage supports listfile
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
typedef enum _CASC_STORAGE_INFO_CLASS
|
||||
{
|
||||
CascStorageFileCount,
|
||||
CascStorageFeatures,
|
||||
CascStorageInfoClassMax
|
||||
|
||||
} CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS;
|
||||
|
||||
|
||||
typedef struct _QUERY_KEY
|
||||
{
|
||||
LPBYTE pbData;
|
||||
DWORD cbData;
|
||||
} QUERY_KEY, *PQUERY_KEY;
|
||||
|
||||
// Structure for SFileFindFirstFile and SFileFindNextFile
|
||||
typedef struct _CASC_FIND_DATA
|
||||
{
|
||||
char szFileName[MAX_PATH]; // Full name of the found file
|
||||
char * szPlainName; // Plain name of the found file
|
||||
ULONGLONG FileNameHash; // File name hash
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key
|
||||
DWORD dwPackageIndex; // File package index (HOTS only)
|
||||
DWORD dwLocaleFlags; // Locale flags (WoW only)
|
||||
DWORD dwFileSize; // Size of the file
|
||||
|
||||
} CASC_FIND_DATA, *PCASC_FIND_DATA;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callback functions
|
||||
|
||||
typedef struct TFileStream TFileStream;
|
||||
typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// We have our own qsort implementation, optimized for sorting array of pointers
|
||||
|
||||
void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for storage manipulation
|
||||
|
||||
bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * phStorage);
|
||||
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
|
||||
bool WINAPI CascCloseStorage(HANDLE hStorage);
|
||||
|
||||
bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile);
|
||||
bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile);
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile);
|
||||
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
|
||||
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
|
||||
bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead);
|
||||
bool WINAPI CascCloseFile(HANDLE hFile);
|
||||
|
||||
HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND_DATA pFindData, const TCHAR * szListFile);
|
||||
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
|
||||
bool WINAPI CascFindClose(HANDLE hFind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __CASCLIB_H__
|
||||
3476
dep/CascLib/src/CascMndxRoot.cpp
Normal file
3476
dep/CascLib/src/CascMndxRoot.cpp
Normal file
File diff suppressed because it is too large
Load Diff
365
dep/CascLib/src/CascMndxRoot.h
Normal file
365
dep/CascLib/src/CascMndxRoot.h
Normal file
@@ -0,0 +1,365 @@
|
||||
/*****************************************************************************/
|
||||
/* CascMndxRoot.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Interface file for MNDX structures */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 15.05.14 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __CASC_MNDX_ROOT__
|
||||
#define __CASC_MNDX_ROOT__
|
||||
|
||||
class TFileNameDatabase;
|
||||
|
||||
#define CASC_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type))
|
||||
|
||||
#define CASC_SEARCH_INITIALIZING 0
|
||||
#define CASC_SEARCH_SEARCHING 2
|
||||
#define CASC_SEARCH_FINISHED 4
|
||||
|
||||
typedef struct _TRIPLET
|
||||
{
|
||||
DWORD BaseValue;
|
||||
DWORD Value2;
|
||||
DWORD Value3;
|
||||
} TRIPLET, *PTRIPLET;
|
||||
|
||||
typedef struct _NAME_FRAG
|
||||
{
|
||||
DWORD ItemIndex; // Back index to various tables
|
||||
DWORD NextIndex; // The following item index
|
||||
DWORD FragOffs; // Higher 24 bits are 0xFFFFFF00 --> A single matching character
|
||||
// Otherwise --> Offset to the name fragment table
|
||||
} NAME_FRAG, *PNAME_FRAG;
|
||||
|
||||
typedef struct _PATH_STOP
|
||||
{
|
||||
DWORD ItemIndex;
|
||||
DWORD field_4;
|
||||
DWORD field_8;
|
||||
DWORD field_C;
|
||||
DWORD field_10;
|
||||
} PATH_STOP, *PPATH_STOP;
|
||||
|
||||
typedef union _ARRAY_POINTER
|
||||
{
|
||||
LPBYTE Bytes; // Pointer to an octet
|
||||
char * Chars; // Pointer to a character
|
||||
PDWORD Uint32s; // Pointer to a DWORD
|
||||
PTRIPLET Triplets; // Pointer to TRIPLET
|
||||
PNAME_FRAG NameFrags; // Pointer to name fragment entry
|
||||
PPATH_STOP PathStopPtr; // Pointer to path checkpoint
|
||||
PULONGLONG Int64Ptr; // Pointer to 64-bit integer
|
||||
|
||||
} ARRAY_POINTER, *PARRAY_POINTER;
|
||||
|
||||
// Simple access to various tables within TGenericArray
|
||||
#define ByteArray ArrayPointer.Bytes
|
||||
#define CharArray ArrayPointer.Chars
|
||||
#define Uint32Array ArrayPointer.Uint32s
|
||||
#define TripletArray ArrayPointer.Triplets
|
||||
#define NameFragArray ArrayPointer.NameFrags
|
||||
|
||||
class TByteStream
|
||||
{
|
||||
public:
|
||||
|
||||
TByteStream();
|
||||
|
||||
void ExchangeWith(TByteStream & Target);
|
||||
int GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray);
|
||||
int SkipBytes(DWORD cbByteCount);
|
||||
int SetByteBuffer(LPBYTE pbNewMarData, DWORD cbNewMarData);
|
||||
int GetValue_DWORD(DWORD & Value);
|
||||
int GetValue_ItemCount(DWORD & NumberOfBytes, DWORD & ItemCount, DWORD ItemSize);
|
||||
int GetArray_DWORDs(PARRAY_POINTER PtrArray, DWORD ItemCount);
|
||||
int GetArray_Triplets(PARRAY_POINTER PtrArray, DWORD ItemCount);
|
||||
int GetArray_NameTable(PARRAY_POINTER PtrArray, DWORD ItemCount);
|
||||
int GetArray_BYTES(PARRAY_POINTER PtrArray, DWORD ItemCount);
|
||||
|
||||
LPBYTE pbByteData;
|
||||
void * pvMappedFile;
|
||||
DWORD cbByteData;
|
||||
DWORD field_C;
|
||||
HANDLE hFile;
|
||||
HANDLE hMap;
|
||||
};
|
||||
|
||||
class TGenericArray
|
||||
{
|
||||
public:
|
||||
|
||||
TGenericArray();
|
||||
~TGenericArray();
|
||||
|
||||
int SetArrayValid();
|
||||
|
||||
void ExchangeWith(TGenericArray & Target);
|
||||
void CopyFrom(TGenericArray & Source);
|
||||
|
||||
void SetMaxItems_CHARS(DWORD NewMaxItemCount);
|
||||
void SetMaxItems_PATH_STOP(DWORD NewMaxItemCount);
|
||||
|
||||
void InsertOneItem_CHAR(char OneChar);
|
||||
void InsertOneItem_PATH_STOP(PATH_STOP & NewItem);
|
||||
|
||||
void sub_19583A0(DWORD NewItemCount);
|
||||
|
||||
int LoadDwordsArray(TByteStream & InStream);
|
||||
int LoadTripletsArray(TByteStream & InStream);
|
||||
int LoadByteArray(TByteStream & InStream);
|
||||
int LoadFragmentInfos(TByteStream & InStream);
|
||||
int LoadStrings(TByteStream & InStream);
|
||||
|
||||
int LoadDwordsArray_Copy(TByteStream & InStream);
|
||||
int LoadTripletsArray_Copy(TByteStream & InStream);
|
||||
int LoadBytes_Copy(TByteStream & InStream);
|
||||
int LoadFragmentInfos_Copy(TByteStream & InStream);
|
||||
int LoadStringsWithCopy(TByteStream & InStream);
|
||||
|
||||
ARRAY_POINTER DataBuffer;
|
||||
ARRAY_POINTER FirstValid;
|
||||
|
||||
ARRAY_POINTER ArrayPointer;
|
||||
DWORD ItemCount; // Number of items in the array
|
||||
DWORD MaxItemCount; // Capacity of the array
|
||||
bool bIsValidArray;
|
||||
};
|
||||
|
||||
class TBitEntryArray : public TGenericArray
|
||||
{
|
||||
public:
|
||||
|
||||
TBitEntryArray();
|
||||
~TBitEntryArray();
|
||||
|
||||
DWORD GetBitEntry(DWORD EntryIndex)
|
||||
{
|
||||
DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05;
|
||||
DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F;
|
||||
DWORD dwEndBit = dwStartBit + BitsPerEntry;
|
||||
DWORD dwResult;
|
||||
|
||||
// If the end bit index is greater than 32,
|
||||
// we also need to load from the next 32-bit item
|
||||
if(dwEndBit > 0x20)
|
||||
{
|
||||
dwResult = (Uint32Array[dwItemIndex + 1] << (0x20 - dwStartBit)) | (Uint32Array[dwItemIndex] >> dwStartBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
dwResult = Uint32Array[dwItemIndex] >> dwStartBit;
|
||||
}
|
||||
|
||||
// Now we also need to mask the result by the bit mask
|
||||
return dwResult & EntryBitMask;
|
||||
}
|
||||
|
||||
void ExchangeWith(TBitEntryArray & Target);
|
||||
int LoadFromStream(TByteStream & InStream);
|
||||
int LoadFromStream_Exchange(TByteStream & InStream);
|
||||
|
||||
DWORD BitsPerEntry;
|
||||
DWORD EntryBitMask;
|
||||
DWORD TotalEntries;
|
||||
};
|
||||
|
||||
class TStruct40
|
||||
{
|
||||
public:
|
||||
|
||||
TStruct40();
|
||||
|
||||
void InitSearchBuffers();
|
||||
|
||||
TGenericArray array_00;
|
||||
TGenericArray PathStops; // Array of path checkpoints
|
||||
DWORD ItemIndex; // Current name fragment: Index to various tables
|
||||
DWORD CharIndex;
|
||||
DWORD ItemCount;
|
||||
DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished
|
||||
};
|
||||
|
||||
class TMndxFindResult
|
||||
{
|
||||
public:
|
||||
|
||||
TMndxFindResult();
|
||||
~TMndxFindResult();
|
||||
|
||||
int CreateStruct40();
|
||||
void FreeStruct40();
|
||||
|
||||
int SetSearchPath(const char * szNewSearchPath, size_t cchNewSearchPath);
|
||||
|
||||
const char * szSearchMask; // Search mask without wioldcards
|
||||
size_t cchSearchMask; // Length of the search mask
|
||||
DWORD field_8;
|
||||
const char * szFoundPath; // Found path name
|
||||
size_t cchFoundPath; // Length of the found path name
|
||||
DWORD FileNameIndex; // Index of the file name
|
||||
TStruct40 * pStruct40;
|
||||
};
|
||||
|
||||
class TSparseArray
|
||||
{
|
||||
public:
|
||||
|
||||
TSparseArray();
|
||||
|
||||
void ExchangeWith(TSparseArray & TargetObject);
|
||||
int LoadFromStream(TByteStream & InStream);
|
||||
int LoadFromStream_Exchange(TByteStream & InStream);
|
||||
|
||||
// Returns true if the item at n-th position is present
|
||||
DWORD IsItemPresent(DWORD ItemIndex)
|
||||
{
|
||||
return (ItemBits.Uint32Array[ItemIndex >> 0x05] & (1 << (ItemIndex & 0x1F)));
|
||||
}
|
||||
|
||||
DWORD GetItemValue(DWORD ItemIndex);
|
||||
|
||||
TGenericArray ItemBits; // Bit array for each item (1 = item is present)
|
||||
DWORD TotalItemCount; // Total number of items in the array
|
||||
DWORD ValidItemCount; // Number of present items in the array
|
||||
TGenericArray BaseValues; // Array of base values for item indexes >= 0x200
|
||||
TGenericArray ArrayDwords_38;
|
||||
TGenericArray ArrayDwords_50;
|
||||
};
|
||||
|
||||
class TNameIndexStruct
|
||||
{
|
||||
public:
|
||||
|
||||
TNameIndexStruct();
|
||||
~TNameIndexStruct();
|
||||
|
||||
bool CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
|
||||
bool CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
|
||||
void CopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
|
||||
|
||||
void ExchangeWith(TNameIndexStruct & Target);
|
||||
int LoadFromStream(TByteStream & InStream);
|
||||
int LoadFromStream_Exchange(TByteStream & InStream);
|
||||
|
||||
TGenericArray NameFragments;
|
||||
TSparseArray Struct68;
|
||||
};
|
||||
|
||||
class TStruct10
|
||||
{
|
||||
public:
|
||||
|
||||
TStruct10();
|
||||
|
||||
void CopyFrom(TStruct10 & Target);
|
||||
int sub_1956FD0(DWORD dwBitMask);
|
||||
int sub_1957050(DWORD dwBitMask);
|
||||
int sub_19572E0(DWORD dwBitMask);
|
||||
int sub_1957800(DWORD dwBitMask);
|
||||
|
||||
DWORD field_0;
|
||||
DWORD field_4;
|
||||
DWORD field_8;
|
||||
DWORD field_C;
|
||||
};
|
||||
|
||||
class TFileNameDatabasePtr
|
||||
{
|
||||
public:
|
||||
|
||||
TFileNameDatabasePtr();
|
||||
~TFileNameDatabasePtr();
|
||||
|
||||
int FindFileInDatabase(TMndxFindResult * pStruct1C);
|
||||
int sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult);
|
||||
|
||||
int GetFileNameCount(PDWORD PtrFileNameCount);
|
||||
int CreateDatabase(LPBYTE pbMarData, DWORD cbMarData);
|
||||
int SetDatabase(TFileNameDatabase * pNewDB);
|
||||
|
||||
TFileNameDatabase * pDB;
|
||||
};
|
||||
|
||||
class TFileNameDatabase
|
||||
{
|
||||
public:
|
||||
|
||||
TFileNameDatabase();
|
||||
|
||||
void ExchangeWith(TFileNameDatabase & Target);
|
||||
int LoadFromStream(TByteStream & InStream);
|
||||
int LoadFromStream_Exchange(TByteStream & InStream);
|
||||
|
||||
DWORD sub_1959CB0(DWORD dwHashValue);
|
||||
DWORD sub_1959F50(DWORD arg_0);
|
||||
|
||||
// Retrieves the name fragment distance
|
||||
// HOTS: 19573D0/inlined
|
||||
DWORD GetNameFragmentOffsetEx(DWORD LoBitsIndex, DWORD HiBitsIndex)
|
||||
{
|
||||
return (FrgmDist_HiBits.GetBitEntry(HiBitsIndex) << 0x08) | FrgmDist_LoBits.ByteArray[LoBitsIndex];
|
||||
}
|
||||
|
||||
// HOTS: 1957350, inlined
|
||||
DWORD GetNameFragmentOffset(DWORD LoBitsIndex)
|
||||
{
|
||||
return GetNameFragmentOffsetEx(LoBitsIndex, Struct68_D0.GetItemValue(LoBitsIndex));
|
||||
}
|
||||
|
||||
bool sub_1957B80(TMndxFindResult * pStruct1C, DWORD dwKey);
|
||||
bool CheckNextPathFragment(TMndxFindResult * pStruct1C);
|
||||
bool FindFileInDatabase(TMndxFindResult * pStruct1C);
|
||||
|
||||
void sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4);
|
||||
bool sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4);
|
||||
bool sub_1958B00(TMndxFindResult * pStruct1C);
|
||||
bool sub_1959460(TMndxFindResult * pStruct1C);
|
||||
|
||||
TSparseArray Struct68_00;
|
||||
TSparseArray FileNameIndexes; // Array of file name indexes
|
||||
TSparseArray Struct68_D0;
|
||||
|
||||
// This pair of arrays serves for fast conversion from name hash to fragment offset
|
||||
TGenericArray FrgmDist_LoBits; // Array of lower 8 bits of name fragment offset
|
||||
TBitEntryArray FrgmDist_HiBits; // Array of upper x bits of name fragment offset
|
||||
|
||||
TNameIndexStruct IndexStruct_174;
|
||||
TFileNameDatabasePtr NextDB;
|
||||
|
||||
TGenericArray NameFragTable;
|
||||
|
||||
DWORD NameFragIndexMask;
|
||||
DWORD field_214;
|
||||
TStruct10 Struct10;
|
||||
TByteStream MarStream;
|
||||
};
|
||||
|
||||
typedef struct _MAR_FILE
|
||||
{
|
||||
TFileNameDatabasePtr * pDatabasePtr;
|
||||
LPBYTE pbMarData;
|
||||
DWORD cbMarData;
|
||||
} MAR_FILE, *PMAR_FILE;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Macros
|
||||
|
||||
// Returns nonzero if the name fragment match is a single-char match
|
||||
inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex)
|
||||
{
|
||||
return ((Table.NameFragArray[ItemIndex].FragOffs & 0xFFFFFF00) == 0xFFFFFF00);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CASC functions related to MNDX
|
||||
|
||||
int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName);
|
||||
int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo);
|
||||
bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData);
|
||||
void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo);
|
||||
|
||||
#endif // __CASC_MNDX_ROOT__
|
||||
4369
dep/CascLib/src/CascMndxRoot_x86.asm
Normal file
4369
dep/CascLib/src/CascMndxRoot_x86.asm
Normal file
File diff suppressed because it is too large
Load Diff
440
dep/CascLib/src/CascOpenFile.cpp
Normal file
440
dep/CascLib/src/CascOpenFile.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/*****************************************************************************/
|
||||
/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System-dependent directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
TCascFile * IsValidFileHandle(HANDLE hFile)
|
||||
{
|
||||
TCascFile * hf = (TCascFile *)hFile;
|
||||
|
||||
return (hf != NULL && hf->hs != NULL && hf->szClassName != NULL && !strcmp(hf->szClassName, "TCascFile")) ? hf : NULL;
|
||||
}
|
||||
|
||||
PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry = NULL;
|
||||
|
||||
if(hs->pIndexEntryMap != NULL)
|
||||
pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData);
|
||||
|
||||
return pIndexEntry;
|
||||
}
|
||||
|
||||
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
size_t StartEntry = 0;
|
||||
size_t MidlEntry;
|
||||
size_t EndEntry = hs->nEncodingEntries;
|
||||
int nResult;
|
||||
|
||||
// Perform binary search
|
||||
while(StartEntry < EndEntry)
|
||||
{
|
||||
// Calculate the middle of the interval
|
||||
MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2);
|
||||
pEncodingEntry = hs->ppEncodingEntries[MidlEntry];
|
||||
|
||||
// Did we find it?
|
||||
nResult = memcmp(pEncodingKey->pbData, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
if(nResult == 0)
|
||||
{
|
||||
if(PtrIndex != NULL)
|
||||
PtrIndex[0] = MidlEntry;
|
||||
return pEncodingEntry;
|
||||
}
|
||||
|
||||
// Move the interval to the left or right
|
||||
(nResult < 0) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1;
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Also used in CascSearchFile
|
||||
PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pFoundEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
ULONGLONG FileNameHash;
|
||||
uint32_t dwHashHigh = 0;
|
||||
uint32_t dwHashLow = 0;
|
||||
size_t StartEntry = 0;
|
||||
size_t MidlEntry = 0;
|
||||
size_t EndEntry = hs->nRootEntries;
|
||||
|
||||
// Calculate the HASH value of the normalized file name
|
||||
hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow);
|
||||
FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
|
||||
|
||||
// Perform binary search
|
||||
while(StartEntry < EndEntry)
|
||||
{
|
||||
// Calculate the middle of the interval
|
||||
MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2);
|
||||
pRootEntry = hs->ppRootEntries[MidlEntry];
|
||||
|
||||
// Did we find it?
|
||||
if(pRootEntry->FileNameHash == FileNameHash)
|
||||
{
|
||||
pFoundEntry = pRootEntry;
|
||||
break;
|
||||
}
|
||||
|
||||
// Move the interval to the left or right
|
||||
(FileNameHash < pRootEntry->FileNameHash) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1;
|
||||
}
|
||||
|
||||
// Move the pointer back to the first entry with that hash
|
||||
if(pFoundEntry != NULL)
|
||||
{
|
||||
while(MidlEntry > 0 && hs->ppRootEntries[MidlEntry - 1]->FileNameHash == FileNameHash)
|
||||
{
|
||||
pFoundEntry = hs->ppRootEntries[MidlEntry - 1];
|
||||
MidlEntry--;
|
||||
}
|
||||
}
|
||||
|
||||
// Return what we found
|
||||
if(PtrIndex != NULL)
|
||||
*PtrIndex = MidlEntry;
|
||||
return pFoundEntry;
|
||||
}
|
||||
|
||||
// Check the root directory for that hash
|
||||
PCASC_ROOT_ENTRY FindRootEntryLocale(TCascStorage * hs, char * szFileName, DWORD Locale)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pThatEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pENUSEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pENGBEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pAnyEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pEndEntry = NULL;
|
||||
PCASC_ROOT_ENTRY pRootEntry = NULL;
|
||||
ULONGLONG FileNameHash;
|
||||
size_t EntryIndex = 0;
|
||||
size_t EndEntry = hs->nRootEntries;
|
||||
|
||||
// Find a root entry with the given name hash
|
||||
pRootEntry = FindFirstRootEntry(hs, szFileName, &EntryIndex);
|
||||
if(pRootEntry != NULL)
|
||||
{
|
||||
// Rememeber the file name hash
|
||||
pEndEntry = hs->pRootEntries + hs->nRootEntries;
|
||||
FileNameHash = pRootEntry->FileNameHash;
|
||||
|
||||
// Find all suitable root entries
|
||||
while(EntryIndex < EndEntry)
|
||||
{
|
||||
// Get the root entry
|
||||
pRootEntry = hs->ppRootEntries[EntryIndex++];
|
||||
if(pRootEntry->FileNameHash != FileNameHash)
|
||||
break;
|
||||
|
||||
// If a locale has been given, check it
|
||||
if(pThatEntry == NULL && Locale != 0 && (Locale & pRootEntry->Locales))
|
||||
pThatEntry = pRootEntry;
|
||||
if(pENUSEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENUS))
|
||||
pENUSEntry = pRootEntry;
|
||||
if(pENGBEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENGB))
|
||||
pENGBEntry = pRootEntry;
|
||||
if(pAnyEntry == NULL)
|
||||
pAnyEntry = pRootEntry;
|
||||
|
||||
// Move to the next one
|
||||
pRootEntry++;
|
||||
}
|
||||
|
||||
// Return the key by priority
|
||||
if(pThatEntry != NULL)
|
||||
return pThatEntry;
|
||||
if(pENGBEntry != NULL)
|
||||
return pENGBEntry;
|
||||
if(pENUSEntry != NULL)
|
||||
return pENUSEntry;
|
||||
}
|
||||
|
||||
// Return whatever we got
|
||||
return pAnyEntry;
|
||||
}
|
||||
|
||||
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
|
||||
{
|
||||
ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1;
|
||||
ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
|
||||
TCascFile * hf;
|
||||
|
||||
// Allocate the CASC file structure
|
||||
hf = (TCascFile *)CASC_ALLOC(TCascFile, 1);
|
||||
if(hf != NULL)
|
||||
{
|
||||
// Initialize the structure
|
||||
memset(hf, 0, sizeof(TCascFile));
|
||||
hf->ArchiveIndex = (DWORD)(FileOffset >> hs->KeyMapping[0].SegmentBits);
|
||||
hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask);
|
||||
hf->szClassName = "TCascFile";
|
||||
|
||||
// Copy the compressed file size
|
||||
hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize) - 0x1E;
|
||||
|
||||
// For now, we set the file size to be equal to compressed size
|
||||
// This is used when loading the "encoding" file, which does not
|
||||
// have entry in the encoding itself
|
||||
hf->FileSize = hf->CompressedSize;
|
||||
|
||||
// Increment the number of references to the archive
|
||||
hs->dwRefCount++;
|
||||
hf->hs = hs;
|
||||
}
|
||||
|
||||
return hf;
|
||||
}
|
||||
|
||||
static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
TCascFile * hf = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
CASCLIB_UNUSED(dwFlags);
|
||||
|
||||
// Find the key entry in the array of file keys
|
||||
pIndexEntry = FindIndexEntry(hs, pIndexKey);
|
||||
if(pIndexEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Create the file handle structure
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
hf = CreateFileHandle(hs, pIndexEntry);
|
||||
*phFile = (HANDLE)hf;
|
||||
if(hf == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
QUERY_KEY IndexKey;
|
||||
TCascFile * hf = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Find the encoding entry
|
||||
pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL);
|
||||
if(pEncodingEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Prepare the file index and open the file by index
|
||||
// Note: We don't know what to do if there is more than just one index key
|
||||
// We always take the first file present. Is that correct?
|
||||
// IndexKey.pbData = pEncodingEntry->EncodingKey + (MD5_HASH_SIZE * pEncodingEntry->KeyCount);
|
||||
// assert(pEncodingEntry->KeyCount == 1);
|
||||
IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
IndexKey.cbData = MD5_HASH_SIZE;
|
||||
if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, phFile))
|
||||
{
|
||||
// Fix the file size from the encoding key
|
||||
hf = IsValidFileHandle(*phFile);
|
||||
if(hf != NULL)
|
||||
{
|
||||
hf->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
TCascStorage * hs;
|
||||
|
||||
// Validate the storage handle
|
||||
hs = IsValidStorageHandle(hStorage);
|
||||
if(hs == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the other parameters
|
||||
if(pIndexKey == NULL || pIndexKey->pbData == NULL || pIndexKey->cbData == 0 || phFile == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the internal function to open the file
|
||||
return OpenFileByIndexKey(hs, pIndexKey, dwFlags, phFile);
|
||||
}
|
||||
|
||||
bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
TCascStorage * hs;
|
||||
|
||||
// Validate the storage handle
|
||||
hs = IsValidStorageHandle(hStorage);
|
||||
if(hs == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the other parameters
|
||||
if(pEncodingKey == NULL || pEncodingKey->pbData == NULL || pEncodingKey->cbData == 0 || phFile == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the internal function fo open the file
|
||||
return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, phFile);
|
||||
}
|
||||
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
CASC_ROOT_KEY_INFO EncodingKeyInfo;
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
PCASC_PACKAGE pPackage;
|
||||
TCascStorage * hs;
|
||||
QUERY_KEY EncodingKey;
|
||||
char * szStrippedName;
|
||||
char * szFileName2;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Validate the storage handle
|
||||
hs = IsValidStorageHandle(hStorage);
|
||||
if(hs == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the other parameters
|
||||
if(szFileName == NULL || szFileName[0] == 0 || phFile == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the copy of the file name
|
||||
szFileName2 = NewStr(szFileName, 0);
|
||||
if(szFileName2 != NULL)
|
||||
{
|
||||
// If the storage has a MNDX root directory, use it to search the entry
|
||||
if(hs->pMndxInfo != NULL)
|
||||
{
|
||||
// Convert the file name to lowercase + slashes
|
||||
NormalizeFileName_LowerSlash(szFileName2);
|
||||
|
||||
// Find the package number
|
||||
pPackage = FindMndxPackage(hs, szFileName2);
|
||||
if(pPackage != NULL)
|
||||
{
|
||||
// Cut the package name off the full path
|
||||
szStrippedName = szFileName2 + pPackage->nLength;
|
||||
while(szStrippedName[0] == '/')
|
||||
szStrippedName++;
|
||||
|
||||
nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &EncodingKeyInfo);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Prepare the encoding key
|
||||
EncodingKey.pbData = EncodingKeyInfo.EncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the file name to lowercase + slashes
|
||||
NormalizeFileName_UpperBkSlash(szFileName2);
|
||||
|
||||
// Check the root directory for that hash
|
||||
pRootEntry = FindRootEntryLocale(hs, szFileName2, dwLocale);
|
||||
nError = (pRootEntry != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
|
||||
if(pRootEntry != NULL)
|
||||
{
|
||||
// Prepare the root key
|
||||
EncodingKey.pbData = pRootEntry->EncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the root key to find the file in the encoding table entry
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, phFile))
|
||||
{
|
||||
assert(GetLastError() != ERROR_SUCCESS);
|
||||
nError = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the file name copy
|
||||
delete [] szFileName2;
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
bool WINAPI CascCloseFile(HANDLE hFile)
|
||||
{
|
||||
TCascFile * hf;
|
||||
|
||||
hf = IsValidFileHandle(hFile);
|
||||
if(hf != NULL)
|
||||
{
|
||||
// Close (dereference) the archive handle
|
||||
if(hf->hs != NULL)
|
||||
CascCloseStorage((HANDLE)hf->hs);
|
||||
hf->hs = NULL;
|
||||
|
||||
// Free the file cache and frame array
|
||||
if(hf->pbFileCache != NULL)
|
||||
CASC_FREE(hf->pbFileCache);
|
||||
if(hf->pFrames != NULL)
|
||||
CASC_FREE(hf->pFrames);
|
||||
|
||||
// Free the structure itself
|
||||
hf->szClassName = NULL;
|
||||
CASC_FREE(hf);
|
||||
return true;
|
||||
}
|
||||
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
1226
dep/CascLib/src/CascOpenStorage.cpp
Normal file
1226
dep/CascLib/src/CascOpenStorage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,16 @@
|
||||
/*****************************************************************************/
|
||||
/* StormPort.h Copyright (c) Marko Friedemann 2001 */
|
||||
/* CascPort.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Portability module for the StormLib library. Contains a wrapper symbols */
|
||||
/* Portability module for the CascLib library. Contains a wrapper symbols */
|
||||
/* to make the compilation under Linux work */
|
||||
/* */
|
||||
/* Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de> */
|
||||
/* Created at: Mon Jan 29 18:26:01 CEST 2001 */
|
||||
/* Computer: whiplash.flachland-chemnitz.de */
|
||||
/* System: Linux 2.4.0 on i686 */
|
||||
/* */
|
||||
/* Author: Sam Wilkins <swilkins1337@gmail.com> */
|
||||
/* System: Mac OS X and port to big endian processor */
|
||||
/* */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.01.01 1.00 Mar Created */
|
||||
/* 24.03.03 1.01 Lad Some cosmetic changes */
|
||||
/* 12.11.03 1.02 Dan Macintosh compatibility */
|
||||
/* 24.07.04 1.03 Sam Mac OS X compatibility */
|
||||
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
|
||||
/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */
|
||||
/* 29.04.14 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __STORMPORT_H__
|
||||
#define __STORMPORT_H__
|
||||
#ifndef __CASCPORT_H__
|
||||
#define __CASCPORT_H__
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define bool char
|
||||
@@ -32,7 +18,9 @@
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines for Windows
|
||||
|
||||
#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64))
|
||||
|
||||
// In MSVC 8.0, there are some functions declared as deprecated.
|
||||
@@ -47,6 +35,7 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <sys/types.h>
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
#ifdef WIN64
|
||||
@@ -55,12 +44,17 @@
|
||||
#define PLATFORM_32BIT
|
||||
#endif
|
||||
|
||||
#define PATH_SEPARATOR '\\'
|
||||
#define CREATE_DIRECTORY(name) CreateDirectory(name, NULL);
|
||||
|
||||
#define PLATFORM_WINDOWS
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
|
||||
#endif
|
||||
|
||||
// Defines for Mac
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines for Mac
|
||||
|
||||
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
|
||||
|
||||
// Macintosh
|
||||
@@ -70,8 +64,15 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// Support for PowerPC on Max OS X
|
||||
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
|
||||
#include <stdint.h>
|
||||
#include <CoreFoundation/CFByteOrder.h>
|
||||
#endif
|
||||
|
||||
#define PKEXPORT
|
||||
#define __SYS_ZLIB
|
||||
#define __SYS_BZLIB
|
||||
@@ -80,19 +81,27 @@
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#define PATH_SEPARATOR '/'
|
||||
#define CREATE_DIRECTORY(name) mkdir(name, 0755)
|
||||
|
||||
#define PLATFORM_MAC
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
|
||||
#define FIELD_OFFSET(t,f) offsetof(t,f)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
|
||||
|
||||
#if !defined(PLATFORM_DEFINED)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -102,13 +111,19 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define PATH_SEPARATOR '/'
|
||||
#define CREATE_DIRECTORY(name) mkdir(name, 0755)
|
||||
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#define PLATFORM_LINUX
|
||||
#define PLATFORM_DEFINED
|
||||
|
||||
#define FIELD_OFFSET(t,f) offsetof(t,f)
|
||||
#endif
|
||||
|
||||
// Definition of Windows-specific structures for non-Windows platforms
|
||||
//-----------------------------------------------------------------------------
|
||||
// Definition of Windows-specific types for non-Windows platforms
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
#if __LP64__
|
||||
#define PLATFORM_64BIT
|
||||
@@ -126,12 +141,13 @@
|
||||
typedef long INT_PTR;
|
||||
typedef long long LONGLONG;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef unsigned long long *PULONGLONG;
|
||||
typedef void * HANDLE;
|
||||
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
|
||||
typedef char TCHAR;
|
||||
typedef unsigned int LCID;
|
||||
typedef LONG * PLONG;
|
||||
typedef DWORD * LPDWORD;
|
||||
typedef DWORD * PDWORD;
|
||||
typedef BYTE * LPBYTE;
|
||||
|
||||
#ifdef PLATFORM_32BIT
|
||||
@@ -149,30 +165,38 @@
|
||||
#define FILE_CURRENT SEEK_CUR
|
||||
#define FILE_END SEEK_END
|
||||
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
||||
|
||||
#define _T(x) x
|
||||
#define _tcslen strlen
|
||||
#define _tcscpy strcpy
|
||||
#define _tcscat strcat
|
||||
#define _tcschr strchr
|
||||
#define _tcsrchr strrchr
|
||||
#define _tcsstr strstr
|
||||
#define _tcsspn strspn
|
||||
#define _tprintf printf
|
||||
#define _stprintf sprintf
|
||||
#define _tremove remove
|
||||
#define _tmkdir mkdir
|
||||
|
||||
#define _stricmp strcasecmp
|
||||
#define _strnicmp strncasecmp
|
||||
#define _tcsicmp strcasecmp
|
||||
#define _tcsnicmp strncasecmp
|
||||
|
||||
#endif // !WIN32
|
||||
#endif // !PLATFORM_WINDOWS
|
||||
|
||||
// 64-bit calls are supplied by "normal" calls on Mac
|
||||
#if defined(PLATFORM_MAC)
|
||||
#define stat64 stat
|
||||
#define fstat64 fstat
|
||||
#define lseek64 lseek
|
||||
#define ftruncate64 ftruncate
|
||||
#define off64_t off_t
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
|
||||
// Platform-specific error codes for UNIX-based platforms
|
||||
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
|
||||
#define ERROR_SUCCESS 0
|
||||
@@ -180,18 +204,21 @@
|
||||
#define ERROR_ACCESS_DENIED EPERM
|
||||
#define ERROR_INVALID_HANDLE EBADF
|
||||
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
|
||||
#define ERROR_BAD_FORMAT 105 // No such error code under Linux
|
||||
#define ERROR_NO_MORE_FILES 106
|
||||
#define ERROR_HANDLE_EOF 107 // No such error code under Linux
|
||||
#define ERROR_NOT_SUPPORTED ENOTSUP
|
||||
#define ERROR_INVALID_PARAMETER EINVAL
|
||||
#define ERROR_DISK_FULL ENOSPC
|
||||
#define ERROR_ALREADY_EXISTS EEXIST
|
||||
#define ERROR_CAN_NOT_COMPLETE 108 // No such error code under Linux
|
||||
#define ERROR_FILE_CORRUPT 109 // No such error code under Linux
|
||||
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
|
||||
#define ERROR_BAD_FORMAT 1000 // No such error code under Linux
|
||||
#define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
|
||||
#define ERROR_HANDLE_EOF 1002 // No such error code under Linux
|
||||
#define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
|
||||
#define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Swapping functions
|
||||
|
||||
#ifdef PLATFORM_LITTLE_ENDIAN
|
||||
#define BSWAP_INT16_UNSIGNED(a) (a)
|
||||
#define BSWAP_INT16_SIGNED(a) (a)
|
||||
@@ -202,10 +229,11 @@
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
|
||||
#define BSWAP_PART_HEADER(a) {}
|
||||
#define BSWAP_TMPQUSERDATA(a) {}
|
||||
#define BSWAP_TMPQHEADER(a) {}
|
||||
#else
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int16_t SwapInt16(uint16_t);
|
||||
uint16_t SwapUInt16(uint16_t);
|
||||
int32_t SwapInt32(uint32_t);
|
||||
@@ -215,9 +243,9 @@
|
||||
void ConvertUInt16Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt32Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt64Buffer(void * ptr, size_t length);
|
||||
void ConvertPartHeader(void * partHeader);
|
||||
void ConvertTMPQUserData(void *userData);
|
||||
void ConvertTMPQHeader(void *header);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
|
||||
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
|
||||
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
|
||||
@@ -227,9 +255,6 @@
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
||||
#define BSWAP_PART_HEADER(a) ConvertPartHeader(a)
|
||||
#define BSWAP_TMPQUSERDATA(a) ConvertTMPQUserData((a))
|
||||
#define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a))
|
||||
#endif
|
||||
|
||||
#endif // __STORMPORT_H__
|
||||
#endif // __CASCPORT_H__
|
||||
475
dep/CascLib/src/CascReadFile.cpp
Normal file
475
dep/CascLib/src/CascReadFile.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/*****************************************************************************/
|
||||
/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System-dependent directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
#define BLTE_HEADER_SIGNATURE 0x45544C42
|
||||
|
||||
// Data file begin:
|
||||
// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array
|
||||
// DWORD dwFileSize; // Size of the file
|
||||
// BYTE SomeSize[4]; // Some size (big endian)
|
||||
// BYTE Padding[6]; // Padding (?)
|
||||
|
||||
typedef struct _BLTE_HEADER
|
||||
{
|
||||
DWORD dwSignature; // Must be "BLTE"
|
||||
BYTE HeaderSizeAsBytes[4]; // Header size in bytes (big endian)
|
||||
BYTE MustBe0F; // Must be 0x0F
|
||||
BYTE FrameCount[3]; // Number of frames (big endian)
|
||||
|
||||
} BLTE_HEADER, *PBLTE_HEADER;
|
||||
|
||||
typedef struct _BLTE_FRAME
|
||||
{
|
||||
BYTE CompressedSize[4]; // Compressed file size as big endian
|
||||
BYTE FrameSize[4]; // File size as big endian
|
||||
BYTE md5[MD5_HASH_SIZE]; // Hash of the frame
|
||||
|
||||
} BLTE_FRAME, *PBLTE_FRAME;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
TCascFile * IsValidFileHandle(HANDLE hFile); // In CascOpenFile.cpp
|
||||
|
||||
static int EnsureDataStreamIsOpen(TCascFile * hf)
|
||||
{
|
||||
TCascStorage * hs = hf->hs;
|
||||
TFileStream * pStream = NULL;
|
||||
TCHAR * szDataFile;
|
||||
TCHAR szPlainName[0x40];
|
||||
|
||||
// If the file is not open yet, do it
|
||||
if(hs->DataFileArray[hf->ArchiveIndex] == NULL)
|
||||
{
|
||||
// Prepare the name of the data file
|
||||
_stprintf(szPlainName, _T("data.%03u"), hf->ArchiveIndex);
|
||||
szDataFile = CombinePath(hs->szIndexPath, szPlainName);
|
||||
|
||||
// Open the data file
|
||||
if(szDataFile != NULL)
|
||||
{
|
||||
// Open the stream
|
||||
pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
|
||||
hs->DataFileArray[hf->ArchiveIndex] = pStream;
|
||||
|
||||
// TODO: There is 0x1E bytes at the beginning of the file stream
|
||||
// Ignore them for now, but we will want to know what they mean
|
||||
// Offs0000: MD5 of something
|
||||
// Offs0010: 2 bytes
|
||||
CASC_FREE(szDataFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Return error or success
|
||||
hf->pStream = hs->DataFileArray[hf->ArchiveIndex];
|
||||
return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int LoadFileFrames(TCascFile * hf, DWORD FrameCount)
|
||||
{
|
||||
PBLTE_FRAME pFileFrames;
|
||||
PBLTE_FRAME pFileFrame;
|
||||
ULONGLONG ArchiveFileOffset;
|
||||
DWORD FrameOffset = 0;
|
||||
DWORD FileSize = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
assert(hf != NULL);
|
||||
assert(hf->pStream != NULL);
|
||||
assert(hf->pFrames != NULL);
|
||||
|
||||
// Allocate frame array
|
||||
pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, FrameCount);
|
||||
if(pFileFrames != NULL)
|
||||
{
|
||||
// Load the frame array
|
||||
ArchiveFileOffset = hf->FramesOffset;
|
||||
if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, FrameCount * sizeof(BLTE_FRAME)))
|
||||
{
|
||||
// Move the raw archive offset
|
||||
ArchiveFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME));
|
||||
|
||||
// Copy the frames to the file structure
|
||||
for(DWORD i = 0; i < FrameCount; i++, pFileFrame++)
|
||||
{
|
||||
hf->pFrames[i].FrameArchiveOffset = (DWORD)ArchiveFileOffset;
|
||||
hf->pFrames[i].FrameFileOffset = FrameOffset;
|
||||
hf->pFrames[i].CompressedSize = ConvertBytesToInteger_4(pFileFrame->CompressedSize);
|
||||
hf->pFrames[i].FrameSize = ConvertBytesToInteger_4(pFileFrame->FrameSize);
|
||||
memcpy(hf->pFrames[i].md5, pFileFrame->md5, MD5_HASH_SIZE);
|
||||
|
||||
ArchiveFileOffset += hf->pFrames[i].CompressedSize;
|
||||
FrameOffset += hf->pFrames[i].FrameSize;
|
||||
FileSize += hf->pFrames[i].FrameSize;
|
||||
}
|
||||
|
||||
// Fill-in the frame count
|
||||
hf->FrameCount = FrameCount;
|
||||
}
|
||||
else
|
||||
nError = GetLastError();
|
||||
|
||||
// Verify the file size
|
||||
// assert(FileSize == hf->FileSize);
|
||||
// hf->FileSize = FileSize;
|
||||
|
||||
// Free the array
|
||||
CASC_FREE(pFileFrames);
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int EnsureFrameHeadersLoaded(TCascFile * hf)
|
||||
{
|
||||
PBLTE_HEADER pBlteHeader;
|
||||
ULONGLONG FileOffset = hf->HeaderOffset;
|
||||
DWORD dwHeaderOffsetFixup = 0;
|
||||
DWORD dwFrameHeaderSize;
|
||||
DWORD dwFrameCount;
|
||||
BYTE HeaderBuffer[sizeof(BLTE_HEADER) + 0x20];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Sanity check
|
||||
assert(hf->pStream != NULL);
|
||||
|
||||
// If the frame headers are not loaded yet, do it
|
||||
if(hf->pFrames == NULL)
|
||||
{
|
||||
// Note that older builds of Heroes of the Storm have entries pointing
|
||||
// to the begin of the BLTE header, which is MD5 + some junk.
|
||||
// Newer versions of HOTS have encoding entries pointing directly to
|
||||
// the BLTE header
|
||||
FileStream_Read(hf->pStream, &FileOffset, HeaderBuffer, sizeof(HeaderBuffer));
|
||||
pBlteHeader = (PBLTE_HEADER)HeaderBuffer;
|
||||
|
||||
// If we don't have the BLTE header right there,
|
||||
// just get the block that is 0x1E bytes later
|
||||
if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE)
|
||||
{
|
||||
memcpy(&HeaderBuffer[0x00], &HeaderBuffer[0x1E], sizeof(BLTE_HEADER));
|
||||
dwHeaderOffsetFixup = 0x1E;
|
||||
}
|
||||
|
||||
// Check for the BLTE header signature
|
||||
if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
hf->HeaderOffset += dwHeaderOffsetFixup;
|
||||
|
||||
// Check for a single unit file
|
||||
dwFrameHeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSizeAsBytes);
|
||||
dwFrameCount = (dwFrameHeaderSize != 0) ? ConvertBytesToInteger_3(pBlteHeader->FrameCount) : 1;
|
||||
|
||||
// Allocate the frame array
|
||||
hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, dwFrameCount);
|
||||
if(hf->pFrames != NULL)
|
||||
{
|
||||
// Save the number of frames
|
||||
hf->FrameCount = dwFrameCount;
|
||||
|
||||
// Either load the frames from the file or supply them on our own
|
||||
if(dwFrameHeaderSize != 0)
|
||||
{
|
||||
if(pBlteHeader->MustBe0F != 0x0F)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
|
||||
hf->FramesOffset = hf->HeaderOffset + sizeof(BLTE_HEADER);
|
||||
nError = LoadFileFrames(hf, dwFrameCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Offset of the first frame is right after the file frames
|
||||
hf->FramesOffset = hf->HeaderOffset + sizeof(pBlteHeader->dwSignature) + sizeof(pBlteHeader->HeaderSizeAsBytes);
|
||||
|
||||
hf->pFrames[0].FrameArchiveOffset = hf->FramesOffset;
|
||||
hf->pFrames[0].FrameFileOffset = 0;
|
||||
hf->pFrames[0].CompressedSize = hf->CompressedSize;
|
||||
hf->pFrames[0].FrameSize = hf->FileSize;
|
||||
memset(hf->pFrames[0].md5, 0, MD5_HASH_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
return (hf->pFrames != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer)
|
||||
{
|
||||
PCASC_FILE_FRAME pFrame = hf->pFrames;
|
||||
DWORD FrameBegin;
|
||||
DWORD FrameEnd;
|
||||
|
||||
// Sanity checks
|
||||
assert(hf->pFrames != NULL);
|
||||
assert(hf->FrameCount != 0);
|
||||
|
||||
// Find the frame where to read from
|
||||
for(DWORD i = 0; i < hf->FrameCount; i++, pFrame++)
|
||||
{
|
||||
// Does the read request fit into the current frame?
|
||||
FrameBegin = pFrame->FrameFileOffset;
|
||||
FrameEnd = FrameBegin + pFrame->FrameSize;
|
||||
if(FrameBegin <= FilePointer && FilePointer < FrameEnd)
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
|
||||
{
|
||||
TCascFile * hf;
|
||||
|
||||
CASCLIB_UNUSED(pdwFileSizeHigh);
|
||||
|
||||
// Validate the file handle
|
||||
if((hf = IsValidFileHandle(hFile)) == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return CASC_INVALID_SIZE;
|
||||
}
|
||||
|
||||
// Give the file size to the caller
|
||||
if(pdwFileSizeHigh != NULL)
|
||||
*pdwFileSizeHigh = 0;
|
||||
return hf->FileSize;
|
||||
}
|
||||
|
||||
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod)
|
||||
{
|
||||
TCascFile * hf;
|
||||
ULONGLONG FilePosition;
|
||||
ULONGLONG MoveOffset;
|
||||
DWORD dwFilePosHi;
|
||||
|
||||
// If the hFile is not a valid file handle, return an error.
|
||||
hf = IsValidFileHandle(hFile);
|
||||
if(hf == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return CASC_INVALID_POS;
|
||||
}
|
||||
|
||||
// Get the relative point where to move from
|
||||
switch(dwMoveMethod)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
FilePosition = 0;
|
||||
break;
|
||||
|
||||
case FILE_CURRENT:
|
||||
FilePosition = hf->FilePointer;
|
||||
break;
|
||||
|
||||
case FILE_END:
|
||||
FilePosition = hf->FileSize;
|
||||
break;
|
||||
|
||||
default:
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return CASC_INVALID_POS;
|
||||
}
|
||||
|
||||
// Now get the move offset. Note that both values form
|
||||
// a signed 64-bit value (a file pointer can be moved backwards)
|
||||
if(plFilePosHigh != NULL)
|
||||
dwFilePosHi = *plFilePosHigh;
|
||||
else
|
||||
dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0;
|
||||
MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos);
|
||||
|
||||
// Now calculate the new file pointer
|
||||
// Do not allow the file pointer to overflow
|
||||
FilePosition = ((FilePosition + MoveOffset) >= FilePosition) ? (FilePosition + MoveOffset) : 0;
|
||||
|
||||
// CASC files can't be bigger than 4 GB.
|
||||
// We don't allow to go past 4 GB
|
||||
if(FilePosition >> 32)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return CASC_INVALID_POS;
|
||||
}
|
||||
|
||||
// Change the file position
|
||||
hf->FilePointer = (DWORD)FilePosition;
|
||||
|
||||
// Return the new file position
|
||||
if(plFilePosHigh != NULL)
|
||||
*plFilePosHigh = 0;
|
||||
return hf->FilePointer;
|
||||
}
|
||||
|
||||
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead)
|
||||
{
|
||||
PCASC_FILE_FRAME pFrame = NULL;
|
||||
ULONGLONG FileOffset;
|
||||
TCascFile * hf;
|
||||
LPBYTE pbBuffer = (LPBYTE)pvBuffer;
|
||||
DWORD dwStartPointer = 0;
|
||||
DWORD dwFilePointer = 0;
|
||||
DWORD dwEndPointer = 0;
|
||||
DWORD cbOutBuffer;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// The buffer must be valid
|
||||
if(pvBuffer == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the file handle
|
||||
if((hf = IsValidFileHandle(hFile)) == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the file position is at or beyond end of file, do nothing
|
||||
if(hf->FilePointer >= hf->FileSize)
|
||||
{
|
||||
*pdwBytesRead = 0;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Make sure we have that data file open
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = EnsureDataStreamIsOpen(hf);
|
||||
}
|
||||
|
||||
// If the file frames are not loaded yet, do it now
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = EnsureFrameHeadersLoaded(hf);
|
||||
}
|
||||
|
||||
// Find the file frame where to read from
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Get the frame
|
||||
pFrame = FindFileFrame(hf, hf->FilePointer);
|
||||
if(pFrame == NULL)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Perform the read
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// If not enough bytes in the file remaining, cut them
|
||||
dwStartPointer = dwFilePointer = hf->FilePointer;
|
||||
dwEndPointer = dwStartPointer + dwBytesToRead;
|
||||
if(dwEndPointer > hf->FileSize)
|
||||
dwEndPointer = hf->FileSize;
|
||||
|
||||
// Perform block read from each file frame
|
||||
while(dwFilePointer < dwEndPointer)
|
||||
{
|
||||
LPBYTE pbRawData = NULL;
|
||||
DWORD dwFrameStart = pFrame->FrameFileOffset;
|
||||
DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize;
|
||||
|
||||
// Shall we populate the cache with a new data?
|
||||
if(dwFrameStart != hf->CacheStart || hf->CacheEnd != dwFrameEnd)
|
||||
{
|
||||
// Shall we reallocate the cache buffer?
|
||||
if(pFrame->FrameSize > hf->cbFileCache)
|
||||
{
|
||||
if(hf->pbFileCache != NULL)
|
||||
CASC_FREE(hf->pbFileCache);
|
||||
|
||||
hf->pbFileCache = CASC_ALLOC(BYTE, pFrame->FrameSize);
|
||||
hf->cbFileCache = pFrame->FrameSize;
|
||||
}
|
||||
|
||||
// We also need to allocate buffer for the raw data
|
||||
pbRawData = CASC_ALLOC(BYTE, pFrame->CompressedSize);
|
||||
if(pbRawData == NULL)
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the raw file data to memory
|
||||
FileOffset = pFrame->FrameArchiveOffset;
|
||||
if(!FileStream_Read(hf->pStream, &FileOffset, pbRawData, pFrame->CompressedSize))
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify the block MD5
|
||||
if(IsValidMD5(pFrame->md5) && !VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5))
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Decompress the file frame
|
||||
cbOutBuffer = pFrame->FrameSize;
|
||||
nError = CascDecompress(hf->pbFileCache, &cbOutBuffer, pbRawData, pFrame->CompressedSize);
|
||||
if(nError != ERROR_SUCCESS || cbOutBuffer != pFrame->FrameSize)
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the start and end of the cache
|
||||
hf->CacheStart = dwFrameStart;
|
||||
hf->CacheEnd = dwFrameEnd;
|
||||
|
||||
// Free the decompress buffer, if needed
|
||||
CASC_FREE(pbRawData);
|
||||
}
|
||||
|
||||
// Copy the decompressed data
|
||||
if(dwFrameEnd > dwEndPointer)
|
||||
dwFrameEnd = dwEndPointer;
|
||||
memcpy(pbBuffer, hf->pbFileCache + (dwFilePointer - dwFrameStart), (dwFrameEnd - dwFilePointer));
|
||||
pbBuffer += (dwFrameEnd - dwFilePointer);
|
||||
|
||||
// Move pointers
|
||||
dwFilePointer = dwFrameEnd;
|
||||
pFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the file position
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(pdwBytesRead != NULL)
|
||||
*pdwBytesRead = (dwFilePointer - dwStartPointer);
|
||||
hf->FilePointer = dwFilePointer;
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
672
dep/CascLib/src/common/Common.cpp
Normal file
672
dep/CascLib/src/common/Common.cpp
Normal file
@@ -0,0 +1,672 @@
|
||||
/*****************************************************************************/
|
||||
/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Conversion to uppercase/lowercase
|
||||
|
||||
// Converts ASCII characters to lowercase
|
||||
// Converts slash (0x2F) to backslash (0x5C)
|
||||
unsigned char AsciiToLowerTable[256] =
|
||||
{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
||||
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||||
};
|
||||
|
||||
// Converts ASCII characters to uppercase
|
||||
// Converts slash (0x2F) to backslash (0x5C)
|
||||
unsigned char AsciiToUpperTable[256] =
|
||||
{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
||||
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||||
};
|
||||
|
||||
unsigned char IntToHexChar[] = "0123456789abcdef";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for memory reallocation
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
void * DbgRealloc(void * ptr, size_t nSize)
|
||||
{
|
||||
// HeapReAlloc does not support NULL as previous block
|
||||
if(ptr == NULL)
|
||||
return HeapAlloc(GetProcessHeap, 0, nSize);
|
||||
|
||||
return HeapReAlloc(GetProcessHeap(), 0, ptr, nSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetLastError/SetLastError support for non-Windows platform
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
static int nLastError = ERROR_SUCCESS;
|
||||
|
||||
int GetLastError()
|
||||
{
|
||||
return nLastError;
|
||||
}
|
||||
|
||||
void SetLastError(int nError)
|
||||
{
|
||||
nLastError = nError;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String manipulation
|
||||
|
||||
void CopyString(char * szTarget, const char * szSource, size_t cchLength)
|
||||
{
|
||||
memcpy(szTarget, szSource, cchLength);
|
||||
szTarget[cchLength] = 0;
|
||||
}
|
||||
|
||||
void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength)
|
||||
{
|
||||
mbstowcs(szTarget, szSource, cchLength);
|
||||
szTarget[cchLength] = 0;
|
||||
}
|
||||
|
||||
void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength)
|
||||
{
|
||||
wcstombs(szTarget, szSource, cchLength);
|
||||
szTarget[cchLength] = 0;
|
||||
}
|
||||
|
||||
char * NewStr(const char * szString, size_t nCharsToReserve)
|
||||
{
|
||||
char * szNewString = NULL;
|
||||
size_t nLength;
|
||||
|
||||
if(szString != NULL)
|
||||
{
|
||||
nLength = strlen(szString);
|
||||
szNewString = CASC_ALLOC(char, nLength + nCharsToReserve + 1);
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
memcpy(szNewString, szString, nLength);
|
||||
szNewString[nLength] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
{
|
||||
wchar_t * szNewString = NULL;
|
||||
size_t nLength;
|
||||
|
||||
if(szString != NULL)
|
||||
{
|
||||
nLength = wcslen(szString);
|
||||
szNewString = CASC_ALLOC(wchar_t, nLength + nCharsToReserve + 1);
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
memcpy(szNewString, szString, nLength * sizeof(wchar_t));
|
||||
szNewString[nLength] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd)
|
||||
{
|
||||
TCHAR * szNewString = NULL;
|
||||
TCHAR * szStringPtr = NULL;
|
||||
size_t nLength = (size_t)(pbStringEnd - pbStringBegin);
|
||||
|
||||
if(pbStringEnd > pbStringBegin)
|
||||
{
|
||||
szNewString = szStringPtr = CASC_ALLOC(TCHAR, nLength + 1);
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
CopyString(szStringPtr, (const char *)pbStringBegin, nLength);
|
||||
szStringPtr[nLength] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
|
||||
{
|
||||
TCHAR * szFullPath = NULL;
|
||||
TCHAR * szPathPtr;
|
||||
size_t nLength1 = 0;
|
||||
size_t nLength2 = 0;
|
||||
|
||||
// Calculate lengths of each part
|
||||
if(szDirectory != NULL)
|
||||
{
|
||||
// Get the length of the directory
|
||||
nLength1 = _tcslen(szDirectory);
|
||||
|
||||
// Cut all ending backslashes
|
||||
while(nLength1 > 0 && szDirectory[nLength1 - 1] == _T(PATH_SEPARATOR))
|
||||
nLength1--;
|
||||
}
|
||||
|
||||
if(szSubDir != NULL)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
memcpy(szPathPtr, szDirectory, (nLength1 * sizeof(TCHAR)));
|
||||
szPathPtr += nLength1;
|
||||
}
|
||||
|
||||
// Copy the sub-directory
|
||||
if(szSubDir != NULL && nLength2 != 0)
|
||||
{
|
||||
// Append backslash to the previous one
|
||||
if(szPathPtr > szFullPath)
|
||||
*szPathPtr++ = _T(PATH_SEPARATOR);
|
||||
|
||||
// Copy the string
|
||||
memcpy(szPathPtr, szSubDir, (nLength2 * sizeof(TCHAR)));
|
||||
szPathPtr += nLength2;
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
szPathPtr[0] = 0;
|
||||
}
|
||||
|
||||
return szFullPath;
|
||||
}
|
||||
|
||||
void NormalizeFileName_UpperBkSlash(char * szFileName)
|
||||
{
|
||||
// Normalize the file name: ToLower + BackSlashToSlash
|
||||
for(size_t i = 0; szFileName[i] != 0; i++)
|
||||
szFileName[i] = AsciiToUpperTable[szFileName[i]];
|
||||
}
|
||||
|
||||
void NormalizeFileName_LowerSlash(char * szFileName)
|
||||
{
|
||||
// Normalize the file name: ToLower + BackSlashToSlash
|
||||
for(size_t i = 0; szFileName[i] != 0; i++)
|
||||
{
|
||||
szFileName[i] = AsciiToLowerTable[szFileName[i]];
|
||||
szFileName[i] = (szFileName[i] != '\\') ? szFileName[i] : '/';
|
||||
}
|
||||
}
|
||||
|
||||
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue)
|
||||
{
|
||||
BYTE Digit;
|
||||
|
||||
Digit = (BYTE)(AsciiToUpperTable[szString[0]] - _T('0'));
|
||||
if(Digit > 9)
|
||||
Digit -= 'A' - '9' - 1;
|
||||
|
||||
PtrValue[0] = Digit;
|
||||
return (Digit > 0x0F) ? ERROR_BAD_FORMAT : ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue)
|
||||
{
|
||||
// The number of digits must be even
|
||||
assert((nMaxDigits & 0x01) == 0);
|
||||
assert(nMaxDigits <= 8);
|
||||
|
||||
// Prepare the variables
|
||||
PtrValue[0] = 0;
|
||||
nMaxDigits >>= 1;
|
||||
|
||||
// Convert the string up to the number of digits
|
||||
for(size_t i = 0; i < nMaxDigits; i++)
|
||||
{
|
||||
BYTE DigitOne;
|
||||
BYTE DigitTwo;
|
||||
|
||||
DigitOne = (BYTE)(AsciiToUpperTable[szString[0]] - _T('0'));
|
||||
if(DigitOne > 9)
|
||||
DigitOne -= 'A' - '9' - 1;
|
||||
|
||||
DigitTwo = (BYTE)(AsciiToUpperTable[szString[1]] - _T('0'));
|
||||
if(DigitTwo > 9)
|
||||
DigitTwo -= 'A' - '9' - 1;
|
||||
|
||||
if(DigitOne > 0x0F || DigitTwo > 0x0F)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
PtrValue[0] = (PtrValue[0] << 0x08) | (DigitOne << 0x04) | DigitTwo;
|
||||
szString += 2;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
*szBuffer++ = IntToHexChar[pbBinary[0] >> 0x04];
|
||||
*szBuffer++ = IntToHexChar[pbBinary[0] & 0x0F];
|
||||
pbBinary++;
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*szBuffer = 0;
|
||||
return szSaveBuffer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File name utilities
|
||||
|
||||
const wchar_t * GetPlainFileName(const wchar_t * szFileName)
|
||||
{
|
||||
const wchar_t * szPlainName = szFileName;
|
||||
|
||||
while(*szFileName != 0)
|
||||
{
|
||||
if(*szFileName == '\\' || *szFileName == '/')
|
||||
szPlainName = szFileName + 1;
|
||||
szFileName++;
|
||||
}
|
||||
|
||||
return szPlainName;
|
||||
}
|
||||
|
||||
const char * GetPlainFileName(const char * szFileName)
|
||||
{
|
||||
const char * szPlainName = szFileName;
|
||||
|
||||
while(*szFileName != 0)
|
||||
{
|
||||
if(*szFileName == '\\' || *szFileName == '/')
|
||||
szPlainName = szFileName + 1;
|
||||
szFileName++;
|
||||
}
|
||||
|
||||
return szPlainName;
|
||||
}
|
||||
|
||||
bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||
{
|
||||
const char * szSubString;
|
||||
int nSubStringLength;
|
||||
int nMatchCount = 0;
|
||||
|
||||
// When the mask is empty, it never matches
|
||||
if(szWildCard == NULL || *szWildCard == 0)
|
||||
return false;
|
||||
|
||||
// If the wildcard contains just "*", then it always matches
|
||||
if(szWildCard[0] == '*' && szWildCard[1] == 0)
|
||||
return true;
|
||||
|
||||
// Do normal test
|
||||
for(;;)
|
||||
{
|
||||
// If there is '?' in the wildcard, we skip one char
|
||||
while(*szWildCard == '?')
|
||||
{
|
||||
szWildCard++;
|
||||
szString++;
|
||||
}
|
||||
|
||||
// If there is '*', means zero or more chars. We have to
|
||||
// find the sequence after '*'
|
||||
if(*szWildCard == '*')
|
||||
{
|
||||
// More stars is equal to one star
|
||||
while(*szWildCard == '*' || *szWildCard == '?')
|
||||
szWildCard++;
|
||||
|
||||
// If we found end of the wildcard, it's a match
|
||||
if(*szWildCard == 0)
|
||||
return true;
|
||||
|
||||
// Determine the length of the substring in szWildCard
|
||||
szSubString = szWildCard;
|
||||
while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*')
|
||||
szSubString++;
|
||||
nSubStringLength = (int)(szSubString - szWildCard);
|
||||
nMatchCount = 0;
|
||||
|
||||
// Now we have to find a substring in szString,
|
||||
// that matches the substring in szWildCard
|
||||
while(*szString != 0)
|
||||
{
|
||||
// Calculate match count
|
||||
while(nMatchCount < nSubStringLength)
|
||||
{
|
||||
if(AsciiToUpperTable[(BYTE)szString[nMatchCount]] != AsciiToUpperTable[(BYTE)szWildCard[nMatchCount]])
|
||||
break;
|
||||
if(szString[nMatchCount] == 0)
|
||||
break;
|
||||
nMatchCount++;
|
||||
}
|
||||
|
||||
// If the match count has reached substring length, we found a match
|
||||
if(nMatchCount == nSubStringLength)
|
||||
{
|
||||
szWildCard += nMatchCount;
|
||||
szString += nMatchCount;
|
||||
break;
|
||||
}
|
||||
|
||||
// No match, move to the next char in szString
|
||||
nMatchCount = 0;
|
||||
szString++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we came to the end of the string, compare it to the wildcard
|
||||
if(AsciiToUpperTable[(BYTE)*szString] != AsciiToUpperTable[(BYTE)*szWildCard])
|
||||
return false;
|
||||
|
||||
// If we arrived to the end of the string, it's a match
|
||||
if(*szString == 0)
|
||||
return true;
|
||||
|
||||
// Otherwise, continue in comparing
|
||||
szWildCard++;
|
||||
szString++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hashing functions
|
||||
|
||||
bool IsValidMD5(LPBYTE pbMd5)
|
||||
{
|
||||
BYTE BitSummary = 0;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)
|
||||
{
|
||||
hash_state md5_state;
|
||||
BYTE md5_digest[MD5_HASH_SIZE];
|
||||
|
||||
// Don't verify the block if the MD5 is not valid.
|
||||
if(!IsValidMD5(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);
|
||||
|
||||
// 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)
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
98
dep/CascLib/src/common/Common.h
Normal file
98
dep/CascLib/src/common/Common.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*****************************************************************************/
|
||||
/* CascCommon.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascCommon.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common macros
|
||||
|
||||
// Macro for building 64-bit file offset from two 32-bit
|
||||
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo)
|
||||
|
||||
#ifndef ALIGN_TO_SIZE
|
||||
#define ALIGN_TO_SIZE(x, a) (((x) + (a)-1) & ~((a)-1))
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Conversion tables
|
||||
|
||||
extern unsigned char AsciiToLowerTable[256];
|
||||
extern unsigned char AsciiToUpperTable[256];
|
||||
extern unsigned char IntToHexChar[];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Memory management helper
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
void * DbgRealloc(void * ptr, size_t nSize);
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetLastError/SetLastError support for non-Windows platform
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
int GetLastError();
|
||||
void SetLastError(int nError);
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String manipulation
|
||||
|
||||
void CopyString(char * szTarget, const char * szSource, size_t cchLength);
|
||||
void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength);
|
||||
void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength);
|
||||
|
||||
char * NewStr(const char * szString, size_t nCharsToReserve);
|
||||
wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve);
|
||||
|
||||
TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd);
|
||||
|
||||
TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir);
|
||||
|
||||
void NormalizeFileName_UpperBkSlash(char * szFileName);
|
||||
void NormalizeFileName_LowerSlash(char * szFileName);
|
||||
|
||||
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
|
||||
int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue);
|
||||
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scanning a directory
|
||||
|
||||
typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext);
|
||||
|
||||
bool DirectoryExists(const TCHAR * szDirectory);
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD IndexArray,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext
|
||||
);
|
||||
|
||||
#endif // __COMMON_H__
|
||||
102
dep/CascLib/src/common/Directory.cpp
Normal file
102
dep/CascLib/src/common/Directory.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/*****************************************************************************/
|
||||
/* Directory.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* System-dependent directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of Directory.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
bool DirectoryExists(const TCHAR * szDirectory)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
DWORD dwAttributes = GetFileAttributes(szDirectory);
|
||||
if((dwAttributes != INVALID_FILE_ATTRIBUTES) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return true;
|
||||
|
||||
#else // PLATFORM_WINDOWS
|
||||
|
||||
DIR * dir = opendir(szDirectory);
|
||||
|
||||
if(dir != NULL)
|
||||
{
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD MainIndexes,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
WIN32_FIND_DATA wf;
|
||||
TCHAR * szSearchMask;
|
||||
HANDLE hFind;
|
||||
|
||||
// Prepare the search mask
|
||||
szSearchMask = CombinePath(szIndexPath, _T("*"));
|
||||
if(szSearchMask == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Prepare directory search
|
||||
hFind = FindFirstFile(szSearchMask, &wf);
|
||||
if(hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Skip the first file as it's always just "." or ".."
|
||||
while(FindNextFile(hFind, &wf))
|
||||
{
|
||||
// If the found object is a file, pass it to the handler
|
||||
if(!(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
// Let the callback scan the file name
|
||||
pfnOnFileFound(wf.cFileName, MainIndexes, OldIndexArray, pvContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the search handle
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
CASC_FREE(szSearchMask);
|
||||
|
||||
#else // PLATFORM_WINDOWS
|
||||
|
||||
struct dirent * dir_entry;
|
||||
DIR * dir;
|
||||
|
||||
dir = opendir(szIndexPath);
|
||||
if(dir != NULL)
|
||||
{
|
||||
while((dir_entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if(dir_entry->d_type != DT_DIR)
|
||||
{
|
||||
pfnOnFileFound(dir_entry->d_name, MainIndexes, OldIndexArray, pvContext);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
2721
dep/CascLib/src/common/FileStream.cpp
Normal file
2721
dep/CascLib/src/common/FileStream.cpp
Normal file
File diff suppressed because it is too large
Load Diff
238
dep/CascLib/src/common/FileStream.h
Normal file
238
dep/CascLib/src/common/FileStream.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/*****************************************************************************/
|
||||
/* FileStream.h Copyright (c) Ladislav Zezula 2012 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: Definitions for FileStream object */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 14.04.12 1.00 Lad The first version of FileStream.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __FILESTREAM_H__
|
||||
#define __FILESTREAM_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
|
||||
typedef void (*STREAM_INIT)(
|
||||
struct TFileStream * pStream // Pointer to an unopened stream
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_CREATE)(
|
||||
struct TFileStream * pStream // Pointer to an unopened stream
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_OPEN)(
|
||||
struct TFileStream * pStream, // Pointer to an unopened stream
|
||||
const TCHAR * szFileName, // Pointer to file name to be open
|
||||
DWORD dwStreamFlags // Stream flags
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_READ)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
||||
void * pvBuffer, // Pointer to data to be read
|
||||
DWORD dwBytesToRead // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_WRITE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
||||
const void * pvBuffer, // Pointer to data to be written
|
||||
DWORD dwBytesToWrite // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_RESIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG FileSize // New size for the file, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETSIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pFileSize // Receives the file size, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETPOS)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset // Pointer to store current file position
|
||||
);
|
||||
|
||||
typedef void (*STREAM_CLOSE)(
|
||||
struct TFileStream * pStream // Pointer to an open stream
|
||||
);
|
||||
|
||||
typedef bool (*BLOCK_READ)(
|
||||
struct TFileStream * pStream, // Pointer to a block-oriented stream
|
||||
ULONGLONG StartOffset, // Byte offset of start of the block array
|
||||
ULONGLONG EndOffset, // End offset (either end of the block or end of the file)
|
||||
LPBYTE BlockBuffer, // Pointer to block-aligned buffer
|
||||
DWORD BytesNeeded, // Number of bytes that are really needed
|
||||
bool bAvailable // true if the block is available
|
||||
);
|
||||
|
||||
typedef bool (*BLOCK_CHECK)(
|
||||
struct TFileStream * pStream, // Pointer to a block-oriented stream
|
||||
ULONGLONG BlockOffset // Offset of the file to check
|
||||
);
|
||||
|
||||
typedef void (*BLOCK_SAVEMAP)(
|
||||
struct TFileStream * pStream // Pointer to a block-oriented stream
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures - partial file structure and bitmap footer
|
||||
|
||||
#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3')
|
||||
#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block
|
||||
#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs
|
||||
|
||||
typedef struct _PART_FILE_HEADER
|
||||
{
|
||||
DWORD PartialVersion; // Always set to 2
|
||||
char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ
|
||||
DWORD Flags; // Flags (details unknown)
|
||||
DWORD FileSizeLo; // Low 32 bits of the contained file size
|
||||
DWORD FileSizeHi; // High 32 bits of the contained file size
|
||||
DWORD BlockSize; // Size of one file block, in bytes
|
||||
|
||||
} PART_FILE_HEADER, *PPART_FILE_HEADER;
|
||||
|
||||
// Structure describing the block-to-file map entry
|
||||
typedef struct _PART_FILE_MAP_ENTRY
|
||||
{
|
||||
DWORD Flags; // 3 = the block is present in the file
|
||||
DWORD BlockOffsLo; // Low 32 bits of the block position in the file
|
||||
DWORD BlockOffsHi; // High 32 bits of the block position in the file
|
||||
DWORD LargeValueLo; // 64-bit value, meaning is unknown
|
||||
DWORD LargeValueHi;
|
||||
|
||||
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
|
||||
|
||||
typedef struct _FILE_BITMAP_FOOTER
|
||||
{
|
||||
DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER)
|
||||
DWORD Version; // Unknown, seems to always have value of 3 (version?)
|
||||
DWORD BuildNumber; // Game build number for that MPQ
|
||||
DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map
|
||||
DWORD MapOffsetHi; // High 32-bits of the offset of the bit map
|
||||
DWORD BlockSize; // Size of one block (usually 0x4000 bytes)
|
||||
|
||||
} FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for file stream
|
||||
|
||||
union TBaseProviderData
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Last write time
|
||||
HANDLE hFile; // File handle
|
||||
} File;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Last write time
|
||||
LPBYTE pbFile; // Pointer to mapped view
|
||||
} Map;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Last write time
|
||||
HANDLE hInternet; // Internet handle
|
||||
HANDLE hConnect; // Connection to the internet server
|
||||
} Http;
|
||||
};
|
||||
|
||||
struct TFileStream
|
||||
{
|
||||
// Stream provider functions
|
||||
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
|
||||
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
|
||||
STREAM_RESIZE StreamResize; // Pointer to function changing file size
|
||||
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
|
||||
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
|
||||
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
|
||||
|
||||
// Block-oriented functions
|
||||
BLOCK_READ BlockRead; // Pointer to function reading one or more blocks
|
||||
BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present
|
||||
|
||||
// Base provider functions
|
||||
STREAM_CREATE BaseCreate; // Pointer to base create function
|
||||
STREAM_OPEN BaseOpen; // Pointer to base open function
|
||||
STREAM_READ BaseRead; // Read from the stream
|
||||
STREAM_WRITE BaseWrite; // Write to the stream
|
||||
STREAM_RESIZE BaseResize; // Pointer to function changing file size
|
||||
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
|
||||
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
||||
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
|
||||
|
||||
// Base provider data (file size, file position)
|
||||
TBaseProviderData Base;
|
||||
|
||||
// Stream provider data
|
||||
TFileStream * pMaster; // Master stream (e.g. MPQ on a web server)
|
||||
TCHAR * szFileName; // File name (self-relative pointer)
|
||||
|
||||
ULONGLONG StreamSize; // Stream size (can be less than file size)
|
||||
ULONGLONG StreamPos; // Stream position
|
||||
DWORD BuildNumber; // Game build number
|
||||
DWORD dwFlags; // Stream flags
|
||||
|
||||
// Followed by stream provider data, with variable length
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures for block-oriented stream
|
||||
|
||||
struct TBlockStream : public TFileStream
|
||||
{
|
||||
STREAM_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading
|
||||
void * FileBitmap; // Array of bits for file blocks
|
||||
void * UserData; // User data to be passed to the download callback
|
||||
DWORD BitmapSize; // Size of the file bitmap (in bytes)
|
||||
DWORD BlockSize; // Size of one block, in bytes
|
||||
DWORD BlockCount; // Number of data blocks in the file
|
||||
DWORD IsComplete; // If nonzero, no blocks are missing
|
||||
DWORD IsModified; // nonzero if the bitmap has been modified
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for encrypted stream
|
||||
|
||||
#define ENCRYPTED_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
|
||||
|
||||
struct TEncryptedStream : public TBlockStream
|
||||
{
|
||||
BYTE Key[ENCRYPTED_CHUNK_SIZE]; // File key
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions for file stream
|
||||
|
||||
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
const TCHAR * FileStream_GetFileName(TFileStream * pStream);
|
||||
size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider);
|
||||
|
||||
bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData);
|
||||
|
||||
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);
|
||||
bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite);
|
||||
bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize);
|
||||
bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize);
|
||||
bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset);
|
||||
bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT);
|
||||
bool FileStream_GetFlags(TFileStream * pStream, PDWORD pdwStreamFlags);
|
||||
bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream);
|
||||
void FileStream_Close(TFileStream * pStream);
|
||||
|
||||
|
||||
#endif // __FILESTREAM_H__
|
||||
266
dep/CascLib/src/common/ListFile.cpp
Normal file
266
dep/CascLib/src/common/ListFile.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/*****************************************************************************/
|
||||
/* ListFile.cpp Copyright (c) Ladislav Zezula 2004 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 12.06.04 1.00 Lad The first version of ListFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile entry structure
|
||||
|
||||
#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
|
||||
|
||||
typedef bool (*RELOAD_CACHE)(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead);
|
||||
typedef void (*CLOSE_STREAM)(void * pvCacheContext);
|
||||
|
||||
struct TListFileCache
|
||||
{
|
||||
RELOAD_CACHE pfnReloadCache; // Function for reloading the cache
|
||||
CLOSE_STREAM pfnCloseStream; // Function for closing the stream
|
||||
void * pvCacheContext; // Reload context passed to reload function
|
||||
char * szMask; // Self-relative pointer to file mask
|
||||
DWORD dwFileSize; // Total size of the cached file
|
||||
DWORD dwFilePos; // Position of the cache in the file
|
||||
BYTE * pBegin; // The begin of the listfile cache
|
||||
BYTE * pPos;
|
||||
BYTE * pEnd; // The last character in the file cache
|
||||
|
||||
BYTE Buffer[CACHE_BUFFER_SIZE];
|
||||
// char MaskBuff[1] // Followed by the name mask (if any)
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool ReloadCache_ExternalFile(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead)
|
||||
{
|
||||
TFileStream * pStream = (TFileStream *)pvCacheContext;
|
||||
|
||||
return FileStream_Read(pStream, NULL, pbBuffer, dwBytesToRead);
|
||||
}
|
||||
|
||||
static void CloseStream_ExternalFile(void * pvCacheContext)
|
||||
{
|
||||
TFileStream * pStream = (TFileStream *)pvCacheContext;
|
||||
|
||||
return FileStream_Close(pStream);
|
||||
}
|
||||
|
||||
|
||||
// Reloads the cache. Returns number of characters
|
||||
// that has been loaded into the cache.
|
||||
static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||
{
|
||||
DWORD dwBytesToRead = 0;
|
||||
|
||||
// Only do something if the cache is empty
|
||||
if(pCache->pPos >= pCache->pEnd)
|
||||
{
|
||||
// Move the file position forward
|
||||
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
||||
if(pCache->dwFilePos >= pCache->dwFileSize)
|
||||
return 0;
|
||||
|
||||
// Get the number of bytes remaining
|
||||
dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos;
|
||||
if(dwBytesToRead > CACHE_BUFFER_SIZE)
|
||||
dwBytesToRead = CACHE_BUFFER_SIZE;
|
||||
|
||||
// Load the next data chunk to the cache
|
||||
// If we didn't read anything, it might mean that the block
|
||||
// of the file is not available
|
||||
// We stop reading the file at this point, because the rest
|
||||
// of the listfile is unreliable
|
||||
if(!pCache->pfnReloadCache(pCache->pvCacheContext, pCache->Buffer, dwBytesToRead))
|
||||
return 0;
|
||||
|
||||
// Set the buffer pointers
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesToRead;
|
||||
}
|
||||
|
||||
return dwBytesToRead;
|
||||
}
|
||||
|
||||
static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nMaxChars)
|
||||
{
|
||||
char * szLineBegin = szLine;
|
||||
char * szLineEnd = szLine + nMaxChars - 1;
|
||||
char * szExtraString = NULL;
|
||||
|
||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||
for(;;)
|
||||
{
|
||||
// If we need to reload the cache, do it
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a non-whitespace character, stop
|
||||
if(pCache->pPos[0] > 0x20)
|
||||
break;
|
||||
|
||||
// Skip the character
|
||||
pCache->pPos++;
|
||||
}
|
||||
|
||||
// Copy the remaining characters
|
||||
while(szLine < szLineEnd)
|
||||
{
|
||||
// If we need to reload the cache, do it now and resume copying
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have found a newline, stop loading
|
||||
if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
|
||||
break;
|
||||
|
||||
// Blizzard listfiles can also contain information about patch:
|
||||
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
|
||||
if(*pCache->pPos == '~')
|
||||
szExtraString = szLine;
|
||||
|
||||
// Copy the character
|
||||
*szLine++ = *pCache->pPos++;
|
||||
}
|
||||
|
||||
// Terminate line with zero
|
||||
*szLine = 0;
|
||||
|
||||
// If there was extra string after the file name, clear it
|
||||
if(szExtraString != NULL)
|
||||
{
|
||||
if(szExtraString[0] == '~' && szExtraString[1] == 'P')
|
||||
{
|
||||
szLine = szExtraString;
|
||||
*szExtraString = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the length of the line
|
||||
return (szLine - szLineBegin);
|
||||
}
|
||||
|
||||
static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_STREAM pfnCloseStream, void * pvCacheContext, DWORD dwFileSize)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
DWORD dwBytesToRead;
|
||||
|
||||
// Allocate cache for one file block
|
||||
pCache = (TListFileCache *)CASC_ALLOC(BYTE, sizeof(TListFileCache));
|
||||
if(pCache != NULL)
|
||||
{
|
||||
// Clear the entire structure
|
||||
memset(pCache, 0, sizeof(TListFileCache));
|
||||
pCache->pfnReloadCache = pfnReloadCache;
|
||||
pCache->pfnCloseStream = pfnCloseStream;
|
||||
pCache->pvCacheContext = pvCacheContext;
|
||||
pCache->dwFileSize = dwFileSize;
|
||||
|
||||
// Load the file cache from the file
|
||||
dwBytesToRead = CASCLIB_MIN(CACHE_BUFFER_SIZE, dwFileSize);
|
||||
if(pfnReloadCache(pvCacheContext, pCache->Buffer, dwBytesToRead))
|
||||
{
|
||||
// Allocate pointers
|
||||
pCache->pBegin = pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesToRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
ListFile_Free(pCache);
|
||||
pCache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the cache
|
||||
return pCache;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile functions
|
||||
|
||||
void * ListFile_OpenExternal(const TCHAR * szListFile)
|
||||
{
|
||||
TListFileCache * pCache;
|
||||
TFileStream * pStream;
|
||||
ULONGLONG FileSize = 0;
|
||||
|
||||
// Open the external listfile
|
||||
pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY);
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Retrieve the size of the external listfile
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if(0 < FileSize && FileSize <= 0xFFFFFFFF)
|
||||
{
|
||||
// Create the cache for the listfile
|
||||
pCache = CreateListFileCache(ReloadCache_ExternalFile, CloseStream_ExternalFile, pStream, (DWORD)FileSize);
|
||||
if(pCache != NULL)
|
||||
return pCache;
|
||||
}
|
||||
|
||||
// Close the file stream
|
||||
FileStream_Close(pStream);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)pvListFile;
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Check for parameters
|
||||
if(pCache != NULL)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
// Read the (next) line
|
||||
nLength = ReadListFileLine(pCache, szBuffer, nMaxChars);
|
||||
if(nLength == 0)
|
||||
{
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
|
||||
// If some mask entered, check it
|
||||
if(CheckWildCard(szBuffer, szMask))
|
||||
{
|
||||
nError = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return nLength;
|
||||
}
|
||||
|
||||
void ListFile_Free(void * pvListFile)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)pvListFile;
|
||||
|
||||
// Valid parameter check
|
||||
if(pCache != NULL)
|
||||
{
|
||||
if(pCache->pfnCloseStream != NULL)
|
||||
pCache->pfnCloseStream(pCache->pvCacheContext);
|
||||
CASC_FREE(pCache);
|
||||
}
|
||||
}
|
||||
18
dep/CascLib/src/common/ListFile.h
Normal file
18
dep/CascLib/src/common/ListFile.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*****************************************************************************/
|
||||
/* ListFile.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 10.05.14 1.00 Lad The first version of ListFile.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __LISTFILE_H__
|
||||
#define __LISTFILE_H__
|
||||
|
||||
void * ListFile_OpenExternal(const TCHAR * szListFile);
|
||||
size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars);
|
||||
void ListFile_Free(void * pvListFile);
|
||||
|
||||
#endif // __LISTFILE_H__
|
||||
178
dep/CascLib/src/common/Map.cpp
Normal file
178
dep/CascLib/src/common/Map.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*****************************************************************************/
|
||||
/* 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
|
||||
|
||||
static DWORD CalcHashIndex(PCASC_MAP pMap, void * pvIdentifier)
|
||||
{
|
||||
DWORD dwHash = 0x7EEE7EEE;
|
||||
|
||||
// Is it a string table?
|
||||
if(pMap->KeyLength == KEY_LENGTH_STRING)
|
||||
{
|
||||
char * szString = (char *)pvIdentifier;
|
||||
|
||||
for(size_t i = 0; szString[i] != 0; i++)
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
LPBYTE pbHash = (LPBYTE)pvIdentifier;
|
||||
|
||||
// Construct the hash from the first 4 digits
|
||||
for(size_t i = 0; i < pMap->KeyLength; i++)
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbHash[i];
|
||||
}
|
||||
|
||||
// Return the hash limited by the table size
|
||||
return (dwHash % pMap->TableSize);
|
||||
}
|
||||
|
||||
static bool CompareIdentifier(PCASC_MAP pMap, void * pvTableEntry, void * pvIdentifier)
|
||||
{
|
||||
// Is it a string table?
|
||||
if(pMap->KeyLength == KEY_LENGTH_STRING)
|
||||
{
|
||||
char * szTableEntry = (char *)pvTableEntry;
|
||||
char * szIdentifier = (char *)pvIdentifier;
|
||||
|
||||
return (strcmp(szTableEntry, szIdentifier) == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(pvTableEntry, pvIdentifier, pMap->KeyLength) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset)
|
||||
{
|
||||
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->MemberOffset = dwMemberOffset;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
{
|
||||
void * pvTableEntry;
|
||||
DWORD dwHashIndex;
|
||||
|
||||
// Verify pointer to the map
|
||||
if(pMap != NULL)
|
||||
{
|
||||
// Construct the main index
|
||||
dwHashIndex = CalcHashIndex(pMap, pvIdentifier);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
pvTableEntry = pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Compare the hash
|
||||
if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier))
|
||||
return ((LPBYTE)pvTableEntry - pMap->MemberOffset);
|
||||
|
||||
// Move to the next entry
|
||||
dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
{
|
||||
void * pvTableEntry;
|
||||
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(pMap, pvIdentifier);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
pvTableEntry = pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Check if hash being inserted conflicts with an existing hash
|
||||
if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier))
|
||||
return false;
|
||||
|
||||
// Move to the next entry
|
||||
dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
|
||||
}
|
||||
|
||||
// Insert at that position
|
||||
pMap->HashTable[dwHashIndex] = pvIdentifier;
|
||||
pMap->ItemCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
|
||||
void Map_Free(PCASC_MAP pMap)
|
||||
{
|
||||
if(pMap != NULL)
|
||||
{
|
||||
CASC_FREE(pMap);
|
||||
}
|
||||
}
|
||||
39
dep/CascLib/src/common/Map.h
Normal file
39
dep/CascLib/src/common/Map.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*****************************************************************************/
|
||||
/* Map.h Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Interface of hash-to-ptr map for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 10.06.14 1.00 Lad The first version of Map.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __HASHTOPTR_H__
|
||||
#define __HASHTOPTR_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object
|
||||
|
||||
typedef struct _CASC_MAP
|
||||
{
|
||||
size_t TableSize;
|
||||
size_t ItemCount; // Number of items in the map
|
||||
size_t MemberOffset; // How far is the hash from the begin of the structure (in bytes)
|
||||
size_t KeyLength; // Length of the hash key
|
||||
void * HashTable[1]; // Pointer table
|
||||
|
||||
} CASC_MAP, *PCASC_MAP;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset);
|
||||
size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray);
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier);
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier);
|
||||
void Map_Sort(PCASC_MAP pMap);
|
||||
void Map_Free(PCASC_MAP pMap);
|
||||
|
||||
#endif // __HASHTOPTR_H__
|
||||
@@ -1,272 +0,0 @@
|
||||
set(SRC_FILES
|
||||
src/adpcm/adpcm.cpp
|
||||
src/huffman/huff.cpp
|
||||
src/jenkins/lookup3.c
|
||||
src/lzma/C/LzFind.c
|
||||
src/lzma/C/LzmaDec.c
|
||||
src/lzma/C/LzmaEnc.c
|
||||
src/pklib/explode.c
|
||||
src/pklib/implode.c
|
||||
src/sparse/sparse.cpp
|
||||
src/FileStream.cpp
|
||||
src/SBaseCommon.cpp
|
||||
src/SBaseFileTable.cpp
|
||||
src/SCompression.cpp
|
||||
src/SFileAddFile.cpp
|
||||
src/SFileAttributes.cpp
|
||||
src/SFileCompactArchive.cpp
|
||||
src/SFileCreateArchive.cpp
|
||||
src/SFileExtractFile.cpp
|
||||
src/SFileFindFile.cpp
|
||||
src/SFileListFile.cpp
|
||||
src/SFileOpenArchive.cpp
|
||||
src/SFileOpenFileEx.cpp
|
||||
src/SFilePatchArchives.cpp
|
||||
src/SFileReadFile.cpp
|
||||
src/SFileVerify.cpp
|
||||
)
|
||||
|
||||
set(TOMCRYPT_FILES
|
||||
src/libtomcrypt/src/hashes/hash_memory.c
|
||||
src/libtomcrypt/src/hashes/md5.c
|
||||
src/libtomcrypt/src/hashes/sha1.c
|
||||
src/libtomcrypt/src/math/ltm_desc.c
|
||||
src/libtomcrypt/src/math/multi.c
|
||||
src/libtomcrypt/src/math/rand_prime.c
|
||||
src/libtomcrypt/src/misc/base64_decode.c
|
||||
src/libtomcrypt/src/misc/crypt_argchk.c
|
||||
src/libtomcrypt/src/misc/crypt_find_hash.c
|
||||
src/libtomcrypt/src/misc/crypt_find_prng.c
|
||||
src/libtomcrypt/src/misc/crypt_hash_descriptor.c
|
||||
src/libtomcrypt/src/misc/crypt_hash_is_valid.c
|
||||
src/libtomcrypt/src/misc/crypt_libc.c
|
||||
src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c
|
||||
src/libtomcrypt/src/misc/crypt_prng_descriptor.c
|
||||
src/libtomcrypt/src/misc/crypt_prng_is_valid.c
|
||||
src/libtomcrypt/src/misc/crypt_register_hash.c
|
||||
src/libtomcrypt/src/misc/crypt_register_prng.c
|
||||
src/libtomcrypt/src/misc/zeromem.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_bit_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_boolean.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_choice.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_ia5_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_integer.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_object_identifier.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_octet_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_printable_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_utctime.c
|
||||
src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_bit_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_boolean.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_integer.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_object_identifier.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_octet_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_printable_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_sequence.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_utctime.c
|
||||
src/libtomcrypt/src/pk/asn1/der_sequence_free.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_utf8_string.c
|
||||
src/libtomcrypt/src/pk/asn1/der_length_short_integer.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_map.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c
|
||||
src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c
|
||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
|
||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
|
||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
|
||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_exptmod.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_free.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_import.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_make_key.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c
|
||||
)
|
||||
|
||||
set(TOMMATH_FILES
|
||||
src/libtommath/bncore.c
|
||||
src/libtommath/bn_fast_mp_invmod.c
|
||||
src/libtommath/bn_fast_mp_montgomery_reduce.c
|
||||
src/libtommath/bn_fast_s_mp_mul_digs.c
|
||||
src/libtommath/bn_fast_s_mp_mul_high_digs.c
|
||||
src/libtommath/bn_fast_s_mp_sqr.c
|
||||
src/libtommath/bn_mp_2expt.c
|
||||
src/libtommath/bn_mp_abs.c
|
||||
src/libtommath/bn_mp_add.c
|
||||
src/libtommath/bn_mp_addmod.c
|
||||
src/libtommath/bn_mp_add_d.c
|
||||
src/libtommath/bn_mp_and.c
|
||||
src/libtommath/bn_mp_clamp.c
|
||||
src/libtommath/bn_mp_clear.c
|
||||
src/libtommath/bn_mp_clear_multi.c
|
||||
src/libtommath/bn_mp_cmp.c
|
||||
src/libtommath/bn_mp_cmp_d.c
|
||||
src/libtommath/bn_mp_cmp_mag.c
|
||||
src/libtommath/bn_mp_cnt_lsb.c
|
||||
src/libtommath/bn_mp_copy.c
|
||||
src/libtommath/bn_mp_count_bits.c
|
||||
src/libtommath/bn_mp_div.c
|
||||
src/libtommath/bn_mp_div_2.c
|
||||
src/libtommath/bn_mp_div_2d.c
|
||||
src/libtommath/bn_mp_div_3.c
|
||||
src/libtommath/bn_mp_div_d.c
|
||||
src/libtommath/bn_mp_dr_is_modulus.c
|
||||
src/libtommath/bn_mp_dr_reduce.c
|
||||
src/libtommath/bn_mp_dr_setup.c
|
||||
src/libtommath/bn_mp_exch.c
|
||||
src/libtommath/bn_mp_exptmod.c
|
||||
src/libtommath/bn_mp_exptmod_fast.c
|
||||
src/libtommath/bn_mp_expt_d.c
|
||||
src/libtommath/bn_mp_exteuclid.c
|
||||
src/libtommath/bn_mp_fread.c
|
||||
src/libtommath/bn_mp_fwrite.c
|
||||
src/libtommath/bn_mp_gcd.c
|
||||
src/libtommath/bn_mp_get_int.c
|
||||
src/libtommath/bn_mp_grow.c
|
||||
src/libtommath/bn_mp_init.c
|
||||
src/libtommath/bn_mp_init_copy.c
|
||||
src/libtommath/bn_mp_init_multi.c
|
||||
src/libtommath/bn_mp_init_set.c
|
||||
src/libtommath/bn_mp_init_set_int.c
|
||||
src/libtommath/bn_mp_init_size.c
|
||||
src/libtommath/bn_mp_invmod.c
|
||||
src/libtommath/bn_mp_invmod_slow.c
|
||||
src/libtommath/bn_mp_is_square.c
|
||||
src/libtommath/bn_mp_jacobi.c
|
||||
src/libtommath/bn_mp_karatsuba_mul.c
|
||||
src/libtommath/bn_mp_karatsuba_sqr.c
|
||||
src/libtommath/bn_mp_lcm.c
|
||||
src/libtommath/bn_mp_lshd.c
|
||||
src/libtommath/bn_mp_mod.c
|
||||
src/libtommath/bn_mp_mod_2d.c
|
||||
src/libtommath/bn_mp_mod_d.c
|
||||
src/libtommath/bn_mp_montgomery_calc_normalization.c
|
||||
src/libtommath/bn_mp_montgomery_reduce.c
|
||||
src/libtommath/bn_mp_montgomery_setup.c
|
||||
src/libtommath/bn_mp_mul.c
|
||||
src/libtommath/bn_mp_mulmod.c
|
||||
src/libtommath/bn_mp_mul_2.c
|
||||
src/libtommath/bn_mp_mul_2d.c
|
||||
src/libtommath/bn_mp_mul_d.c
|
||||
src/libtommath/bn_mp_neg.c
|
||||
src/libtommath/bn_mp_n_root.c
|
||||
src/libtommath/bn_mp_or.c
|
||||
src/libtommath/bn_mp_prime_fermat.c
|
||||
src/libtommath/bn_mp_prime_is_divisible.c
|
||||
src/libtommath/bn_mp_prime_is_prime.c
|
||||
src/libtommath/bn_mp_prime_miller_rabin.c
|
||||
src/libtommath/bn_mp_prime_next_prime.c
|
||||
src/libtommath/bn_mp_prime_rabin_miller_trials.c
|
||||
src/libtommath/bn_mp_prime_random_ex.c
|
||||
src/libtommath/bn_mp_radix_size.c
|
||||
src/libtommath/bn_mp_radix_smap.c
|
||||
src/libtommath/bn_mp_rand.c
|
||||
src/libtommath/bn_mp_read_radix.c
|
||||
src/libtommath/bn_mp_read_signed_bin.c
|
||||
src/libtommath/bn_mp_read_unsigned_bin.c
|
||||
src/libtommath/bn_mp_reduce.c
|
||||
src/libtommath/bn_mp_reduce_2k.c
|
||||
src/libtommath/bn_mp_reduce_2k_l.c
|
||||
src/libtommath/bn_mp_reduce_2k_setup.c
|
||||
src/libtommath/bn_mp_reduce_2k_setup_l.c
|
||||
src/libtommath/bn_mp_reduce_is_2k.c
|
||||
src/libtommath/bn_mp_reduce_is_2k_l.c
|
||||
src/libtommath/bn_mp_reduce_setup.c
|
||||
src/libtommath/bn_mp_rshd.c
|
||||
src/libtommath/bn_mp_set.c
|
||||
src/libtommath/bn_mp_set_int.c
|
||||
src/libtommath/bn_mp_shrink.c
|
||||
src/libtommath/bn_mp_signed_bin_size.c
|
||||
src/libtommath/bn_mp_sqr.c
|
||||
src/libtommath/bn_mp_sqrmod.c
|
||||
src/libtommath/bn_mp_sqrt.c
|
||||
src/libtommath/bn_mp_sub.c
|
||||
src/libtommath/bn_mp_submod.c
|
||||
src/libtommath/bn_mp_sub_d.c
|
||||
src/libtommath/bn_mp_toom_mul.c
|
||||
src/libtommath/bn_mp_toom_sqr.c
|
||||
src/libtommath/bn_mp_toradix.c
|
||||
src/libtommath/bn_mp_toradix_n.c
|
||||
src/libtommath/bn_mp_to_signed_bin.c
|
||||
src/libtommath/bn_mp_to_signed_bin_n.c
|
||||
src/libtommath/bn_mp_to_unsigned_bin.c
|
||||
src/libtommath/bn_mp_to_unsigned_bin_n.c
|
||||
src/libtommath/bn_mp_unsigned_bin_size.c
|
||||
src/libtommath/bn_mp_xor.c
|
||||
src/libtommath/bn_mp_zero.c
|
||||
src/libtommath/bn_prime_tab.c
|
||||
src/libtommath/bn_reverse.c
|
||||
src/libtommath/bn_s_mp_add.c
|
||||
src/libtommath/bn_s_mp_exptmod.c
|
||||
src/libtommath/bn_s_mp_mul_digs.c
|
||||
src/libtommath/bn_s_mp_mul_high_digs.c
|
||||
src/libtommath/bn_s_mp_sqr.c
|
||||
src/libtommath/bn_s_mp_sub.c
|
||||
)
|
||||
|
||||
set(ZLIB_BZIP2_FILES
|
||||
src/bzip2/blocksort.c
|
||||
src/bzip2/bzlib.c
|
||||
src/bzip2/compress.c
|
||||
src/bzip2/crctable.c
|
||||
src/bzip2/decompress.c
|
||||
src/bzip2/huffman.c
|
||||
src/bzip2/randtable.c
|
||||
src/zlib/adler32.c
|
||||
src/zlib/compress2.c
|
||||
src/zlib/crc32.c
|
||||
src/zlib/deflate.c
|
||||
src/zlib/inffast.c
|
||||
src/zlib/inflate.c
|
||||
src/zlib/inftrees.c
|
||||
src/zlib/trees.c
|
||||
src/zlib/zutil.c
|
||||
)
|
||||
|
||||
set(TEST_SRC_FILES
|
||||
test/Test.cpp
|
||||
)
|
||||
|
||||
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
add_definitions(-D_7ZIP_ST -DWIN32)
|
||||
endif()
|
||||
set(SRC_ADDITIONAL_FILES ${ZLIB_BZIP2_FILES} ${TOMCRYPT_FILES} ${TOMMATH_FILES})
|
||||
set(LINK_LIBS wininet)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(LINK_LIBS z bz2)
|
||||
set(SRC_ADDITIONAL_FILES ${TOMCRYPT_FILES} ${TOMMATH_FILES})
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
||||
option(WITH_LIBTOMCRYPT "Use system LibTomCrypt library" OFF)
|
||||
if(WITH_LIBTOMCRYPT)
|
||||
set(LINK_LIBS z bz2 tomcrypt)
|
||||
else()
|
||||
set(LINK_LIBS z bz2)
|
||||
set(SRC_ADDITIONAL_FILES ${TOMCRYPT_FILES} ${TOMMATH_FILES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(storm STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
||||
target_link_libraries(storm ${LINK_LIBS})
|
||||
|
||||
if(UNIX)
|
||||
set_target_properties(storm PROPERTIES SOVERSION 0)
|
||||
endif()
|
||||
|
||||
# On Win32, build StormLib.dll since we don't want to clash with Storm.dll
|
||||
if(WIN32)
|
||||
set_target_properties(storm PROPERTIES OUTPUT_NAME StormLib)
|
||||
endif()
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
StormLib history
|
||||
================
|
||||
|
||||
Version 8.02
|
||||
|
||||
- Support for UNICODE encoding for on-disk files
|
||||
- Optimized file deleting
|
||||
|
||||
Version 8.01
|
||||
|
||||
- SFileFindFirstFile and SFileFindNextFile no longer find files that have
|
||||
patch file in the oldest MPQ in the patch chain
|
||||
- Write support for MPQs version 4
|
||||
|
||||
Version 8.00
|
||||
|
||||
- Updated support for protected maps from Warcraft III
|
||||
|
||||
Version 7.11
|
||||
|
||||
- Support for MPQs v 3.0 (WOW-Cataclysm BETA)
|
||||
- StormLib now deals properly with files that have MPQ_SECTOR_CHECKSUM missing,
|
||||
but have sector checksum entry present in the sector offset table
|
||||
|
||||
Version 7.10
|
||||
|
||||
- Support for partial MPQs ("interface.MPQ.part")
|
||||
- The only operation that is externally allowed to do with internal files
|
||||
("(listfile)", "(attributes)" and "(signature)") is reading. Attempt to modify any of the file
|
||||
fails and GetLastError returns ERROR_INTERNAL_FILE
|
||||
- Fixed memory leak that has occured when writing more than one sector to the file at once
|
||||
|
||||
Version 7.01
|
||||
|
||||
- Support for adding files from memory
|
||||
- Fixed improper validation of handles to MPQ file and MPQ archive
|
||||
- Fixed bug where StormLib didn't save CRC32 of the file when added to archive
|
||||
|
||||
Version 7.00
|
||||
|
||||
- Properly deals with MPQs protected by w3xMaster
|
||||
- Major rewrite
|
||||
- Fixed support for (attributes)
|
||||
- Added file verification
|
||||
- Added MPQ signature verification
|
||||
|
||||
Version 6.22
|
||||
|
||||
- Properly deals with MPQs protected by w3xMaster
|
||||
|
||||
Version 6.21
|
||||
|
||||
- SFileRenameFile now properly re-crypts the file if necessary.
|
||||
- SFileFindFirstFile correctly deals with deleted files
|
||||
|
||||
Version 6.20
|
||||
|
||||
- Fixed lots of bugs when processing files with same names but different locales
|
||||
- Fixed bugs when repeately extracts the same file with MPQ_FILE_SINGLE_UNIT flag
|
||||
- Added SFileFlushArchive
|
||||
- Fixed issue opening AVI files renamed to MPQ using SFileCreateArchiveEx
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
After sector offset table
|
||||
=========================
|
||||
|
||||
FileSize CmpSize DWORDs
|
||||
00007588 000075A4 0x01
|
||||
0000A9EA 000095EC 0x01
|
||||
0000E51D 0000E20D 0x02
|
||||
00015C00 00015C40 0x02
|
||||
0001C578 000186C4 0x02
|
||||
0002A9D4 0002A9EA 0x04
|
||||
00037BAC 00037A42 0x06
|
||||
0003C3C1 00034084 0x06
|
||||
0003D224 0003B30F 0x06
|
||||
00045105 0004195A 0x07
|
||||
00045D9C 0003D87D 0x07
|
||||
0004AAB8 0004860A 0x08
|
||||
0004D18E 00048E0C 0x07
|
||||
00056B4C 00056BDD 0x09
|
||||
0005DC08 00059426 0x09
|
||||
00061EC0 00057711 0x0A
|
||||
0006CEC4 00062561 0x0B
|
||||
000778EE 00066736 0x0C
|
||||
000AD0CB 0007F32E 0x11
|
||||
00327EAC 00303395 0x53
|
||||
@@ -1,318 +0,0 @@
|
||||
THE MOPAQ ARCHIVE FORMAT
|
||||
v0.9 (Thursday, June 30, 2005)
|
||||
by Justin Olbrantz(Quantam)
|
||||
|
||||
Distribution and reproduction of this specification are allowed without limitation, as long as it is not altered. Quoting
|
||||
in other works is freely allowed, as long as the source and author of the quote is stated.
|
||||
|
||||
TABLE OF CONTENTS
|
||||
1. Introduction to the MoPaQ Format
|
||||
2. The MoPaQ Format
|
||||
2.1 General Archive Layout
|
||||
2.2 Archive Header
|
||||
2.3 Block Table
|
||||
2.4 Hash Table
|
||||
2.5 File Data
|
||||
2.6 Listfile
|
||||
2.7 Extended Attributes
|
||||
2.8 Weak (Old) Digital Signature
|
||||
2.9 Strong (New) Digital Signature
|
||||
3. Algorithm Source Code
|
||||
3.1 Encryption/Decryption
|
||||
3.2 Hashing
|
||||
3.3 Conversion of FILETIME and time_t
|
||||
|
||||
1. INTRODUCTION TO THE MOPAQ FORMAT
|
||||
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard
|
||||
Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be
|
||||
a read-only game archive format, and excels at this role.
|
||||
|
||||
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked.
|
||||
The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
||||
|
||||
2. THE MOPAQ FORMAT
|
||||
All numbers in the MoPaQ format are in little endian. Data types are listed either as int (integer, the number of bits specified),
|
||||
byte (8 bits), and char (bytes which contain ASCII characters). All sizes and offsets are in bytes, unless specified otherwise.
|
||||
Structure members are listed in the following general form:
|
||||
offset from the beginning of the structure: data type(array size) member name : member description
|
||||
|
||||
2.1 GENERAL ARCHIVE LAYOUT
|
||||
- Archive Header
|
||||
- File Data
|
||||
- File Data - Special Files
|
||||
- Hash Table
|
||||
- Block Table
|
||||
- Strong Digital signature
|
||||
|
||||
This is the usual archive format, and is not absolutely essential. Some archives have been observed placing the hash table
|
||||
and file table after the archive header, and before the file data.
|
||||
|
||||
2.2 ARCHIVE HEADER
|
||||
00h: char(4) Magic : Indicates that the file is a MoPaQ archive. Must be ASCII "MPQ" 1Ah.
|
||||
04h: int32 HeaderSize : Size of the archive header. Should be 32.
|
||||
08h: int32 ArchiveSize : Size of the whole archive, including the header. Does not include the strong digital signature, if present.
|
||||
This size is used, among other things, for determining the region to hash in computing the digital signature.
|
||||
0Ch: int16 Unknown : Unknown
|
||||
0Eh: int8 SectorSizeShift : Power of two exponent specifying the number of 512-byte disk sectors in each logical sector
|
||||
in the archive. The size of each logical sector the archive is 512 * 2^SectorSizeShift. Bugs in the Storm library dictate
|
||||
that this should always be 3 (4096 byte sectors).
|
||||
10h: int32 HashTableOffset : Offset to the beginning of the hash table, relative to the beginning of the archive.
|
||||
14h: int32 BlockTableOffset : Offset to the beginning of the block table, relative to the beginning of the archive.
|
||||
18h: int32 HashTableEntries : Number of entries in the hash table. Must be a power of two, and must be less than 2^16.
|
||||
1Ch: int32 BlockTableEntries : Number of entries in the block table.
|
||||
|
||||
The archive header is the first structure in the archive, at archive offset 0, but the archive does not need to be at offset
|
||||
0 of the containing file. The offset of the archive in the file is referred to here as ArchiveOffset. If the archive is not
|
||||
at the beginning of the file, it must begin at a disk sector boundary (512 bytes). Early versions of Storm require that the
|
||||
archive be at the end of the containing file (ArchiveOffset + ArchiveSize = file size), but this is not required in newer
|
||||
versions (due to the strong digital signature not being considered a part of the archive).
|
||||
|
||||
2.3 BLOCK TABLE
|
||||
The block table contains entries for each region in the archive. Regions may be either files or empty space, which may be
|
||||
overwritten by new files (typically this space is from deleted file data). The block table is encrypted, using the hash
|
||||
of "(block table)" as the key. Each entry is structured as follows:
|
||||
|
||||
00h: int32 BlockOffset : Offset of the beginning of the block, relative to the beginning of the archive. Meaningless if the block size is 0.
|
||||
04h: int32 BlockSize : Size of the block in the archive.
|
||||
08h: int32 FileSize : Size of the file data stored in the block. Only valid if the block is a file, otherwise meaningless, and should be 0. If the file is compressed, this is the size of the uncompressed file data.
|
||||
0Ch: int32 Flags : Bit mask of the flags for the block. The following values are conclusively identified:
|
||||
80000000h: Block is a file, and follows the file data format; otherwise, block is free space, and may be overwritten. If the block is not a file, all other flags should be cleared.
|
||||
01000000h: File is stored as a single unit, rather than split into sectors.
|
||||
00020000h: The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
|
||||
00010000h: File is encrypted.
|
||||
00000200h: File is compressed. Mutually exclusive to file imploded.
|
||||
00000100h: File is imploded. Mutually exclusive to file compressed.
|
||||
|
||||
2.4 HASH TABLE
|
||||
Instead of storing file names, for quick access MoPaQs use a fixed, power of two-size hash table of files in the archive. A file is uniquely identified by its file path, its language, and its platform. The home entry for a file in the hash table is computed as a hash of the file path. In the event of a collision (the home entry is occupied by another file), progressive overflow is used, and the file is placed in the next available hash table entry. Searches for a desired file in the hash table proceed from the home entry for the file until either the file is found, the entire hash table is searched, or an empty hash table entry (FileBlockIndex of FFFFFFFFh) is encountered. The hash table is encrypted using the hash of "(hash table)" as the key. Each entry is structured as follows:
|
||||
|
||||
00h: int32 FilePathHashA : The hash of the file path, using method A.
|
||||
04h: int32 FilePathHashB : The hash of the file path, using method B.
|
||||
08h: int16 Language : The language of the file. This is a Windows LANGID data type, and uses the same values. 0 indicates the default language (American English), or that the file is language-neutral.
|
||||
0Ah: int8 Platform : The platform the file is used for. 0 indicates the default platform. No other values have been observed.
|
||||
0Ch: int32 FileBlockIndex : If the hash table entry is valid, this is the index into the block table of the file. Otherwise, one of the following two values:
|
||||
FFFFFFFFh: Hash table entry is empty, and has always been empty. Terminates searches for a given file.
|
||||
FFFFFFFEh: Hash table entry is empty, but was valid at some point (in other words, the file was deleted). Does not terminate searches for a given file.
|
||||
|
||||
2.5 FILE DATA
|
||||
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector's data, relative to the beginning of the file data. Not present if this information is calculatable (see details below).
|
||||
immediately following SectorOffsetTable: SectorData : Data of each sector in the file, packed end to end (see details below).
|
||||
|
||||
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may be smaller than this, depending on the size of the file data. This sector size is the size of the raw file data; if the file is compressed, the compressed sector will be smaller or the same size as the uncompressed sector size. Individual sectors in a compressed file may be stored uncompressed; this occurs if and only if the sector could not be compressed by the algorithm used (if the compressed sector size was greater than or equal to the size of the raw data), and is indicated by the sector's compressed size in SectorOffsetTable being equal to the uncompressed size of the sector (which may be calculated from the FileSize).
|
||||
|
||||
If the sector is compressed (but not imploded), a bit mask byte of the compression algorithm(s) used to compress the sector is appended to the beginning of the compressed sector data. This additional byte counts towards the total size of the sector; if the size of the sector (including this byte) exceeds or matches the uncompressed size of the sector data, the sector will be stored uncompressed, and this byte omitted. Multiple compression algorithms may be used on the same sector; in this case, successive compression occurs in the order the algorithms are listed below, and decompression occurs in the opposite order. For implimentations of all of these algorithms, see StormLib.
|
||||
40h: IMA ADPCM mono
|
||||
80h: IMA ADPCM stereo
|
||||
01h: Huffman encoded
|
||||
02h: Deflated (see ZLib)
|
||||
08h: Imploded (see PKWare Data Compression Library)
|
||||
10h: BZip2 compressed (see BZip2)
|
||||
|
||||
If the file is stored as a single unit (indicated in the file's Flags), there is effectively only a single sector, which
|
||||
contains the entire file.
|
||||
|
||||
If the file is encrypted, each sector (after compression and appendage of the compression type byte, if applicable)
|
||||
is encrypted with the file's key. The base key for a file is determined by a hash of the file name stripped of the
|
||||
directory (i.e. the key for a file named "directory\file" would be computed as the hash of "file"). If this key is
|
||||
adjusted, as indicated in the file's Flags, the final key is calculated as ((base key + BlockOffset - ArchiveOffset)
|
||||
XOR FileSize) (StormLib - an open-source implementation of the MoPaQ reading and writing functions,
|
||||
by Ladislav Zezula - incorrectly uses an AND in place of the XOR). Each sector is encrypted using the key + the
|
||||
0-based index of the sector in the file. The SectorOffsetTable, if present, is encrypted using the key - 1.
|
||||
|
||||
The SectorOffsetTable is omitted when the sizes and offsets of all sectors in the file are calculatable from the FileSize.
|
||||
This can happen in several circumstances. If the file is not compressed/imploded, then the size and offset of all sectors
|
||||
is known, based on the archive's SectorSizeShift. If the file is stored as a single unit compressed/imploded, then the
|
||||
SectorOffsetTable is omitted, as the single file "sector" corresponds to BlockSize and FileSize, as mentioned previously.
|
||||
Note that the SectorOffsetTable will always be present if the file is compressed/imploded and the file is not stored as
|
||||
a single unit, even if there is only a single sector in the file (the size of the file is less than or equal to the
|
||||
archive's sector size).
|
||||
|
||||
2.6 LISTFILE
|
||||
The listfile is a very simple extension to the MoPaQ format that contains the file paths of (most) files in the archive.
|
||||
The languages and platforms of the files are not stored in the listfile. The listfile is contained in the file "(listfile)",
|
||||
and is simply a non-Unix-style text file with one file path on each line, lines terminated with the bytes 0Dh 0Ah. The file
|
||||
"(listfile)" may not be listed in the listfile.
|
||||
|
||||
2.7 EXTENDED ATTRIBUTES
|
||||
The extended attributes are optional file attributes for files in the block table. These attributes were added at times after
|
||||
the MoPaQ format was already finalized, and it is not necessary for every archive to have all (or any) of the extended attributes.
|
||||
If an archive contains a given attribute, there will be an instance of that attribute for every block in the block table, although
|
||||
the attribute will be meaningless if the block is not a file. The order of the attributes for blocks correspond to the order of the
|
||||
blocks in the block table, and are of the same number. The attributes are stored in parallel arrays in the "(attributes)" file,
|
||||
in the archive. The attributes corresponding to this file need not be valid (and logically cannot be). Unlike all the other
|
||||
structures in the MoPaQ format, entries in the extended attributes are NOT guaranteed to be aligned. Also note that in some
|
||||
archives, malicious zeroing of the attributes has been observed, perhaps with the intent of breaking archive viewers. This
|
||||
file is structured as follows:
|
||||
|
||||
00h: int32 Version : Specifies the extended attributes format version. For now, must be 100.
|
||||
04h: int32 AttributesPresent : Bit mask of the extended attributes present in the archive:
|
||||
00000001h: File CRC32s.
|
||||
00000002h: File timestamps.
|
||||
00000004h: File MD5s.
|
||||
08h: int32(BlockTableEntries) CRC32s : CRC32s of the (uncompressed) file data for each block in the archive. Omitted if the
|
||||
archive does not have CRC32s. immediately after CRC32s: FILETIME(BlockTableEntries) Timestamps : Timestamps for each block
|
||||
in the archive. The format is that of the Windows FILETIME structure. Omitted if the archive does not have timestamps.
|
||||
immediately after Timestamps: MD5(BlockTableEntries) MD5s : MD5s of the (uncompressed) file data for each block in the archive.
|
||||
Omitted if the archive does not have MD5s.
|
||||
|
||||
2.8 WEAK DIGITAL SIGNATURE
|
||||
The weak digital signature is a digital signature using Microsoft CryptoAPI. It is an implimentation of the RSASSA-PKCS1-v1_5
|
||||
digital signature protocol, using the MD5 hashing algorithm and a 512-bit (weak) RSA key (for more information about this
|
||||
protocol, see the RSA Labs PKCS1 specification). The public key and exponent are stored in a resource in Storm. The signature
|
||||
is stored uncompressed, unencrypted in the file "(signature)" in the archive. The archive is hashed from the beginning of the
|
||||
archive (ArchiveOffset in the containing file) to the end of the archive (the length indicated by ArchiveSize); the signature
|
||||
file is added to the archive before signing, and the space occupied by the file is considered to be all binary 0s during
|
||||
signing/verification. This file is structured as follows:
|
||||
|
||||
00h: int32 Unknown : Must be 0.
|
||||
04h: int32 Unknown : must be 0.
|
||||
08h: int512 Signature : The digital signature. Like all other numbers in the MoPaQ format, this is stored in little-endian order.
|
||||
|
||||
2.9 STRONG DIGITAL SIGNATURE
|
||||
The strong digital signature uses a simple proprietary implementation of RSA signing, using the SHA-1 hashing algorithm and
|
||||
a 2048-bit (strong) RSA key. The default public key and exponent are stored in Storm, but other keys may be used as well.
|
||||
The strong digital signature is stored immediately after the archive, in the containing file; the entire archive (ArchiveSize
|
||||
bytes, starting at ArchiveOffset in the containing file) is hashed as a single block. The signature has the following format:
|
||||
|
||||
00h: char(4) Magic : Indicates the presence of a digital signature. Must be "NGIS" ("SIGN" backwards).
|
||||
04h: int2048 Signature : The digital signature, stored in little-endian format.
|
||||
|
||||
When the Signature field is decrypted with the public key and exponent, and the result stored in little-endian order, it is structured as follows:
|
||||
|
||||
00h: byte Padding : Must be 0Bh.
|
||||
01h: byte(235) Padding : Must be BBh.
|
||||
ECh: byte(20) SHA-1 : SHA-1 hash of the archive, in standard SHA-1 format.
|
||||
|
||||
3. ALGORITHM SOURCE CODE
|
||||
3.1 ENCRYPTION/DECRYPTION
|
||||
I believe this was derived at some point from code in StormLib. Assumes the long type to be 32 bits, and the machine to be little endian order.
|
||||
|
||||
unsigned long dwCryptTable[0x500];
|
||||
|
||||
void InitializeCryptTable()
|
||||
{
|
||||
unsigned long seed = 0x00100001;
|
||||
unsigned long index1 = 0;
|
||||
unsigned long index2 = 0;
|
||||
int i;
|
||||
|
||||
for (index1 = 0; index1 < 0x100; index1++)
|
||||
{
|
||||
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
|
||||
{
|
||||
unsigned long temp1, temp2;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp1 = (seed & 0xFFFF) << 0x10;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp2 = (seed & 0xFFFF);
|
||||
|
||||
dwCryptTable[index2] = (temp1 | temp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EncryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
|
||||
{
|
||||
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
|
||||
unsigned long seed = 0xEEEEEEEE;
|
||||
unsigned long ch;
|
||||
|
||||
assert(lpbyBuffer);
|
||||
|
||||
dwLength /= sizeof(unsigned long);
|
||||
|
||||
while(dwLength-- > 0)
|
||||
{
|
||||
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
|
||||
ch = *lpdwBuffer ^ (dwKey + seed);
|
||||
|
||||
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
|
||||
seed = *lpdwBuffer + seed + (seed << 5) + 3;
|
||||
|
||||
*lpdwBuffer++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
|
||||
{
|
||||
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
|
||||
unsigned long seed = 0xEEEEEEEE;
|
||||
unsigned long ch;
|
||||
|
||||
assert(lpbyBuffer);
|
||||
|
||||
dwLength /= sizeof(unsigned long);
|
||||
|
||||
while(dwLength-- > 0)
|
||||
{
|
||||
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
|
||||
ch = *lpdwBuffer ^ (dwKey + seed);
|
||||
|
||||
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
|
||||
seed = ch + seed + (seed << 5) + 3;
|
||||
|
||||
*lpdwBuffer++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
3.2 HASHING
|
||||
Based on code from StormLib.
|
||||
|
||||
// Different types of hashes to make with HashString
|
||||
#define MPQ_HASH_TABLE_OFFSET 0
|
||||
#define MPQ_HASH_NAME_A 1
|
||||
#define MPQ_HASH_NAME_B 2
|
||||
#define MPQ_HASH_FILE_KEY 3
|
||||
|
||||
unsigned long HashString(const char *lpszString, unsigned long dwHashType)
|
||||
{
|
||||
unsigned long seed1 = 0x7FED7FED;
|
||||
unsigned long seed2 = 0xEEEEEEEE;
|
||||
int ch;
|
||||
|
||||
while (*lpszString != 0)
|
||||
{
|
||||
ch = toupper(*lpszString++);
|
||||
|
||||
seed1 = dwCryptTable[(dwHashType * 0xFF) + ch] ^ (seed1 + seed2);
|
||||
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
|
||||
}
|
||||
return seed1;
|
||||
}
|
||||
|
||||
3.3 CONVERSION OF FILETIME AND time_t
|
||||
|
||||
#define EPOCH_OFFSET 116444736000000000ULL // Number of 100 ns units between 01/01/1601 and 01/01/1970
|
||||
|
||||
bool GetTimeFromFileTime(FILETIME &fileTime, time_t &time)
|
||||
{
|
||||
// The FILETIME represents a 64-bit integer: the number of 100 ns units since January 1, 1601
|
||||
unsigned long long nTime = ((unsigned long long)fileTime.dwHighDateTime << 32) + fileTime.dwLowDateTime;
|
||||
|
||||
if (nTime < EPOCH_OFFSET)
|
||||
return false;
|
||||
|
||||
nTime -= EPOCH_OFFSET; // Convert the time base from 01/01/1601 to 01/01/1970
|
||||
nTime /= 10000000ULL; // Convert 100 ns to sec
|
||||
|
||||
time = (time_t)nTime;
|
||||
|
||||
// Test for overflow (FILETIME is 64 bits, time_t is 32 bits)
|
||||
if ((nTime - (unsigned long long)time) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetFileTimeFromTime(time_t &time, FILETIME &fileTime)
|
||||
{
|
||||
unsigned long long nTime = (unsigned long long)time;
|
||||
|
||||
nTime *= 10000000ULL;
|
||||
nTime += EPOCH_OFFSET;
|
||||
|
||||
fileTime.dwLowDateTime = (DWORD)nTime;
|
||||
fileTime.dwHighDateTime = (DWORD)(nTime >> 32);
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
THE MOPAQ ARCHIVE FORMAT
|
||||
v1.0 (Friday, September 1, 2006)
|
||||
by Justin Olbrantz(Quantam)
|
||||
|
||||
Distribution and reproduction of this specification are allowed without limitation, as long as it is not altered. Quotation in other works is freely allowed, as long as the source and author of the quote are stated.
|
||||
|
||||
TABLE OF CONTENTS
|
||||
1. Introduction to the MoPaQ Format
|
||||
2. The MoPaQ Format
|
||||
2.1 General Archive Layout
|
||||
2.2 Archive Header
|
||||
2.3 Block Table
|
||||
2.4 Extended Block Table
|
||||
2.5 Hash Table
|
||||
2.6 File Data
|
||||
2.7 Listfile
|
||||
2.8 Extended Attributes
|
||||
2.9 Weak (Old) Digital Signature
|
||||
2.10 Strong (New) Digital Signature
|
||||
3. Algorithm Source Code
|
||||
3.1 Encryption/Decryption
|
||||
3.2 Hashing and File Key Computation
|
||||
3.3 Finding Files
|
||||
3.4 Deleting Files
|
||||
3.5 Conversion of FILETIME and time_t
|
||||
3.6 Forming a 64-bit Large Archive Offset from 32-bit and 16-bit Components
|
||||
4. Revision History
|
||||
|
||||
1. INTRODUCTION TO THE MOPAQ FORMAT
|
||||
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be a read-only game archive format, and excels at this role.
|
||||
|
||||
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked. The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
||||
|
||||
StormLib - mentioned several times in this specification - is an open-source MoPaQ reading and writing library written by Ladislav Zezula (no affiliation with Blizzard Entertainment). While it's a bit dated, and does not support all of the newer MoPaQ features, it contains source code to the more exotic compression methods used by MoPaQ, such as the PKWare implode algorithm, MoPaQ's huffman compression algorithm, and the IMA ADPCM compression used by MoPaQ.
|
||||
|
||||
2. THE MOPAQ FORMAT
|
||||
All numbers in the MoPaQ format are in little endian byte order; signed numbers use the two's complement system. Data types are listed either as int (integer, the number of bits specified), byte (8 bits), or char (bytes which contain ASCII characters). All sizes and offsets are in bytes, unless specified otherwise. Structure members are listed in the following general form:
|
||||
offset from the beginning of the structure: data type(array size) member name : member description
|
||||
|
||||
2.1 GENERAL ARCHIVE LAYOUT
|
||||
- Archive Header
|
||||
- File Data
|
||||
- File Data - Special Files
|
||||
- Hash Table
|
||||
- Block Table
|
||||
- Extended Block Table
|
||||
- Strong Digital signature
|
||||
|
||||
This is the usual archive format, but it is not mandatory. Some archives have been observed placing the hash table and file table after the archive header, and before the file data.
|
||||
|
||||
2.2 ARCHIVE HEADER
|
||||
00h: char(4) Magic : Indicates that the file is a MoPaQ archive. Must be ASCII "MPQ" 1Ah.
|
||||
04h: int32 HeaderSize : Size of the archive header.
|
||||
08h: int32 ArchiveSize : Size of the whole archive, including the header. Does not include the strong digital signature, if present. This size is used, among other things, for determining the region to hash in computing the digital signature. This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive is calculated as the size from the beginning of the archive to the end of the hash table, block table, or extended block table (whichever is largest).
|
||||
0Ch: int16 FormatVersion : MoPaQ format version. MPQAPI will not open archives where this is negative. Known versions:
|
||||
0000h: Original format. HeaderSize should be 20h, and large archives are not supported.
|
||||
0001h: Burning Crusade format. Header size should be 2Ch, and large archives are supported.
|
||||
0Eh: int8 SectorSizeShift : Power of two exponent specifying the number of 512-byte disk sectors in each logical sector in the archive. The size of each logical sector in the archive is 512 * 2^SectorSizeShift. Bugs in the Storm library dictate that this should always be 3 (4096 byte sectors).
|
||||
10h: int32 HashTableOffset : Offset to the beginning of the hash table, relative to the beginning of the archive.
|
||||
14h: int32 BlockTableOffset : Offset to the beginning of the block table, relative to the beginning of the archive.
|
||||
18h: int32 HashTableEntries : Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for the original MoPaQ format, or less than 2^20 for the Burning Crusade format.
|
||||
1Ch: int32 BlockTableEntries : Number of entries in the block table.
|
||||
Fields only present in the Burning Crusade format and later:
|
||||
20h: int64 ExtendedBlockTableOffset : Offset to the beginning of the extended block table, relative to the beginning of the archive.
|
||||
28h: int16 HashTableOffsetHigh : High 16 bits of the hash table offset for large archives.
|
||||
2Ah: int16 BlockTableOffsetHigh : High 16 bits of the block table offset for large archives.
|
||||
|
||||
The archive header is the first structure in the archive, at archive offset 0; however, the archive does not need to be at offset 0 of the containing file. The offset of the archive in the file is referred to here as ArchiveOffset. If the archive is not at the beginning of the file, it must begin at a disk sector boundary (512 bytes). Early versions of Storm require that the archive be at the end of the containing file (ArchiveOffset + ArchiveSize = file size), but this is not required in newer versions (due to the strong digital signature not being considered a part of the archive).
|
||||
|
||||
2.3 BLOCK TABLE
|
||||
The block table contains entries for each region in the archive. Regions may be either files, empty space, which may be overwritten by new files (typically this space is from deleted file data), or unused block table entries. Empty space entries should have BlockOffset and BlockSize nonzero, and FileSize and Flags zero; unused block table entries should have BlockSize, FileSize, and Flags zero. The block table is encrypted, using the hash of "(block table)" as the key. Each entry is structured as follows:
|
||||
|
||||
00h: int32 BlockOffset : Offset of the beginning of the block, relative to the beginning of the archive.
|
||||
04h: int32 BlockSize : Size of the block in the archive.
|
||||
08h: int32 FileSize : Size of the file data stored in the block. Only valid if the block is a file; otherwise meaningless, and should be 0. If the file is compressed, this is the size of the uncompressed file data.
|
||||
0Ch: int32 Flags : Bit mask of the flags for the block. The following values are conclusively identified:
|
||||
80000000h: Block is a file, and follows the file data format; otherwise, block is free space or unused. If the block is not a file, all other flags should be cleared, and FileSize should be 0.
|
||||
01000000h: File is stored as a single unit, rather than split into sectors.
|
||||
00020000h: The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
|
||||
00010000h: File is encrypted.
|
||||
00000200h: File is compressed. File cannot be imploded.
|
||||
00000100h: File is imploded. File cannot be compressed.
|
||||
|
||||
2.4 EXTENDED BLOCK TABLE
|
||||
The extended block table was added to support archives larger than 4 gigabytes (2^32 bytes). The table contains the upper bits of the archive offsets for each block in the block table. It is simply an array of int16s, which become bits 32-47 of the archive offsets for each block, with bits 48-63 being zero. Individual blocks in the archive are still limited to 4 gigabytes in size. This table is only present in Burning Crusade format archives that exceed 4 gigabytes size.
|
||||
|
||||
As of the Burning Crusade Friends and Family beta, this table is not encrypted.
|
||||
|
||||
2.5 HASH TABLE
|
||||
Instead of storing file names, for quick access MoPaQs use a fixed, power of two-size hash table of files in the archive. A file is uniquely identified by its file path, its language, and its platform. The home entry for a file in the hash table is computed as a hash of the file path. In the event of a collision (the home entry is occupied by another file), progressive overflow is used, and the file is placed in the next available hash table entry. Searches for a desired file in the hash table proceed from the home entry for the file until either the file is found, the entire hash table is searched, or an empty hash table entry (FileBlockIndex of FFFFFFFFh) is encountered. The hash table is encrypted using the hash of "(hash table)" as the key. Each entry is structured as follows:
|
||||
|
||||
00h: int32 FilePathHashA : The hash of the file path, using method A.
|
||||
04h: int32 FilePathHashB : The hash of the file path, using method B.
|
||||
08h: int16 Language : The language of the file. This is a Windows LANGID data type, and uses the same values. 0 indicates the default language (American English), or that the file is language-neutral.
|
||||
0Ah: int8 Platform : The platform the file is used for. 0 indicates the default platform. No other values have been observed.
|
||||
0Ch: int32 FileBlockIndex : If the hash table entry is valid, this is the index into the block table of the file. Otherwise, one of the following two values:
|
||||
FFFFFFFFh: Hash table entry is empty, and has always been empty. Terminates searches for a given file.
|
||||
FFFFFFFEh: Hash table entry is empty, but was valid at some point (in other words, the file was deleted). Does not terminate searches for a given file.
|
||||
|
||||
2.6 FILE DATA
|
||||
The data for each file is composed of the following structure:
|
||||
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector, relative to the beginning of the file data. The last entry contains the file size, making it possible to easily calculate the size of any given sector. This table is not present if this information can be calculated (see details below).
|
||||
immediately following SectorOffsetTable: SECTOR Sectors(SectorsInFile) : Data of each sector in the file, packed end to end (see details below).
|
||||
|
||||
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may contain less than this, depending on the size of the entire file's data. If the file is compressed or imploded, the sector will be smaller or the same size as the file data it contains. Individual sectors in a compressed or imploded file may be stored uncompressed; this occurs if and only if the file data the sector contains could not be compressed by the algorithm(s) used (if the compressed sector size was greater than or equal to the size of the file data), and is indicated by the sector's size in SectorOffsetTable being equal to the size of the file data in the sector (which may be calculated from the FileSize).
|
||||
|
||||
The format of each sector depends on the kind of sector it is. Uncompressed sectors are simply the the raw file data contained in the sector. Imploded sectors are the raw compressed data following compression with the implode algorithm (these sectors can only be in imploded files). Compressed sectors (only found in compressed - not imploded - files) are compressed with one or more compression algorithms, and have the following structure:
|
||||
00h: byte CompressionMask : Mask of the compression types applied to this sector. If multiple compression types are used, they are applied in the order listed below, and decompression is performed in the opposite order. This byte counts towards the total sector size, meaning that the sector will be stored uncompressed if the data cannot be compressed by at least two bytes; as well, this byte is encrypted with the sector data, if applicable. The following compression types are defined (for implementations of these algorithms, see StormLib):
|
||||
40h: IMA ADPCM mono
|
||||
80h: IMA ADPCM stereo
|
||||
01h: Huffman encoded
|
||||
02h: Deflated (see ZLib)
|
||||
08h: Imploded (see PKWare Data Compression Library)
|
||||
10h: BZip2 compressed (see BZip2)
|
||||
01h: byte(SectorSize - 1) SectorData : The compressed data for the sector.
|
||||
|
||||
If the file is stored as a single unit (indicated in the file's Flags), there is effectively only a single sector, which contains the entire file data.
|
||||
|
||||
If the file is encrypted, each sector (after compression/implosion, if applicable) is encrypted with the file's key. The base key for a file is determined by a hash of the file name stripped of the directory (i.e. the key for a file named "directory\file" would be computed as the hash of "file"). If this key is adjusted, as indicated in the file's Flags, the final key is calculated as ((base key + BlockOffset - ArchiveOffset) XOR FileSize) (StormLib incorrectly uses an AND in place of the XOR). Each sector is encrypted using the key + the 0-based index of the sector in the file. The SectorOffsetTable, if present, is encrypted using the key - 1.
|
||||
|
||||
The SectorOffsetTable is omitted when the sizes and offsets of all sectors in the file are calculatable from the FileSize. This can happen in several circumstances. If the file is not compressed/imploded, then the size and offset of all sectors is known, based on the archive's SectorSizeShift. If the file is stored as a single unit compressed/imploded, then the SectorOffsetTable is omitted, as the single file "sector" corresponds to BlockSize and FileSize, as mentioned previously. However, the SectorOffsetTable will be present if the file is compressed/imploded and the file is not stored as a single unit, even if there is only a single sector in the file (the size of the file is less than or equal to the archive's sector size).
|
||||
|
||||
2.7 LISTFILE
|
||||
The listfile is a very simple extension to the MoPaQ format that contains the file paths of (most) files in the archive. The languages and platforms of the files are not stored in the listfile. The listfile is contained in the file "(listfile)" (default language and platform), and is simply a text file with file paths separated by ';', 0Dh, 0Ah, or some combination of these. The file "(listfile)" may not be listed in the listfile.
|
||||
|
||||
2.8 EXTENDED ATTRIBUTES
|
||||
The extended attributes are optional file attributes for files in the block table. These attributes were added at times after the MoPaQ format was already finalized, and it is not necessary for every archive to have all (or any) of the extended attributes. If an archive contains a given attribute, there will be an instance of that attribute for every block in the block table, although the attribute will be meaningless if the block is not a file. The order of the attributes for blocks correspond to the order of the blocks in the block table, and are of the same number. The attributes are stored in parallel arrays in the "(attributes)" file (default language and platform), in the archive. The attributes corresponding to this file need not be valid (and logically cannot be). Unlike all the other structures in the MoPaQ format, entries in the extended attributes are NOT guaranteed to be aligned. Also note that in some archives, malicious zeroing of the attributes has been observed, perhaps with the intent of breaking archive viewers. This file is structured as follows:
|
||||
|
||||
00h: int32 Version : Specifies the extended attributes format version. For now, must be 100.
|
||||
04h: int32 AttributesPresent : Bit mask of the extended attributes present in the archive:
|
||||
00000001h: File CRC32s.
|
||||
00000002h: File timestamps.
|
||||
00000004h: File MD5s.
|
||||
08h: int32(BlockTableEntries) CRC32s : CRC32s of the (uncompressed) file data for each block in the archive. Omitted if the archive does not have CRC32s.
|
||||
immediately after CRC32s: FILETIME(BlockTableEntries) Timestamps : Timestamps for each block in the archive. The format is that of the Windows FILETIME structure. Omitted if the archive does not have timestamps.
|
||||
immediately after Timestamps: MD5(BlockTableEntries) MD5s : MD5s of the (uncompressed) file data for each block in the archive. Omitted if the archive does not have MD5s.
|
||||
|
||||
2.9 WEAK DIGITAL SIGNATURE
|
||||
The weak digital signature is a digital signature using Microsoft CryptoAPI. It is an implimentation
|
||||
of the RSASSA-PKCS1-v1_5 digital signature protocol, using the MD5 hashing algorithm and a 512-bit (weak)
|
||||
RSA key (for more information about this protocol, see the RSA Labs PKCS1 specification). The public key
|
||||
and exponent are stored in a resource in Storm, the private key is stored in a separate file, whose filename
|
||||
is passed to MPQAPI (the private key is not stored in MPQAPI). The signature is stored uncompressed,
|
||||
unencrypted in the file "(signature)" (default language and platform) in the archive. The archive
|
||||
is hashed from the beginning of the archive (ArchiveOffset in the containing file) to the end of
|
||||
the archive (the length indicated by ArchiveSize, or calculated in the Burning Crusade MoPaQ format);
|
||||
the signature file is added to the archive before signing, and the space occupied by the file is considered
|
||||
to be all binary 0s during signing/verification. This file is structured as follows:
|
||||
|
||||
00h: int32 Unknown : Must be 0.
|
||||
04h: int32 Unknown : Must be 0.
|
||||
08h: int512 Signature : The digital signature. Like all other numbers in the MoPaQ format, this is stored
|
||||
in little-endian order. The structure of this, when decrypted, follows the RSASSA-PKCS1-v1_5 specification;
|
||||
this format is rather icky to work with (I wrote a program to verify this signature using nothing but an MD5
|
||||
function and huge integer functions; it wasn't pleasant), and best left to an encryption library such as Cryto++.
|
||||
|
||||
2.10 STRONG DIGITAL SIGNATURE
|
||||
The strong digital signature uses a simple proprietary implementation of RSA signing, using the SHA-1 hashing algorithm and a 2048-bit (strong) RSA key. The default public key and exponent are stored in Storm, but other keys may be used as well. The strong digital signature is stored immediately after the archive, in the containing file; the entire archive (ArchiveSize bytes, starting at ArchiveOffset in the containing file) is hashed as a single block. The signature has the following format:
|
||||
|
||||
00h: char(4) Magic : Indicates the presence of a digital signature. Must be "NGIS" ("SIGN" backwards).
|
||||
04h: int2048 Signature : The digital signature, stored in little-endian format.
|
||||
|
||||
When the Signature field is decrypted with the public key and exponent, and the resulting large integer is stored in little-endian order, it is structured as follows:
|
||||
|
||||
00h: byte Padding : Must be 0Bh.
|
||||
01h: byte(235) Padding : Must be BBh.
|
||||
ECh: byte(20) SHA-1 : SHA-1 hash of the archive, in standard SHA-1 byte order.
|
||||
|
||||
3. ALGORITHM SOURCE CODE
|
||||
All of the sample code here assumes little endian machine byte order, that the short type is 16 bits, that the long type is 32 bits, and that the long long type is 64 bits. Adjustments must be made if these assumptions are not correct on a given platform. All code not credited otherwise was written by myself in the writing of this specification.
|
||||
|
||||
3.1 ENCRYPTION/DECRYPTION
|
||||
Based on code from StormLib.
|
||||
|
||||
unsigned long dwCryptTable[0x500];
|
||||
|
||||
// The encryption and hashing functions use a number table in their procedures. This table must be initialized before the functions are called the first time.
|
||||
void InitializeCryptTable()
|
||||
{
|
||||
unsigned long seed = 0x00100001;
|
||||
unsigned long index1 = 0;
|
||||
unsigned long index2 = 0;
|
||||
int i;
|
||||
|
||||
for (index1 = 0; index1 < 0x100; index1++)
|
||||
{
|
||||
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
|
||||
{
|
||||
unsigned long temp1, temp2;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp1 = (seed & 0xFFFF) << 0x10;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp2 = (seed & 0xFFFF);
|
||||
|
||||
dwCryptTable[index2] = (temp1 | temp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EncryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
|
||||
{
|
||||
assert(lpbyBuffer);
|
||||
|
||||
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
|
||||
unsigned long seed = 0xEEEEEEEE;
|
||||
unsigned long ch;
|
||||
|
||||
dwLength /= sizeof(unsigned long);
|
||||
|
||||
while(dwLength-- > 0)
|
||||
{
|
||||
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
|
||||
ch = *lpdwBuffer ^ (dwKey + seed);
|
||||
|
||||
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
|
||||
seed = *lpdwBuffer + seed + (seed << 5) + 3;
|
||||
|
||||
*lpdwBuffer++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
|
||||
{
|
||||
assert(lpbyBuffer);
|
||||
|
||||
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
|
||||
unsigned long seed = 0xEEEEEEEEL;
|
||||
unsigned long ch;
|
||||
|
||||
dwLength /= sizeof(unsigned long);
|
||||
|
||||
while(dwLength-- > 0)
|
||||
{
|
||||
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
|
||||
ch = *lpdwBuffer ^ (dwKey + seed);
|
||||
|
||||
dwKey = ((~dwKey << 0x15) + 0x11111111L) | (dwKey >> 0x0B);
|
||||
seed = ch + seed + (seed << 5) + 3;
|
||||
|
||||
*lpdwBuffer++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
3.2 HASHING AND FILE KEY COMPUTATION
|
||||
These functions may have been derived from StormLib code at some point in the very distant past. It was so long ago that I don't remember for certain.
|
||||
|
||||
// Different types of hashes to make with HashString
|
||||
#define MPQ_HASH_TABLE_OFFSET 0
|
||||
#define MPQ_HASH_NAME_A 1
|
||||
#define MPQ_HASH_NAME_B 2
|
||||
#define MPQ_HASH_FILE_KEY 3
|
||||
|
||||
// Based on code from StormLib.
|
||||
unsigned long HashString(const char *lpszString, unsigned long dwHashType)
|
||||
{
|
||||
assert(lpszString);
|
||||
assert(dwHashType <= MPQ_HASH_FILE_KEY);
|
||||
|
||||
unsigned long seed1 = 0x7FED7FEDL;
|
||||
unsigned long seed2 = 0xEEEEEEEEL;
|
||||
int ch;
|
||||
|
||||
while (*lpszString != 0)
|
||||
{
|
||||
ch = toupper(*lpszString++);
|
||||
|
||||
seed1 = dwCryptTable[(dwHashType * 0x100) + ch] ^ (seed1 + seed2);
|
||||
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
|
||||
}
|
||||
return seed1;
|
||||
}
|
||||
|
||||
#define BLOCK_OFFSET_ADJUSTED_KEY 0x00020000L
|
||||
|
||||
unsigned long ComputeFileKey(const char *lpszFilePath, const BlockTableEntry &blockEntry, unsigned long nArchiveOffset)
|
||||
{
|
||||
assert(lpszFilePath);
|
||||
|
||||
// Find the file name part of the path
|
||||
const char *lpszFileName = strrchr(lpszFilePath, '\\');
|
||||
if (lpszFileName)
|
||||
lpszFileName++; // Skip the \
|
||||
else
|
||||
lpszFileName = lpszFilePath;
|
||||
|
||||
// Hash the name to get the base key
|
||||
unsigned long nFileKey = HashString(lpszFileName, MPQ_HASH_FILE_KEY);
|
||||
|
||||
// Offset-adjust the key if necessary
|
||||
if (blockEntry.Flags & BLOCK_OFFSET_ADJUSTED_KEY)
|
||||
nFileKey = (nFileKey + blockEntry.BlockOffset) ^ blockEntry.FileSize;
|
||||
|
||||
return nFileKey;
|
||||
}
|
||||
|
||||
3.3 FINDING FILES
|
||||
|
||||
#define MPQ_HASH_ENTRY_EMPTY 0xFFFFFFFFL
|
||||
#define MPQ_HASH_ENTRY_DELETED 0xFFFFFFFEL
|
||||
|
||||
bool FindFileInHashTable(const HashTableEntry *lpHashTable, unsigned long nHashTableSize, const char *lpszFilePath, unsigned short nLang, unsigned char nPlatform, unsigned long &iFileHashEntry)
|
||||
{
|
||||
assert(lpHashTable);
|
||||
assert(nHashTableSize);
|
||||
assert(lpszFilePath);
|
||||
|
||||
// Find the home entry in the hash table for the file
|
||||
unsigned long iInitEntry = HashString(lpszFilePath, MPQ_HASH_TABLE_OFFSET) & (nHashTableSize - 1);
|
||||
|
||||
// Is there anything there at all?
|
||||
if (lpHashTable[iInitEntry].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
||||
return false;
|
||||
|
||||
// Compute the hashes to compare the hash table entry against
|
||||
unsigned long nNameHashA = HashString(lpszFilePath, MPQ_HASH_NAME_A),
|
||||
nNameHashB = HashString(lpszFilePath, MPQ_HASH_NAME_B),
|
||||
iCurEntry = iInitEntry;
|
||||
|
||||
// Check each entry in the hash table till a termination point is reached
|
||||
do
|
||||
{
|
||||
if (lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_DELETED)
|
||||
{
|
||||
if (lpHashTable[iCurEntry].FilePathHashA == nNameHashA
|
||||
&& lpHashTable[iCurEntry].FilePathHashB == nNameHashB
|
||||
&& lpHashTable[iCurEntry].Language == nLang
|
||||
&& lpHashTable[iCurEntry].Platform == nPlatform)
|
||||
{
|
||||
iFileHashEntry = iCurEntry;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
iCurEntry = (iCurEntry + 1) & (nHashTableSize - 1);
|
||||
} while (iCurEntry != iInitEntry && lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_EMPTY);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
3.4 DELETING FILES
|
||||
|
||||
bool DeleteFile(HashTableEntry *lpHashTable, unsigned long nHashTableSize, BlockTableEntry *lpBlockTable, const char *lpszFilePath, unsigned short nLang, unsigned char nPlatform)
|
||||
{
|
||||
assert(lpHashTable);
|
||||
assert(nHashTableSize);
|
||||
assert(lpBlockTable);
|
||||
|
||||
// Find the file in the hash table
|
||||
unsigned long iFileHashEntry;
|
||||
|
||||
if (!FindFileInHashTable(lpHashTable, nHashTableSize, lpszFilePath, nLang, nPlatform, iFileHashEntry))
|
||||
return false;
|
||||
|
||||
// Get the block table index before we nuke the hash table entry
|
||||
unsigned long iFileBlockEntry = lpHashTable[iFileHashEntry].FileBlockIndex;
|
||||
|
||||
// Delete the file's entry in the hash table
|
||||
memset(&lpHashTable[iFileHashEntry], 0xFF, sizeof(HashTableEntry));
|
||||
|
||||
// If the next entry is empty, mark this one as empty; otherwise, mark this as deleted.
|
||||
if (lpHashTable[(iFileHashEntry + 1) & (nHashTableSize - 1)].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
||||
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_EMPTY;
|
||||
else
|
||||
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_DELETED;
|
||||
|
||||
// If the block occupies space, mark the block as free space; otherwise, clear the block table entry.
|
||||
if (lpBlockTable[iFileBlockEntry].BlockSize > 0)
|
||||
{
|
||||
lpBlockTable[iFileBlockEntry].FileSize = 0;
|
||||
lpBlockTable[iFileBlockEntry].Flags = 0;
|
||||
}
|
||||
else
|
||||
memset(&lpBlockTable[iFileBlockEntry], 0, sizeof(BlockTableEntry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
3.5 CONVERSION OF FILETIME AND time_t
|
||||
This code assumes that the base ("zero") date for time_t is 01/01/1970. This is true on Windows, Unix System V systems, and Mac OS X. It is unknown whether this is true on all other platforms. You'll need to research this yourself, if you plan on porting it somewhere else.
|
||||
|
||||
#define EPOCH_OFFSET 116444736000000000ULL // Number of 100 ns units between 01/01/1601 and 01/01/1970
|
||||
|
||||
bool GetTimeFromFileTime(const FILETIME &fileTime, time_t &time)
|
||||
{
|
||||
// The FILETIME represents a 64-bit integer: the number of 100 ns units since January 1, 1601
|
||||
unsigned long long nTime = ((unsigned long long)fileTime.dwHighDateTime << 32) + fileTime.dwLowDateTime;
|
||||
|
||||
if (nTime < EPOCH_OFFSET)
|
||||
return false;
|
||||
|
||||
nTime -= EPOCH_OFFSET; // Convert the time base from 01/01/1601 to 01/01/1970
|
||||
nTime /= 10000000ULL; // Convert 100 ns to sec
|
||||
|
||||
time = (time_t)nTime;
|
||||
|
||||
// Test for overflow (FILETIME is 64 bits, time_t is 32 bits)
|
||||
if ((nTime - (unsigned long long)time) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetFileTimeFromTime(const time_t &time, FILETIME &fileTime)
|
||||
{
|
||||
unsigned long long nTime = (unsigned long long)time;
|
||||
|
||||
nTime *= 10000000ULL;
|
||||
nTime += EPOCH_OFFSET;
|
||||
|
||||
fileTime.dwLowDateTime = (DWORD)nTime;
|
||||
fileTime.dwHighDateTime = (DWORD)(nTime >> 32);
|
||||
}
|
||||
|
||||
3.6 FORMING A 64-BIT LARGE ARCHIVE OFFSET FROM 32-BIT AND 16-BIT COMPONENTS
|
||||
unsigned long long MakeLargeArchiveOffset(unsigned long nOffsetLow, unsigned short nOffsetHigh)
|
||||
{
|
||||
return ((unsigned long long)nOffsetHigh << 32) + (unsigned long long)nOffsetLow;
|
||||
}
|
||||
|
||||
4. REVISION HISTORY
|
||||
1.0
|
||||
- Updated to include most of the changes found in the Burning Crusade Friends and Family beta
|
||||
|
||||
0.91.
|
||||
- Updated several structure member descriptions
|
||||
- Listed the full set of characters that can separate list file entries
|
||||
- Noted that (attributes), (listfile), and (signature) use the default language and platform codes
|
||||
- Redid part of the file data specs to clarify the format of sectors
|
||||
- Enhanced descriptions of the different kinds of block table entries
|
||||
- Added ComputeFileKey, FindFileInHashTable, and DeleteFile source
|
||||
@@ -1 +0,0 @@
|
||||
UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK
|
||||
@@ -1 +0,0 @@
|
||||
MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP
|
||||
@@ -1 +0,0 @@
|
||||
8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP
|
||||
@@ -1 +0,0 @@
|
||||
EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ
|
||||
@@ -1 +0,0 @@
|
||||
PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT
|
||||
@@ -1 +0,0 @@
|
||||
X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2
|
||||
@@ -1 +0,0 @@
|
||||
5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2
|
||||
@@ -1 +0,0 @@
|
||||
478JD2K56EVNVVY4XX8TDWYT5B8KB254
|
||||
@@ -1 +0,0 @@
|
||||
8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A
|
||||
@@ -1 +0,0 @@
|
||||
LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB
|
||||
@@ -1 +0,0 @@
|
||||
K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG
|
||||
@@ -1 +0,0 @@
|
||||
6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"config":{
|
||||
"product": "D3",
|
||||
"install_progress_percent": 70.0,
|
||||
"install_progress_info": [8000000000.0, 0.0, 0.0],
|
||||
"download_progress_info": [10000000.0, 500000000.0, 500000000.0],
|
||||
"install_progress_speed": 5000000.0,
|
||||
"tome_download_progress_percent": 0.0,
|
||||
"updater_product": "d3_patch",
|
||||
"expansion_level": 0,
|
||||
"ptr": false,
|
||||
"beta": false,
|
||||
"update_method": "patch on demand",
|
||||
"supports_multibox": false,
|
||||
"data_dir": "Data_D3/PC/MPQs/",
|
||||
"patch_url": "http://ruRU.patch.battle.net:1119/patch",
|
||||
"update_regex": "(?P<prefix>d3-update-(?P<dataset>\\w+))-(?P<build>\\d+)\\.mpq$",
|
||||
"update_identifier": "d3-update-",
|
||||
"torrent_file_path": "Diablo III.tfil",
|
||||
"manifest_file_path": "Diablo III.mfil",
|
||||
"priority_file_path": "Diablo III.pfil",
|
||||
"priority_file_layout": "Retail",
|
||||
"binary_version_path": "Diablo III.exe",
|
||||
"binary_launch_path": "Diablo III.exe",
|
||||
"display_locales":["ruRU"],
|
||||
"supported_locales" : ["enUS", "esMX", "ptBR", "enGB", "deDE", "esES", "frFR", "itIT", "plPL", "enSG", "ptPT", "ruRU", "koKR", "zhTW", "zhCN"],
|
||||
"launch_arguments":["-launch","-uid","diablo3_ruru"],
|
||||
"form": {
|
||||
"eula": {
|
||||
"eula":false
|
||||
},
|
||||
"game_dir": {
|
||||
"default": "Program Files",
|
||||
"dirname": "Diablo III",
|
||||
"required_space": 15032385536
|
||||
},
|
||||
"language": {
|
||||
"default":["ruRU"],
|
||||
"list":["ruRU"]
|
||||
},
|
||||
"authentication_key": {
|
||||
"url": [
|
||||
"http://ruru.nydus.battle.net/D3/ruRU/setup/mediakey",
|
||||
"http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-deDE.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-enSG.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-enUS.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-esES.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-esMX.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-frFR.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-itIT.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-koKR.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-plPL.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-ptBR.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt <====
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-zhTW.txt
|
||||
http://dist.blizzard.com/mediakey/d3-authenticationcode-zhCN.txt <====
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,189 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* FileStream.h Copyright (c) Ladislav Zezula 2012 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: Definitions for FileStream object */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 14.04.12 1.00 Lad The first version of FileStream.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __FILESTREAM_H__
|
||||
#define __FILESTREAM_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
|
||||
typedef bool (*STREAM_READ)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
||||
void * pvBuffer, // Pointer to data to be read
|
||||
DWORD dwBytesToRead // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_WRITE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
||||
const void * pvBuffer, // Pointer to data to be written
|
||||
DWORD dwBytesToWrite // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETPOS)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG & ByteOffset // Pointer to store current file position
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETSIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG & FileSize // Receives the file size, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_SETSIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG FileSize // New size for the file, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETTIME)(
|
||||
struct TFileStream * pStream,
|
||||
ULONGLONG * pFT
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_SWITCH)(
|
||||
struct TFileStream * pStream,
|
||||
struct TFileStream * pNewStream
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETBMP)(
|
||||
TFileStream * pStream,
|
||||
TFileBitmap * pBitmap,
|
||||
DWORD Length,
|
||||
LPDWORD LengthNeeded
|
||||
);
|
||||
|
||||
typedef void (*STREAM_CLOSE)(
|
||||
struct TFileStream * pStream
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures - part file structure
|
||||
|
||||
typedef struct _PART_FILE_HEADER
|
||||
{
|
||||
DWORD PartialVersion; // Always set to 2
|
||||
char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ
|
||||
DWORD Flags; // Flags (details unknown)
|
||||
DWORD FileSizeLo; // Low 32 bits of the contained file size
|
||||
DWORD FileSizeHi; // High 32 bits of the contained file size
|
||||
DWORD BlockSize; // Size of one file block, in bytes
|
||||
|
||||
} PART_FILE_HEADER, *PPART_FILE_HEADER;
|
||||
|
||||
// Structure describing the block-to-file map entry
|
||||
typedef struct _PART_FILE_MAP_ENTRY
|
||||
{
|
||||
DWORD Flags; // 3 = the block is present in the file
|
||||
DWORD BlockOffsLo; // Low 32 bits of the block position in the file
|
||||
DWORD BlockOffsHi; // High 32 bits of the block position in the file
|
||||
DWORD LargeValueLo; // 64-bit value, meaning is unknown
|
||||
DWORD LargeValueHi;
|
||||
|
||||
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
union TBaseData
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
HANDLE hFile; // File handle
|
||||
} File;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Mapped file size
|
||||
ULONGLONG FilePos; // Current stream position
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
LPBYTE pbFile; // Pointer to mapped view
|
||||
} Map;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the internet file
|
||||
ULONGLONG FilePos; // Current position in the file
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
HANDLE hInternet; // Internet handle
|
||||
HANDLE hConnect; // Connection to the internet server
|
||||
} Http;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for linear stream
|
||||
|
||||
struct TFileStream
|
||||
{
|
||||
// Stream provider functions
|
||||
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
|
||||
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
|
||||
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
|
||||
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
|
||||
STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size
|
||||
STREAM_GETTIME StreamGetTime; // Pointer to function retrieving the file time
|
||||
STREAM_GETBMP StreamGetBmp; // Pointer to function that retrieves the file bitmap
|
||||
STREAM_SWITCH StreamSwitch; // Pointer to function changing the stream to another file
|
||||
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
|
||||
|
||||
// Stream provider data members
|
||||
TCHAR szFileName[MAX_PATH]; // File name
|
||||
DWORD dwFlags; // Stream flags
|
||||
|
||||
// Base provider functions
|
||||
STREAM_READ BaseRead;
|
||||
STREAM_WRITE BaseWrite;
|
||||
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
||||
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
|
||||
STREAM_SETSIZE BaseSetSize; // Pointer to function changing file size
|
||||
STREAM_GETTIME BaseGetTime; // Pointer to function retrieving the file time
|
||||
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
|
||||
|
||||
// Base provider data members
|
||||
TBaseData Base; // Base provider data
|
||||
|
||||
// Followed by stream provider data, with variable length
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for linear stream
|
||||
|
||||
struct TLinearStream : public TFileStream
|
||||
{
|
||||
TFileBitmap * pBitmap; // Pointer to the stream bitmap
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for partial stream
|
||||
|
||||
struct TPartialStream : public TFileStream
|
||||
{
|
||||
ULONGLONG VirtualSize; // Virtual size of the file
|
||||
ULONGLONG VirtualPos; // Virtual position in the file
|
||||
DWORD BlockCount; // Number of file blocks. Used by partial file stream
|
||||
DWORD BlockSize; // Size of one block. Used by partial file stream
|
||||
|
||||
PPART_FILE_MAP_ENTRY PartMap; // File map, variable length
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for encrypted stream
|
||||
|
||||
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
|
||||
|
||||
struct TEncryptedStream : public TFileStream
|
||||
{
|
||||
BYTE Key[MPQE_CHUNK_SIZE]; // File key
|
||||
};
|
||||
|
||||
#endif // __FILESTREAM_H__
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,144 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SBaseDumpData.cpp Copyright (c) Ladislav Zezula 2011 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description : */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 26.01.11 1.00 Lad The first version of SBaseDumpData.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
#ifdef __STORMLIB_DUMP_DATA__
|
||||
|
||||
void DumpMpqHeader(TMPQHeader * pHeader)
|
||||
{
|
||||
printf("== MPQ Header =================================\n");
|
||||
printf("DWORD dwID = %08X\n", pHeader->dwID);
|
||||
printf("DWORD dwHeaderSize = %08X\n", pHeader->dwHeaderSize);
|
||||
printf("DWORD dwArchiveSize = %08X\n", pHeader->dwArchiveSize);
|
||||
printf("USHORT wFormatVersion = %04X\n", pHeader->wFormatVersion);
|
||||
printf("USHORT wSectorSize = %04X\n", pHeader->wSectorSize);
|
||||
printf("DWORD dwHashTablePos = %08X\n", pHeader->dwHashTablePos);
|
||||
printf("DWORD dwBlockTablePos = %08X\n", pHeader->dwBlockTablePos);
|
||||
printf("DWORD dwHashTableSize = %08X\n", pHeader->dwHashTableSize);
|
||||
printf("DWORD dwBlockTableSize = %08X\n", pHeader->dwBlockTableSize);
|
||||
printf("ULONGLONG HiBlockTablePos64 = %016llX\n", pHeader->HiBlockTablePos64);
|
||||
printf("USHORT wHashTablePosHi = %04X\n", pHeader->wHashTablePosHi);
|
||||
printf("USHORT wBlockTablePosHi = %04X\n", pHeader->wBlockTablePosHi);
|
||||
printf("ULONGLONG ArchiveSize64 = %016llX\n", pHeader->ArchiveSize64);
|
||||
printf("ULONGLONG BetTablePos64 = %016llX\n", pHeader->BetTablePos64);
|
||||
printf("ULONGLONG HetTablePos64 = %016llX\n", pHeader->HetTablePos64);
|
||||
printf("ULONGLONG HashTableSize64 = %016llX\n", pHeader->HashTableSize64);
|
||||
printf("ULONGLONG BlockTableSize64 = %016llX\n", pHeader->BlockTableSize64);
|
||||
printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64);
|
||||
printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64);
|
||||
printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64);
|
||||
printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize);
|
||||
printf("-----------------------------------------------\n\n");
|
||||
}
|
||||
|
||||
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if(pHetTable == NULL || pBetTable == NULL)
|
||||
return;
|
||||
|
||||
printf("== HET Header =================================\n");
|
||||
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
|
||||
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
|
||||
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
|
||||
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
|
||||
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
|
||||
printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount);
|
||||
printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize);
|
||||
printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize);
|
||||
printf("-----------------------------------------------\n\n");
|
||||
|
||||
printf("== BET Header =================================\n");
|
||||
printf("DWORD dwTableEntrySize = %08X\n", pBetTable->dwTableEntrySize);
|
||||
printf("DWORD dwBitIndex_FilePos = %08X\n", pBetTable->dwBitIndex_FilePos);
|
||||
printf("DWORD dwBitIndex_FileSize = %08X\n", pBetTable->dwBitIndex_FileSize);
|
||||
printf("DWORD dwBitIndex_CmpSize = %08X\n", pBetTable->dwBitIndex_CmpSize);
|
||||
printf("DWORD dwBitIndex_FlagIndex = %08X\n", pBetTable->dwBitIndex_FlagIndex);
|
||||
printf("DWORD dwBitIndex_Unknown = %08X\n", pBetTable->dwBitIndex_Unknown);
|
||||
printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos);
|
||||
printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize);
|
||||
printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize);
|
||||
printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex);
|
||||
printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown);
|
||||
printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal);
|
||||
printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra);
|
||||
printf("DWORD dwBetHashSize = %08X\n", pBetTable->dwBetHashSize);
|
||||
printf("DWORD dwMaxFileCount = %08X\n", pBetTable->dwMaxFileCount);
|
||||
printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount);
|
||||
printf("-----------------------------------------------\n\n");
|
||||
|
||||
printf("== HET & Bet Table ======================================================================\n\n");
|
||||
printf("HetIdx HetHash BetIdx BetHash ByteOffset FileSize CmpSize FlgIdx Flags \n");
|
||||
printf("------ ------- ------ ---------------- ---------------- -------- -------- ------ --------\n");
|
||||
for(i = 0; i < pHetTable->dwHashTableSize; i++)
|
||||
{
|
||||
ULONGLONG ByteOffset = 0;
|
||||
ULONGLONG BetHash = 0;
|
||||
DWORD dwFileSize = 0;
|
||||
DWORD dwCmpSize = 0;
|
||||
DWORD dwFlagIndex = 0;
|
||||
DWORD dwFlags = 0;
|
||||
DWORD dwBetIndex = 0;
|
||||
|
||||
pHetTable->pBetIndexes->GetBits(i * pHetTable->dwIndexSizeTotal,
|
||||
pHetTable->dwIndexSize,
|
||||
&dwBetIndex,
|
||||
4);
|
||||
|
||||
if(dwBetIndex < pHetTable->dwMaxFileCount)
|
||||
{
|
||||
DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex;
|
||||
|
||||
pBetTable->pBetHashes->GetBits(dwBetIndex * pBetTable->dwBetHashSizeTotal,
|
||||
pBetTable->dwBetHashSize,
|
||||
&BetHash,
|
||||
8);
|
||||
|
||||
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos,
|
||||
pBetTable->dwBitCount_FilePos,
|
||||
&ByteOffset,
|
||||
8);
|
||||
|
||||
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize,
|
||||
pBetTable->dwBitCount_FileSize,
|
||||
&dwFileSize,
|
||||
4);
|
||||
|
||||
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
|
||||
pBetTable->dwBitCount_CmpSize,
|
||||
&dwCmpSize,
|
||||
4);
|
||||
|
||||
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
|
||||
pBetTable->dwBitCount_FlagIndex,
|
||||
&dwFlagIndex,
|
||||
4);
|
||||
|
||||
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
|
||||
}
|
||||
|
||||
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
|
||||
pHetTable->pHetHashes[i],
|
||||
dwBetIndex,
|
||||
BetHash,
|
||||
ByteOffset,
|
||||
dwFileSize,
|
||||
dwCmpSize,
|
||||
dwFlagIndex,
|
||||
dwFlags);
|
||||
}
|
||||
printf("-----------------------------------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
#endif // __STORMLIB_DUMP_DATA__
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,477 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SAttrFile.cpp Copyright (c) Ladislav Zezula 2007 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 12.06.04 1.00 Lad The first version of SAttrFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
typedef struct _MPQ_ATTRIBUTES_HEADER
|
||||
{
|
||||
DWORD dwVersion; // Version of the (attributes) file. Must be 100 (0x64)
|
||||
DWORD dwFlags; // See MPQ_ATTRIBUTE_XXXX
|
||||
|
||||
// Followed by an array of CRC32
|
||||
// Followed by an array of file times
|
||||
// Followed by an array of MD5
|
||||
// Followed by an array of patch bits
|
||||
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions (internal use by StormLib)
|
||||
|
||||
int SAttrLoadAttributes(TMPQArchive * ha)
|
||||
{
|
||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
||||
TMPQFile * hf;
|
||||
HANDLE hFile = NULL;
|
||||
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
|
||||
DWORD dwArraySize;
|
||||
DWORD dwBytesRead;
|
||||
DWORD i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// File table must be initialized
|
||||
assert(ha->pFileTable != NULL);
|
||||
|
||||
// Attempt to open the "(attributes)" file.
|
||||
// If it's not there, then the archive doesn't support attributes
|
||||
if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
|
||||
{
|
||||
// Remember the flags for (attributes)
|
||||
hf = (TMPQFile *)hFile;
|
||||
ha->dwFileFlags2 = hf->pFileEntry->dwFlags;
|
||||
|
||||
// Load the content of the attributes file
|
||||
SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL);
|
||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Verify the header of the (attributes) file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion);
|
||||
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags);
|
||||
ha->dwAttrFlags = AttrHeader.dwFlags;
|
||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Verify format of the attributes
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(AttrHeader.dwVersion > MPQ_ATTRIBUTES_V1)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Load the CRC32 (if any)
|
||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32))
|
||||
{
|
||||
LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize);
|
||||
|
||||
if(pArrayCRC32 != NULL)
|
||||
{
|
||||
dwArraySize = dwBlockTableSize * sizeof(DWORD);
|
||||
SFileReadFile(hFile, pArrayCRC32, dwArraySize, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == dwArraySize)
|
||||
{
|
||||
for(i = 0; i < dwBlockTableSize; i++)
|
||||
ha->pFileTable[i].dwCrc32 = BSWAP_INT32_UNSIGNED(pArrayCRC32[i]);
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
STORM_FREE(pArrayCRC32);
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Read the array of file times
|
||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME))
|
||||
{
|
||||
ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize);
|
||||
|
||||
if(pArrayFileTime != NULL)
|
||||
{
|
||||
dwArraySize = dwBlockTableSize * sizeof(ULONGLONG);
|
||||
SFileReadFile(hFile, pArrayFileTime, dwArraySize, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == dwArraySize)
|
||||
{
|
||||
for(i = 0; i < dwBlockTableSize; i++)
|
||||
ha->pFileTable[i].FileTime = BSWAP_INT64_UNSIGNED(pArrayFileTime[i]);
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
STORM_FREE(pArrayFileTime);
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Read the MD5 (if any)
|
||||
// Note: MD5 array can be incomplete, if it's the last array in the (attributes)
|
||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_MD5))
|
||||
{
|
||||
unsigned char * pArrayMD5 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE));
|
||||
unsigned char * md5;
|
||||
|
||||
if(pArrayMD5 != NULL)
|
||||
{
|
||||
dwArraySize = dwBlockTableSize * MD5_DIGEST_SIZE;
|
||||
SFileReadFile(hFile, pArrayMD5, dwArraySize, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == dwArraySize)
|
||||
{
|
||||
md5 = pArrayMD5;
|
||||
for(i = 0; i < dwBlockTableSize; i++)
|
||||
{
|
||||
memcpy(ha->pFileTable[i].md5, md5, MD5_DIGEST_SIZE);
|
||||
md5 += MD5_DIGEST_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
STORM_FREE(pArrayMD5);
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Read the patch bit for each file
|
||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_PATCH_BIT))
|
||||
{
|
||||
LPBYTE pbBitArray;
|
||||
DWORD dwByteSize = ((dwBlockTableSize - 1) / 8) + 1;
|
||||
|
||||
pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
|
||||
if(pbBitArray != NULL)
|
||||
{
|
||||
SFileReadFile(hFile, pbBitArray, dwByteSize, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == dwByteSize)
|
||||
{
|
||||
for(i = 0; i < dwBlockTableSize; i++)
|
||||
{
|
||||
DWORD dwByteIndex = i / 8;
|
||||
DWORD dwBitMask = 0x80 >> (i & 7);
|
||||
|
||||
// Is the appropriate bit set?
|
||||
if(pbBitArray[dwByteIndex] & dwBitMask)
|
||||
{
|
||||
// At the moment, we assume that the patch bit is present
|
||||
// in both file table and (attributes)
|
||||
assert((ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) != 0);
|
||||
ha->pFileTable[i].dwFlags |= MPQ_FILE_PATCH_FILE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
STORM_FREE(pbBitArray);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
|
||||
// Sometimes, number of entries in the (attributes) was 1 item less
|
||||
// than block table size.
|
||||
// If we encounter such table, we will zero all three arrays
|
||||
//
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
ha->dwAttrFlags = 0;
|
||||
|
||||
// Cleanup & exit
|
||||
SFileCloseFile(hFile);
|
||||
}
|
||||
return nError;
|
||||
}
|
||||
|
||||
int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||
{
|
||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
DWORD dwFinalBlockTableSize = ha->dwFileTableSize;
|
||||
DWORD dwFileSize = 0;
|
||||
DWORD dwToWrite;
|
||||
DWORD i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Now we have to check if we need patch bits in the (attributes)
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
||||
{
|
||||
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
|
||||
{
|
||||
ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the (attributes) is not in the file table yet,
|
||||
// we have to increase the final block table size
|
||||
pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// If "(attributes)" file exists, and it's set to 0, then remove it
|
||||
if(ha->dwAttrFlags == 0)
|
||||
{
|
||||
FreeFileEntry(ha, pFileEntry);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't want to create file atributes, do nothing
|
||||
if(ha->dwAttrFlags == 0)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// Check where the file entry is going to be allocated.
|
||||
// If at the end of the file table, we have to increment
|
||||
// the expected size of the (attributes) file.
|
||||
pFileEntry = FindFreeFileEntry(ha);
|
||||
if(pFileEntry == ha->pFileTable + ha->dwFileTableSize)
|
||||
dwFinalBlockTableSize++;
|
||||
}
|
||||
|
||||
// Calculate the size of the attributes file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
dwFileSize = sizeof(MPQ_ATTRIBUTES_HEADER); // Header
|
||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||
dwFileSize += dwFinalBlockTableSize * sizeof(DWORD);
|
||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||
dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG);
|
||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||
dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE;
|
||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||
dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1;
|
||||
}
|
||||
|
||||
// Determine the flags for (attributes)
|
||||
if(ha->dwFileFlags2 == 0)
|
||||
ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize);
|
||||
|
||||
// Create the attributes file in the MPQ
|
||||
nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
|
||||
0,
|
||||
dwFileSize,
|
||||
LANG_NEUTRAL,
|
||||
ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
|
||||
&hf);
|
||||
|
||||
// Write all parts of the (attributes) file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
assert(ha->dwFileTableSize == dwFinalBlockTableSize);
|
||||
|
||||
// Note that we don't know what the new bit (0x08) means.
|
||||
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100);
|
||||
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
|
||||
dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER);
|
||||
nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
||||
}
|
||||
|
||||
// Write the array of CRC32
|
||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32))
|
||||
{
|
||||
LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize);
|
||||
|
||||
if(pArrayCRC32 != NULL)
|
||||
{
|
||||
// Copy from file table
|
||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
||||
pArrayCRC32[i] = BSWAP_INT32_UNSIGNED(ha->pFileTable[i].dwCrc32);
|
||||
|
||||
dwToWrite = ha->dwFileTableSize * sizeof(DWORD);
|
||||
nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
||||
STORM_FREE(pArrayCRC32);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the array of file time
|
||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME))
|
||||
{
|
||||
ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize);
|
||||
|
||||
if(pArrayFileTime != NULL)
|
||||
{
|
||||
// Copy from file table
|
||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
||||
pArrayFileTime[i] = BSWAP_INT64_UNSIGNED(ha->pFileTable[i].FileTime);
|
||||
|
||||
dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG);
|
||||
nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
||||
STORM_FREE(pArrayFileTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the array of MD5s
|
||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5))
|
||||
{
|
||||
char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE);
|
||||
|
||||
if(pArrayMD5 != NULL)
|
||||
{
|
||||
// Copy from file table
|
||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
||||
memcpy(&pArrayMD5[i * MD5_DIGEST_SIZE], ha->pFileTable[i].md5, MD5_DIGEST_SIZE);
|
||||
|
||||
dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE;
|
||||
nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
||||
STORM_FREE(pArrayMD5);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the array of patch bits
|
||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT))
|
||||
{
|
||||
LPBYTE pbBitArray;
|
||||
DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1;
|
||||
|
||||
pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
|
||||
if(pbBitArray != NULL)
|
||||
{
|
||||
memset(pbBitArray, 0, dwByteSize);
|
||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
||||
{
|
||||
DWORD dwByteIndex = i / 8;
|
||||
DWORD dwBitMask = 0x80 >> (i & 7);
|
||||
|
||||
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
|
||||
pbBitArray[dwByteIndex] |= dwBitMask;
|
||||
}
|
||||
|
||||
nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
|
||||
STORM_FREE(pbBitArray);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the file in the archive
|
||||
if(hf != NULL)
|
||||
{
|
||||
SFileAddFile_Finish(hf);
|
||||
}
|
||||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES;
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
DWORD WINAPI SFileGetAttributes(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return SFILE_INVALID_ATTRIBUTES;
|
||||
}
|
||||
|
||||
return ha->dwAttrFlags;
|
||||
}
|
||||
|
||||
bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not allowed when the archive is read-only
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
{
|
||||
SetLastError(ERROR_ACCESS_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the attributes
|
||||
InvalidateInternalFiles(ha);
|
||||
ha->dwAttrFlags = (dwFlags & MPQ_ATTRIBUTE_ALL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName)
|
||||
{
|
||||
hash_state md5_state;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TMPQFile * hf;
|
||||
BYTE Buffer[0x1000];
|
||||
HANDLE hFile = NULL;
|
||||
DWORD dwTotalBytes = 0;
|
||||
DWORD dwBytesRead;
|
||||
DWORD dwCrc32;
|
||||
|
||||
// Verify the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not allowed when the archive is read-only
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
{
|
||||
SetLastError(ERROR_ACCESS_DENIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to open the file
|
||||
if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||
return false;
|
||||
|
||||
// Get the file size
|
||||
hf = (TMPQFile *)hFile;
|
||||
SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD));
|
||||
|
||||
// Initialize the CRC32 and MD5 contexts
|
||||
md5_init(&md5_state);
|
||||
dwCrc32 = crc32(0, Z_NULL, 0);
|
||||
|
||||
// Go through entire file and calculate both CRC32 and MD5
|
||||
while(dwTotalBytes != 0)
|
||||
{
|
||||
// Read data from file
|
||||
SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL);
|
||||
if(dwBytesRead == 0)
|
||||
break;
|
||||
|
||||
// Update CRC32 and MD5
|
||||
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
|
||||
md5_process(&md5_state, Buffer, dwBytesRead);
|
||||
|
||||
// Decrement the total size
|
||||
dwTotalBytes -= dwBytesRead;
|
||||
}
|
||||
|
||||
// Update both CRC32 and MD5
|
||||
hf->pFileEntry->dwCrc32 = dwCrc32;
|
||||
md5_done(&md5_state, hf->pFileEntry->md5);
|
||||
|
||||
// Remember that we need to save the MPQ tables
|
||||
InvalidateInternalFiles(ha);
|
||||
SFileCloseFile(hFile);
|
||||
return true;
|
||||
}
|
||||
@@ -1,765 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileCompactArchive.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Archive compacting function */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
||||
/* 19.11.03 1.01 Dan Big endian handling */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Local variables */
|
||||
/*****************************************************************************/
|
||||
|
||||
static SFILE_COMPACT_CALLBACK CompactCB = NULL;
|
||||
static ULONGLONG CompactBytesProcessed = 0;
|
||||
static ULONGLONG CompactTotalBytes = 0;
|
||||
static void * pvUserData = NULL;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Local functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys)
|
||||
{
|
||||
TFileEntry * pFileTableEnd;
|
||||
TFileEntry * pFileEntry;
|
||||
DWORD dwBlockIndex = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Add the listfile to the MPQ
|
||||
if(nError == ERROR_SUCCESS && szListFile != NULL)
|
||||
{
|
||||
// Notify the user
|
||||
if(CompactCB != NULL)
|
||||
CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
|
||||
nError = SFileAddListFile((HANDLE)ha, szListFile);
|
||||
}
|
||||
|
||||
// Verify the file table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
|
||||
{
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||
{
|
||||
if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL))
|
||||
{
|
||||
DWORD dwFileKey = 0;
|
||||
|
||||
// Resolve the file key. Use plain file name for it
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
{
|
||||
dwFileKey = DecryptFileKey(pFileEntry->szFileName,
|
||||
pFileEntry->ByteOffset,
|
||||
pFileEntry->dwFileSize,
|
||||
pFileEntry->dwFlags);
|
||||
}
|
||||
|
||||
// Give the key to the caller
|
||||
if(pFileKeys != NULL)
|
||||
pFileKeys[dwBlockIndex] = dwFileKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int CopyNonMpqData(
|
||||
TFileStream * pSrcStream,
|
||||
TFileStream * pTrgStream,
|
||||
ULONGLONG & ByteOffset,
|
||||
ULONGLONG & ByteCount)
|
||||
{
|
||||
ULONGLONG DataSize = ByteCount;
|
||||
DWORD dwToRead;
|
||||
char DataBuffer[0x1000];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Copy the data
|
||||
while(DataSize > 0)
|
||||
{
|
||||
// Get the proper size of data
|
||||
dwToRead = sizeof(DataBuffer);
|
||||
if(DataSize < dwToRead)
|
||||
dwToRead = (DWORD)DataSize;
|
||||
|
||||
// Read from the source stream
|
||||
if(!FileStream_Read(pSrcStream, &ByteOffset, DataBuffer, dwToRead))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Write to the target stream
|
||||
if(!FileStream_Write(pTrgStream, NULL, DataBuffer, dwToRead))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the progress
|
||||
if(CompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwToRead;
|
||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
||||
}
|
||||
|
||||
// Decrement the number of data to be copied
|
||||
ByteOffset += dwToRead;
|
||||
DataSize -= dwToRead;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Copies all file sectors into another archive.
|
||||
static int CopyMpqFileSectors(
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
TFileStream * pNewStream)
|
||||
{
|
||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||
ULONGLONG RawFilePos; // Used for calculating sector offset in the old MPQ archive
|
||||
ULONGLONG MpqFilePos; // MPQ file position in the new archive
|
||||
DWORD dwBytesToCopy = pFileEntry->dwCmpSize;
|
||||
DWORD dwPatchSize = 0; // Size of patch header
|
||||
DWORD dwFileKey1 = 0; // File key used for decryption
|
||||
DWORD dwFileKey2 = 0; // File key used for encryption
|
||||
DWORD dwCmpSize = 0; // Compressed file size, including patch header
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Remember the position in the destination file
|
||||
FileStream_GetPos(pNewStream, MpqFilePos);
|
||||
MpqFilePos -= ha->MpqPos;
|
||||
|
||||
// Resolve decryption keys. Note that the file key given
|
||||
// in the TMPQFile structure also includes the key adjustment
|
||||
if(nError == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED))
|
||||
{
|
||||
dwFileKey2 = dwFileKey1 = hf->dwFileKey;
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY)
|
||||
{
|
||||
dwFileKey2 = (dwFileKey1 ^ pFileEntry->dwFileSize) - (DWORD)pFileEntry->ByteOffset;
|
||||
dwFileKey2 = (dwFileKey2 + (DWORD)MpqFilePos) ^ pFileEntry->dwFileSize;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have to save patch header, do it
|
||||
if(nError == ERROR_SUCCESS && hf->pPatchInfo != NULL)
|
||||
{
|
||||
BSWAP_ARRAY32_UNSIGNED(hf->pPatchInfo, sizeof(DWORD) * 3);
|
||||
if(!FileStream_Write(pNewStream, NULL, hf->pPatchInfo, hf->pPatchInfo->dwLength))
|
||||
nError = GetLastError();
|
||||
|
||||
// Save the size of the patch info
|
||||
dwPatchSize = hf->pPatchInfo->dwLength;
|
||||
}
|
||||
|
||||
// If we have to save sector offset table, do it.
|
||||
if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL)
|
||||
{
|
||||
DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]);
|
||||
DWORD dwSectorOffsLen = hf->SectorOffsets[0];
|
||||
|
||||
assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0);
|
||||
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED);
|
||||
|
||||
if(SectorOffsetsCopy == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Encrypt the secondary sector offset table and write it to the target file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen);
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwFileKey2 - 1);
|
||||
|
||||
BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen);
|
||||
if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen))
|
||||
nError = GetLastError();
|
||||
|
||||
dwBytesToCopy -= dwSectorOffsLen;
|
||||
dwCmpSize += dwSectorOffsLen;
|
||||
}
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwSectorOffsLen;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
}
|
||||
|
||||
STORM_FREE(SectorOffsetsCopy);
|
||||
}
|
||||
|
||||
// Now we have to copy all file sectors. We do it without
|
||||
// recompression, because recompression is not necessary in this case
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++)
|
||||
{
|
||||
DWORD dwRawDataInSector = hf->dwSectorSize;
|
||||
DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;
|
||||
|
||||
// Fix the raw data length if the file is compressed
|
||||
if(hf->SectorOffsets != NULL)
|
||||
{
|
||||
dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector];
|
||||
dwRawByteOffset = hf->SectorOffsets[dwSector];
|
||||
}
|
||||
|
||||
// Last sector: If there is not enough bytes remaining in the file, cut the raw size
|
||||
if(dwRawDataInSector > dwBytesToCopy)
|
||||
dwRawDataInSector = dwBytesToCopy;
|
||||
|
||||
// Calculate the raw file offset of the file sector
|
||||
CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);
|
||||
|
||||
// Read the file sector
|
||||
if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// If necessary, re-encrypt the sector
|
||||
// Note: Recompression is not necessary here. Unlike encryption,
|
||||
// the compression does not depend on the position of the file in MPQ.
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2)
|
||||
{
|
||||
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
|
||||
DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey1 + dwSector);
|
||||
EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey2 + dwSector);
|
||||
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
|
||||
}
|
||||
|
||||
// Now write the sector back to the file
|
||||
if(!FileStream_Write(pNewStream, NULL, hf->pbFileSector, dwRawDataInSector))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwRawDataInSector;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
}
|
||||
|
||||
// Adjust byte counts
|
||||
dwBytesToCopy -= dwRawDataInSector;
|
||||
dwCmpSize += dwRawDataInSector;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the sector CRCs, if any
|
||||
// Sector CRCs are always compressed (not imploded) and unencrypted
|
||||
if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL && hf->SectorChksums != NULL)
|
||||
{
|
||||
DWORD dwCrcLength;
|
||||
|
||||
dwCrcLength = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount];
|
||||
if(dwCrcLength != 0)
|
||||
{
|
||||
if(!FileStream_Read(ha->pStream, NULL, hf->SectorChksums, dwCrcLength))
|
||||
nError = GetLastError();
|
||||
|
||||
if(!FileStream_Write(pNewStream, NULL, hf->SectorChksums, dwCrcLength))
|
||||
nError = GetLastError();
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwCrcLength;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
}
|
||||
|
||||
// Size of the CRC block is also included in the compressed file size
|
||||
dwBytesToCopy -= dwCrcLength;
|
||||
dwCmpSize += dwCrcLength;
|
||||
}
|
||||
}
|
||||
|
||||
// There might be extra data beyond sector checksum table
|
||||
// Sometimes, these data are even part of sector offset table
|
||||
// Examples:
|
||||
// 2012 - WoW\15354\locale-enGB.MPQ:DBFilesClient\SpellLevels.dbc
|
||||
// 2012 - WoW\15354\locale-enGB.MPQ:Interface\AddOns\Blizzard_AuctionUI\Blizzard_AuctionUI.xml
|
||||
if(nError == ERROR_SUCCESS && dwBytesToCopy != 0)
|
||||
{
|
||||
LPBYTE pbExtraData;
|
||||
|
||||
// Allocate space for the extra data
|
||||
pbExtraData = STORM_ALLOC(BYTE, dwBytesToCopy);
|
||||
if(pbExtraData != NULL)
|
||||
{
|
||||
if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy))
|
||||
nError = GetLastError();
|
||||
|
||||
if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy))
|
||||
nError = GetLastError();
|
||||
|
||||
// Include these extra data in the compressed size
|
||||
dwCmpSize += dwBytesToCopy;
|
||||
dwBytesToCopy = 0;
|
||||
STORM_FREE(pbExtraData);
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Write the MD5's of the raw file data, if needed
|
||||
if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0)
|
||||
{
|
||||
nError = WriteMpqDataMD5(pNewStream,
|
||||
ha->MpqPos + MpqFilePos,
|
||||
pFileEntry->dwCmpSize,
|
||||
ha->pHeader->dwRawChunkSize);
|
||||
}
|
||||
|
||||
// Update file position in the block table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// At this point, number of bytes written should be exactly
|
||||
// the same like the compressed file size. If it isn't,
|
||||
// there's something wrong (an unknown archive version, MPQ protection, ...)
|
||||
//
|
||||
// Note: Diablo savegames have very weird layout, and the file "hero"
|
||||
// seems to have improper compressed size. Instead of real compressed size,
|
||||
// the "dwCmpSize" member of the block table entry contains
|
||||
// uncompressed size of file data + size of the sector table.
|
||||
// If we compact the archive, Diablo will refuse to load the game
|
||||
// Seems like some sort of protection to me.
|
||||
//
|
||||
// Note: Some patch files in WOW patches don't count the patch header
|
||||
// into compressed size
|
||||
//
|
||||
|
||||
if(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize)
|
||||
{
|
||||
// Note: DO NOT update the compressed size in the file entry, no matter how bad it is.
|
||||
pFileEntry->ByteOffset = MpqFilePos;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Walk through all files and write them to the destination MPQ archive
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
{
|
||||
// Copy all the file sectors
|
||||
// Only do that when the file has nonzero size
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0)
|
||||
{
|
||||
// Allocate structure for the MPQ file
|
||||
hf = CreateMpqFile(ha);
|
||||
if(hf == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Store file entry
|
||||
hf->pFileEntry = pFileEntry;
|
||||
|
||||
// Set the raw file position
|
||||
hf->MpqFilePos = pFileEntry->ByteOffset;
|
||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
||||
|
||||
// Set the file decryption key
|
||||
hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable];
|
||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
||||
|
||||
// If the file is a patch file, load the patch header
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||
{
|
||||
nError = AllocatePatchInfo(hf, true);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate buffers for file sector and sector offset table
|
||||
nError = AllocateSectorBuffer(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Also allocate sector offset table and sector checksum table
|
||||
nError = AllocateSectorOffsets(hf, true);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Also load sector checksums, if any
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)
|
||||
{
|
||||
nError = AllocateSectorChecksums(hf, false);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy all file sectors
|
||||
nError = CopyMpqFileSectors(ha, hf, pNewStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Free buffers. This also sets "hf" to NULL.
|
||||
FreeMPQFile(hf);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup and exit
|
||||
if(hf != NULL)
|
||||
FreeMPQFile(hf);
|
||||
return nError;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Public functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
bool WINAPI SFileSetCompactCallback(HANDLE /* hMpq */, SFILE_COMPACT_CALLBACK aCompactCB, void * pvData)
|
||||
{
|
||||
CompactCB = aCompactCB;
|
||||
pvUserData = pvData;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Archive compacting
|
||||
|
||||
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */)
|
||||
{
|
||||
TFileStream * pTempStream = NULL;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
ULONGLONG ByteOffset;
|
||||
ULONGLONG ByteCount;
|
||||
LPDWORD pFileKeys = NULL;
|
||||
TCHAR szTempFile[MAX_PATH] = _T("");
|
||||
TCHAR * szTemp = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Test the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
|
||||
// If the MPQ is changed at this moment, we have to flush the archive
|
||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED))
|
||||
{
|
||||
SFileFlushArchive(hMpq);
|
||||
}
|
||||
|
||||
// Create the table with file keys
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL)
|
||||
memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize);
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// First of all, we have to check of we are able to decrypt all files.
|
||||
// If not, sorry, but the archive cannot be compacted.
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the progress variables for compact callback
|
||||
FileStream_GetSize(ha->pStream, CompactTotalBytes);
|
||||
CompactBytesProcessed = 0;
|
||||
nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
|
||||
}
|
||||
|
||||
// Get the temporary file name and create it
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
_tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
|
||||
if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
|
||||
_tcscpy(szTemp + 1, _T("mp_"));
|
||||
else
|
||||
_tcscat(szTempFile, _T("_"));
|
||||
|
||||
pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
||||
if(pTempStream == NULL)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Write the data before MPQ user data (if any)
|
||||
if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
|
||||
{
|
||||
// Inform the application about the progress
|
||||
if(CompactCB != NULL)
|
||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
||||
|
||||
ByteOffset = 0;
|
||||
ByteCount = ha->UserDataPos;
|
||||
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||
}
|
||||
|
||||
// Write the MPQ user data (if any)
|
||||
if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
|
||||
{
|
||||
// At this point, we assume that the user data size is equal
|
||||
// to pUserData->dwHeaderOffs.
|
||||
// If this assumption doesn't work, then we have an unknown version of MPQ
|
||||
ByteOffset = ha->UserDataPos;
|
||||
ByteCount = ha->MpqPos - ha->UserDataPos;
|
||||
|
||||
assert(ha->pUserData != NULL);
|
||||
assert(ha->pUserData->dwHeaderOffs == ByteCount);
|
||||
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||
}
|
||||
|
||||
// Write the MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Remember the header size before swapping
|
||||
DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize;
|
||||
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite))
|
||||
nError = GetLastError();
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
|
||||
// Update the progress
|
||||
CompactBytesProcessed += ha->pHeader->dwHeaderSize;
|
||||
}
|
||||
|
||||
// Now copy all files
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = CopyMpqFiles(ha, pFileKeys, pTempStream);
|
||||
ha->dwFlags |= MPQ_FLAG_CHANGED;
|
||||
}
|
||||
|
||||
// If succeeded, switch the streams
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(FileStream_Switch(ha->pStream, pTempStream))
|
||||
pTempStream = NULL;
|
||||
else
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
}
|
||||
|
||||
// If all succeeded, save the MPQ tables
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
//
|
||||
// Note: We don't recalculate position of the MPQ tables at this point.
|
||||
// SaveMPQTables does it automatically.
|
||||
//
|
||||
|
||||
nError = SaveMPQTables(ha);
|
||||
if(nError == ERROR_SUCCESS && CompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
||||
CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
|
||||
CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate the compact callback
|
||||
pvUserData = NULL;
|
||||
CompactCB = NULL;
|
||||
|
||||
// Cleanup and return
|
||||
if(pTempStream != NULL)
|
||||
FileStream_Close(pTempStream);
|
||||
if(pFileKeys != NULL)
|
||||
STORM_FREE(pFileKeys);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Changing hash table size
|
||||
|
||||
DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
return ha->dwMaxFileCount;
|
||||
}
|
||||
|
||||
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
|
||||
{
|
||||
TMPQHetTable * pOldHetTable = NULL;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pOldFileTable = NULL;
|
||||
TFileEntry * pOldFileEntry;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHash * pOldHashTable = NULL;
|
||||
DWORD dwOldHashTableSize = 0;
|
||||
DWORD dwOldFileTableSize = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Test the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
|
||||
// The new limit must not be lower than the index of the last file entry in the table
|
||||
if(nError == ERROR_SUCCESS && ha->dwFileTableSize > dwMaxFileCount)
|
||||
nError = ERROR_DISK_FULL;
|
||||
|
||||
// ALL file names must be known in order to be able
|
||||
// to rebuild hash table size
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = CheckIfAllFilesKnown(ha, NULL, NULL);
|
||||
}
|
||||
|
||||
// If the MPQ has a hash table, then we relocate the hash table
|
||||
if(nError == ERROR_SUCCESS && ha->pHashTable != NULL)
|
||||
{
|
||||
// Save parameters for the current hash table
|
||||
dwOldHashTableSize = ha->pHeader->dwHashTableSize;
|
||||
pOldHashTable = ha->pHashTable;
|
||||
|
||||
// Allocate new hash table
|
||||
ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
||||
ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize);
|
||||
if(ha->pHashTable != NULL)
|
||||
memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// If the MPQ has HET table, allocate new one as well
|
||||
if(nError == ERROR_SUCCESS && ha->pHetTable != NULL)
|
||||
{
|
||||
// Save the original HET table
|
||||
pOldHetTable = ha->pHetTable;
|
||||
|
||||
// Create new one
|
||||
ha->pHetTable = CreateHetTable(dwMaxFileCount, 0x40, true);
|
||||
if(ha->pHetTable == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Now reallocate the file table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Save the current file table
|
||||
dwOldFileTableSize = ha->dwFileTableSize;
|
||||
pOldFileTable = ha->pFileTable;
|
||||
|
||||
// Create new one
|
||||
ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount);
|
||||
if(ha->pFileTable != NULL)
|
||||
memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry));
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Now we have to build both classic hash table and HET table.
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
DWORD dwFileIndex = 0;
|
||||
DWORD dwHashIndex = 0;
|
||||
|
||||
// Create new hash and HET entry for each file
|
||||
pFileEntry = ha->pFileTable;
|
||||
for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++)
|
||||
{
|
||||
if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||
{
|
||||
// Copy the old file entry to the new one
|
||||
memcpy(pFileEntry, pOldFileEntry, sizeof(TFileEntry));
|
||||
assert(pFileEntry->szFileName != NULL);
|
||||
|
||||
// Create new entry in the hash table
|
||||
if(ha->pHashTable != NULL)
|
||||
{
|
||||
dwHashIndex = AllocateHashEntry(ha, pFileEntry);
|
||||
if(dwHashIndex == HASH_ENTRY_FREE)
|
||||
{
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new entry in the HET table, if needed
|
||||
if(ha->pHetTable != NULL)
|
||||
{
|
||||
dwHashIndex = AllocateHetEntry(ha, pFileEntry);
|
||||
if(dwHashIndex == HASH_ENTRY_FREE)
|
||||
{
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next file entry in the new table
|
||||
pFileEntry++;
|
||||
dwFileIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the archive as changed
|
||||
// Note: We always have to rebuild the (attributes) file due to file table change
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ha->dwMaxFileCount = dwMaxFileCount;
|
||||
InvalidateInternalFiles(ha);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert the hash table
|
||||
if(ha->pHashTable != NULL && pOldHashTable != NULL)
|
||||
{
|
||||
STORM_FREE(ha->pHashTable);
|
||||
ha->pHeader->dwHashTableSize = dwOldHashTableSize;
|
||||
ha->pHashTable = pOldHashTable;
|
||||
}
|
||||
|
||||
// Revert the HET table
|
||||
if(ha->pHetTable != NULL && pOldHetTable != NULL)
|
||||
{
|
||||
FreeHetTable(ha->pHetTable);
|
||||
ha->pHetTable = pOldHetTable;
|
||||
}
|
||||
|
||||
// Revert the file table
|
||||
if(pOldFileTable != NULL)
|
||||
{
|
||||
STORM_FREE(ha->pFileTable);
|
||||
ha->pFileTable = pOldFileTable;
|
||||
}
|
||||
|
||||
SetLastError(nError);
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileCreateArchive.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* MPQ Editing functions */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 24.03.03 1.00 Lad Splitted from SFileOpenArchive.cpp */
|
||||
/* 08.06.10 1.00 Lad Renamed to SFileCreateArchive.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local variables
|
||||
|
||||
static const DWORD MpqHeaderSizes[] =
|
||||
{
|
||||
MPQ_HEADER_SIZE_V1,
|
||||
MPQ_HEADER_SIZE_V2,
|
||||
MPQ_HEADER_SIZE_V3,
|
||||
MPQ_HEADER_SIZE_V4
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static USHORT GetSectorSizeShift(DWORD dwSectorSize)
|
||||
{
|
||||
USHORT wSectorSizeShift = 0;
|
||||
|
||||
while(dwSectorSize > 0x200)
|
||||
{
|
||||
dwSectorSize >>= 1;
|
||||
wSectorSizeShift++;
|
||||
}
|
||||
|
||||
return wSectorSizeShift;
|
||||
}
|
||||
|
||||
static int WriteNakedMPQHeader(TMPQArchive * ha)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
TMPQHeader Header;
|
||||
DWORD dwBytesToWrite = pHeader->dwHeaderSize;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Prepare the naked MPQ header
|
||||
memset(&Header, 0, sizeof(TMPQHeader));
|
||||
Header.dwID = pHeader->dwID;
|
||||
Header.dwHeaderSize = pHeader->dwHeaderSize;
|
||||
Header.dwArchiveSize = pHeader->dwHeaderSize;
|
||||
Header.wFormatVersion = pHeader->wFormatVersion;
|
||||
Header.wSectorSize = pHeader->wSectorSize;
|
||||
|
||||
// Write it to the file
|
||||
BSWAP_TMPQHEADER(&Header);
|
||||
if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
|
||||
nError = GetLastError();
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a new MPQ archive.
|
||||
|
||||
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
|
||||
{
|
||||
SFILE_CREATE_MPQ CreateInfo;
|
||||
|
||||
// Fill the create structure
|
||||
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
|
||||
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
|
||||
CreateInfo.dwMpqVersion = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
|
||||
CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE;
|
||||
CreateInfo.dwAttrFlags = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0;
|
||||
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
|
||||
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
|
||||
CreateInfo.dwMaxFileCount = dwMaxFileCount;
|
||||
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
|
||||
}
|
||||
|
||||
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
|
||||
{
|
||||
TFileStream * pStream = NULL; // File stream
|
||||
TMPQArchive * ha = NULL; // MPQ archive handle
|
||||
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
|
||||
HANDLE hMpq = NULL;
|
||||
DWORD dwBlockTableSize = 0; // Initial block table size
|
||||
DWORD dwHashTableSize = 0;
|
||||
DWORD dwMaxFileCount;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check the parameters, if they are valid
|
||||
if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify if all variables in SFILE_CREATE_MPQ are correct
|
||||
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
|
||||
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
|
||||
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
|
||||
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
|
||||
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
|
||||
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)) ||
|
||||
(pCreateInfo->dwMaxFileCount < 4))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// One time initialization of MPQ cryptography
|
||||
InitializeMpqCryptography();
|
||||
|
||||
// We verify if the file already exists and if it's a MPQ archive.
|
||||
// If yes, we won't allow to overwrite it.
|
||||
if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
|
||||
{
|
||||
SFileCloseArchive(hMpq);
|
||||
SetLastError(ERROR_ALREADY_EXISTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, we have to create the archive.
|
||||
// - If the file exists, convert it to MPQ archive.
|
||||
// - If the file doesn't exist, create new empty file
|
||||
//
|
||||
|
||||
pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
|
||||
if(pStream == NULL)
|
||||
{
|
||||
pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
|
||||
if(pStream == NULL)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increment the maximum amount of files to have space
|
||||
// for listfile and attributes file
|
||||
dwMaxFileCount = pCreateInfo->dwMaxFileCount;
|
||||
if(pCreateInfo->dwAttrFlags != 0)
|
||||
dwMaxFileCount++;
|
||||
dwMaxFileCount++;
|
||||
|
||||
// If file count is not zero, initialize the hash table size
|
||||
dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
||||
|
||||
// Retrieve the file size and round it up to 0x200 bytes
|
||||
FileStream_GetSize(pStream, MpqPos);
|
||||
MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL;
|
||||
if(!FileStream_SetSize(pStream, MpqPos))
|
||||
nError = GetLastError();
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Debug code, used for testing StormLib
|
||||
// dwBlockTableSize = dwHashTableSize * 2;
|
||||
#endif
|
||||
|
||||
// Create the archive handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Fill the MPQ archive handle structure
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memset(ha, 0, sizeof(TMPQArchive));
|
||||
ha->pStream = pStream;
|
||||
ha->dwSectorSize = pCreateInfo->dwSectorSize;
|
||||
ha->UserDataPos = MpqPos;
|
||||
ha->MpqPos = MpqPos;
|
||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
||||
ha->dwMaxFileCount = dwMaxFileCount;
|
||||
ha->dwFileTableSize = 0;
|
||||
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
|
||||
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
|
||||
ha->dwFlags = 0;
|
||||
|
||||
// Setup the attributes
|
||||
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
|
||||
pStream = NULL;
|
||||
}
|
||||
|
||||
// Fill the MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
|
||||
// Fill the MPQ header
|
||||
memset(pHeader, 0, sizeof(ha->HeaderData));
|
||||
pHeader->dwID = ID_MPQ;
|
||||
pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
|
||||
pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
|
||||
pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
|
||||
pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
|
||||
pHeader->dwHashTablePos = pHeader->dwHeaderSize;
|
||||
pHeader->dwHashTableSize = dwHashTableSize;
|
||||
pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash);
|
||||
pHeader->dwBlockTableSize = dwBlockTableSize;
|
||||
|
||||
// For MPQs version 4 and higher, we set the size of raw data block
|
||||
// for calculating MD5
|
||||
if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
|
||||
pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
|
||||
|
||||
// Write the naked MPQ header
|
||||
nError = WriteNakedMPQHeader(ha);
|
||||
|
||||
// Remember that the (listfile) and (attributes) need to be saved
|
||||
ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES;
|
||||
}
|
||||
|
||||
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
|
||||
if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3)
|
||||
{
|
||||
ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
|
||||
if(ha->pHetTable == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Create initial hash table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = CreateHashTable(ha, dwHashTableSize);
|
||||
}
|
||||
|
||||
// Create initial file table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
|
||||
if(ha->pFileTable != NULL)
|
||||
memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount);
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Cleanup : If an error, delete all buffers and return
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
FreeMPQArchive(ha);
|
||||
SetLastError(nError);
|
||||
ha = NULL;
|
||||
}
|
||||
|
||||
// Return the values
|
||||
*phMpq = (HANDLE)ha;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileExtractFile.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Simple extracting utility */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 20.06.03 1.00 Lad The first version of SFileExtractFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope)
|
||||
{
|
||||
TFileStream * pLocalFile = NULL;
|
||||
HANDLE hMpqFile = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Open the MPQ file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile))
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Create the local file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
||||
if(pLocalFile == NULL)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Copy the file's content
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
char szBuffer[0x1000];
|
||||
DWORD dwTransferred;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// dwTransferred is only set to nonzero if something has been read.
|
||||
// nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF
|
||||
if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL))
|
||||
nError = GetLastError();
|
||||
if(nError == ERROR_HANDLE_EOF)
|
||||
nError = ERROR_SUCCESS;
|
||||
if(dwTransferred == 0)
|
||||
break;
|
||||
|
||||
// If something has been actually read, write it
|
||||
if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred))
|
||||
nError = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the files
|
||||
if(hMpqFile != NULL)
|
||||
SFileCloseFile(hMpqFile);
|
||||
if(pLocalFile != NULL)
|
||||
FileStream_Close(pLocalFile);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
@@ -1,446 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* A module for file searching within MPQs */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define LISTFILE_CACHE_SIZE 0x1000
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private structure used for file search (search handle)
|
||||
|
||||
struct TMPQSearch;
|
||||
typedef int (*MPQSEARCH)(TMPQSearch *, SFILE_FIND_DATA *);
|
||||
|
||||
// Used by searching in MPQ archives
|
||||
struct TMPQSearch
|
||||
{
|
||||
TMPQArchive * ha; // Handle to MPQ, where the search runs
|
||||
TFileEntry ** pSearchTable; // Table for files that have been already found
|
||||
DWORD dwSearchTableItems; // Number of items in the search table
|
||||
DWORD dwNextIndex; // Next file index to be checked
|
||||
DWORD dwFlagMask; // For checking flag mask
|
||||
char szSearchMask[1]; // Search mask (variable length)
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool IsValidSearchHandle(TMPQSearch * hs)
|
||||
{
|
||||
if(hs == NULL)
|
||||
return false;
|
||||
|
||||
return IsValidMpqHandle(hs->ha);
|
||||
}
|
||||
|
||||
bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||
{
|
||||
const char * szSubString;
|
||||
int nSubStringLength;
|
||||
int nMatchCount = 0;
|
||||
|
||||
// When the mask is empty, it never matches
|
||||
if(szWildCard == NULL || *szWildCard == 0)
|
||||
return false;
|
||||
|
||||
// If the wildcard contains just "*", then it always matches
|
||||
if(szWildCard[0] == '*' && szWildCard[1] == 0)
|
||||
return true;
|
||||
|
||||
// Do normal test
|
||||
for(;;)
|
||||
{
|
||||
// If there is '?' in the wildcard, we skip one char
|
||||
while(*szWildCard == '?')
|
||||
{
|
||||
szWildCard++;
|
||||
szString++;
|
||||
}
|
||||
|
||||
// If there is '*', means zero or more chars. We have to
|
||||
// find the sequence after '*'
|
||||
if(*szWildCard == '*')
|
||||
{
|
||||
// More stars is equal to one star
|
||||
while(*szWildCard == '*' || *szWildCard == '?')
|
||||
szWildCard++;
|
||||
|
||||
// If we found end of the wildcard, it's a match
|
||||
if(*szWildCard == 0)
|
||||
return true;
|
||||
|
||||
// Determine the length of the substring in szWildCard
|
||||
szSubString = szWildCard;
|
||||
while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*')
|
||||
szSubString++;
|
||||
nSubStringLength = (int)(szSubString - szWildCard);
|
||||
nMatchCount = 0;
|
||||
|
||||
// Now we have to find a substring in szString,
|
||||
// that matches the substring in szWildCard
|
||||
while(*szString != 0)
|
||||
{
|
||||
// Calculate match count
|
||||
while(nMatchCount < nSubStringLength)
|
||||
{
|
||||
if(toupper(szString[nMatchCount]) != toupper(szWildCard[nMatchCount]))
|
||||
break;
|
||||
if(szString[nMatchCount] == 0)
|
||||
break;
|
||||
nMatchCount++;
|
||||
}
|
||||
|
||||
// If the match count has reached substring length, we found a match
|
||||
if(nMatchCount == nSubStringLength)
|
||||
{
|
||||
szWildCard += nMatchCount;
|
||||
szString += nMatchCount;
|
||||
break;
|
||||
}
|
||||
|
||||
// No match, move to the next char in szString
|
||||
nMatchCount = 0;
|
||||
szString++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we came to the end of the string, compare it to the wildcard
|
||||
if(toupper(*szString) != toupper(*szWildCard))
|
||||
return false;
|
||||
|
||||
// If we arrived to the end of the string, it's a match
|
||||
if(*szString == 0)
|
||||
return true;
|
||||
|
||||
// Otherwise, continue in comparing
|
||||
szWildCard++;
|
||||
szString++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD GetSearchTableItems(TMPQArchive * ha)
|
||||
{
|
||||
DWORD dwMergeItems = 0;
|
||||
|
||||
// Loop over all patches
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Append the number of files
|
||||
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount
|
||||
: ha->pHeader->dwBlockTableSize;
|
||||
// Move to the patched archive
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
// Return the double size of number of items
|
||||
return (dwMergeItems | 1);
|
||||
}
|
||||
|
||||
static bool FileWasFoundBefore(
|
||||
TMPQArchive * ha,
|
||||
TMPQSearch * hs,
|
||||
TFileEntry * pFileEntry)
|
||||
{
|
||||
TFileEntry * pEntry;
|
||||
char * szRealFileName = pFileEntry->szFileName;
|
||||
DWORD dwStartIndex;
|
||||
DWORD dwNameHash;
|
||||
DWORD dwIndex;
|
||||
|
||||
if(hs->pSearchTable != NULL && szRealFileName != NULL)
|
||||
{
|
||||
// If we are in patch MPQ, we check if patch prefix matches
|
||||
// and then trim the patch prefix
|
||||
if(ha->cchPatchPrefix != 0)
|
||||
{
|
||||
// If the patch prefix doesn't fit, we pretend that the file
|
||||
// was there before and it will be skipped
|
||||
if(_strnicmp(szRealFileName, ha->szPatchPrefix, ha->cchPatchPrefix))
|
||||
return true;
|
||||
|
||||
szRealFileName += ha->cchPatchPrefix;
|
||||
}
|
||||
|
||||
// Calculate the hash to the table
|
||||
dwNameHash = HashString(szRealFileName, MPQ_HASH_NAME_A);
|
||||
dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems);
|
||||
|
||||
// The file might have been found before
|
||||
// only if this is not the first MPQ being searched
|
||||
if(ha->haBase != NULL)
|
||||
{
|
||||
// Enumerate all entries in the search table
|
||||
for(;;)
|
||||
{
|
||||
// Get the file entry at that position
|
||||
pEntry = hs->pSearchTable[dwIndex];
|
||||
if(pEntry == NULL)
|
||||
break;
|
||||
|
||||
if(pEntry->szFileName != NULL)
|
||||
{
|
||||
// Does the name match?
|
||||
if(!_stricmp(pEntry->szFileName, szRealFileName))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
dwIndex = (dwIndex + 1) % hs->dwSearchTableItems;
|
||||
if(dwIndex == dwStartIndex)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Put the entry to the table for later use
|
||||
hs->pSearchTable[dwIndex] = pFileEntry;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
|
||||
{
|
||||
TFileEntry * pPatchEntry = NULL;
|
||||
TFileEntry * pTempEntry;
|
||||
char szFileName[MAX_PATH];
|
||||
LCID lcLocale = pFileEntry->lcLocale;
|
||||
|
||||
// Go while there are patches
|
||||
while(ha->haPatch != NULL)
|
||||
{
|
||||
// Move to the patch archive
|
||||
ha = ha->haPatch;
|
||||
|
||||
// Prepare the prefix for the file name
|
||||
strcpy(szFileName, ha->szPatchPrefix);
|
||||
strcat(szFileName, pFileEntry->szFileName);
|
||||
|
||||
// Try to find the file there
|
||||
pTempEntry = GetFileEntryExact(ha, szFileName, lcLocale);
|
||||
if(pTempEntry != NULL)
|
||||
pPatchEntry = pTempEntry;
|
||||
}
|
||||
|
||||
// Return the found patch entry
|
||||
return pPatchEntry;
|
||||
}
|
||||
|
||||
// Performs one MPQ search
|
||||
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TMPQArchive * ha = hs->ha;
|
||||
TFileEntry * pFileTableEnd;
|
||||
TFileEntry * pPatchEntry;
|
||||
TFileEntry * pFileEntry;
|
||||
const char * szFileName;
|
||||
HANDLE hFile;
|
||||
char szPseudoName[20];
|
||||
DWORD dwBlockIndex;
|
||||
size_t nPrefixLength;
|
||||
|
||||
// Start searching with base MPQ
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Now parse the file entry table in order to get all files.
|
||||
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
pFileEntry = ha->pFileTable + hs->dwNextIndex;
|
||||
|
||||
// Get the length of the patch prefix (0 if none)
|
||||
nPrefixLength = strlen(ha->szPatchPrefix);
|
||||
|
||||
// Parse the file table
|
||||
while(pFileEntry < pFileTableEnd)
|
||||
{
|
||||
// Increment the next index for subsequent search
|
||||
hs->dwNextIndex++;
|
||||
|
||||
// Is it a file and not a patch file?
|
||||
if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
|
||||
{
|
||||
// Now we have to check if this file was not enumerated before
|
||||
if(!FileWasFoundBefore(ha, hs, pFileEntry))
|
||||
{
|
||||
// Find a patch to this file
|
||||
pPatchEntry = FindPatchEntry(ha, pFileEntry);
|
||||
if(pPatchEntry == NULL)
|
||||
pPatchEntry = pFileEntry;
|
||||
|
||||
// Prepare the block index
|
||||
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
|
||||
|
||||
// Get the file name. If it's not known, we will create pseudo-name
|
||||
szFileName = pFileEntry->szFileName;
|
||||
if(szFileName == NULL)
|
||||
{
|
||||
// Open the file by its pseudo-name.
|
||||
// This also generates the file name with a proper extension
|
||||
sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex);
|
||||
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||
{
|
||||
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
|
||||
SFileCloseFile(hFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the file name against the wildcard
|
||||
if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
|
||||
{
|
||||
// Fill the found entry
|
||||
lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex;
|
||||
lpFindFileData->dwBlockIndex = dwBlockIndex;
|
||||
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
|
||||
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
|
||||
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
|
||||
lpFindFileData->lcLocale = pPatchEntry->lcLocale;
|
||||
|
||||
// Fill the filetime
|
||||
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
|
||||
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
|
||||
|
||||
// Fill the file name and plain file name
|
||||
strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
|
||||
lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pFileEntry++;
|
||||
}
|
||||
|
||||
// Move to the next patch in the patch chain
|
||||
hs->ha = ha = ha->haPatch;
|
||||
hs->dwNextIndex = 0;
|
||||
}
|
||||
|
||||
// No more files found, return error
|
||||
return ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
static void FreeMPQSearch(TMPQSearch *& hs)
|
||||
{
|
||||
if(hs != NULL)
|
||||
{
|
||||
if(hs->pSearchTable != NULL)
|
||||
STORM_FREE(hs->pSearchTable);
|
||||
STORM_FREE(hs);
|
||||
hs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TMPQSearch * hs = NULL;
|
||||
size_t nSize = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check for the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szMask == NULL || lpFindFileData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Include the listfile into the MPQ's internal listfile
|
||||
// Note that if the listfile name is NULL, do nothing because the
|
||||
// internal listfile is always included.
|
||||
if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0)
|
||||
nError = SFileAddListFile((HANDLE)ha, szListFile);
|
||||
|
||||
// Allocate the structure for MPQ search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
|
||||
if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Perform the first search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memset(hs, 0, sizeof(TMPQSearch));
|
||||
strcpy(&hs->szSearchMask[0], szMask);
|
||||
hs->dwFlagMask = MPQ_FILE_EXISTS;
|
||||
hs->ha = ha;
|
||||
|
||||
// If the archive is patched archive, we have to create a merge table
|
||||
// to prevent files being repeated
|
||||
if(ha->haPatch != NULL)
|
||||
{
|
||||
hs->dwSearchTableItems = GetSearchTableItems(ha);
|
||||
hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems);
|
||||
hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
|
||||
if(hs->pSearchTable != NULL)
|
||||
memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *));
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform first item searching
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = DoMPQSearch(hs, lpFindFileData);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FreeMPQSearch(hs);
|
||||
SetLastError(nError);
|
||||
}
|
||||
|
||||
// Return the result value
|
||||
return (HANDLE)hs;
|
||||
}
|
||||
|
||||
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check the parameters
|
||||
if(!IsValidSearchHandle(hs))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(lpFindFileData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
nError = DoMPQSearch(hs, lpFindFileData);
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
bool WINAPI SFileFindClose(HANDLE hFind)
|
||||
{
|
||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||
|
||||
// Check the parameters
|
||||
if(!IsValidSearchHandle(hs))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
FreeMPQSearch(hs);
|
||||
return true;
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 12.06.04 1.00 Lad The first version of SListFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
#include <assert.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile entry structure
|
||||
|
||||
#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
|
||||
|
||||
struct TListFileCache
|
||||
{
|
||||
HANDLE hFile; // Stormlib file handle
|
||||
char * szMask; // File mask
|
||||
DWORD dwFileSize; // Total size of the cached file
|
||||
DWORD dwFilePos; // Position of the cache in the file
|
||||
BYTE * pBegin; // The begin of the listfile cache
|
||||
BYTE * pPos;
|
||||
BYTE * pEnd; // The last character in the file cache
|
||||
|
||||
BYTE Buffer[CACHE_BUFFER_SIZE]; // Listfile cache itself
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions (cache)
|
||||
|
||||
static TListFileCache * CreateListFileCache(HANDLE hMpq, const char * szListFile)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
HANDLE hListFile = NULL;
|
||||
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
|
||||
DWORD dwBytesRead = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// If the szListFile is NULL, it means we have to open internal listfile
|
||||
if(szListFile == NULL)
|
||||
{
|
||||
// Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load
|
||||
// the listfile even if there is only non-neutral version of the listfile in the MPQ
|
||||
dwSearchScope = SFILE_OPEN_ANY_LOCALE;
|
||||
szListFile = LISTFILE_NAME;
|
||||
}
|
||||
|
||||
// Open the local/internal listfile
|
||||
if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TMPQFile * hf = (TMPQFile *)hListFile;
|
||||
|
||||
// Remember flags for (listfile)
|
||||
if(hf->pFileEntry != NULL)
|
||||
ha->dwFileFlags1 = hf->pFileEntry->dwFlags;
|
||||
}
|
||||
else
|
||||
nError = GetLastError();
|
||||
|
||||
// Allocate cache for one file block
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1);
|
||||
if(pCache == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the file cache
|
||||
memset(pCache, 0, sizeof(TListFileCache));
|
||||
pCache->dwFileSize = SFileGetFileSize(hListFile, NULL);
|
||||
pCache->hFile = hListFile;
|
||||
|
||||
// Fill the cache
|
||||
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == 0)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Initialize the pointers
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
SetLastError(nError);
|
||||
pCache = NULL;
|
||||
}
|
||||
|
||||
// Return the cache
|
||||
return pCache;
|
||||
}
|
||||
|
||||
// Reloads the cache. Returns number of characters
|
||||
// that has been loaded into the cache.
|
||||
static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||
{
|
||||
DWORD dwBytesToRead;
|
||||
DWORD dwBytesRead = 0;
|
||||
|
||||
// Only do something if the cache is empty
|
||||
if(pCache->pPos >= pCache->pEnd)
|
||||
{
|
||||
// __TryReadBlock:
|
||||
|
||||
// Move the file position forward
|
||||
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
||||
if(pCache->dwFilePos >= pCache->dwFileSize)
|
||||
return 0;
|
||||
|
||||
// Get the number of bytes remaining
|
||||
dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos;
|
||||
if(dwBytesToRead > CACHE_BUFFER_SIZE)
|
||||
dwBytesToRead = CACHE_BUFFER_SIZE;
|
||||
|
||||
// Load the next data chunk to the cache
|
||||
SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN);
|
||||
SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||
|
||||
// If we didn't read anything, it might mean that the block
|
||||
// of the file is not available (in case of partial MPQs).
|
||||
// We stop reading the file at this point, because the rest
|
||||
// of the listfile is unreliable
|
||||
if(dwBytesRead == 0)
|
||||
return 0;
|
||||
|
||||
// Set the buffer pointers
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
}
|
||||
|
||||
return dwBytesRead;
|
||||
}
|
||||
|
||||
static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxChars)
|
||||
{
|
||||
char * szLineBegin = szLine;
|
||||
char * szLineEnd = szLine + nMaxChars - 1;
|
||||
char * szExtraString = NULL;
|
||||
|
||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||
for(;;)
|
||||
{
|
||||
// If we need to reload the cache, do it
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a non-whitespace character, stop
|
||||
if(*pCache->pPos > 0x20)
|
||||
break;
|
||||
|
||||
// Skip the character
|
||||
pCache->pPos++;
|
||||
}
|
||||
|
||||
// Copy the remaining characters
|
||||
while(szLine < szLineEnd)
|
||||
{
|
||||
// If we need to reload the cache, do it now and resume copying
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have found a newline, stop loading
|
||||
if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
|
||||
break;
|
||||
|
||||
// Blizzard listfiles can also contain information about patch:
|
||||
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
|
||||
if(*pCache->pPos == '~')
|
||||
szExtraString = szLine;
|
||||
|
||||
// Copy the character
|
||||
*szLine++ = *pCache->pPos++;
|
||||
}
|
||||
|
||||
// Terminate line with zero
|
||||
*szLine = 0;
|
||||
|
||||
// If there was extra string after the file name, clear it
|
||||
if(szExtraString != NULL)
|
||||
{
|
||||
if(szExtraString[0] == '~' && szExtraString[1] == 'P')
|
||||
{
|
||||
szLine = szExtraString;
|
||||
*szExtraString = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the length of the line
|
||||
return (szLine - szLineBegin);
|
||||
}
|
||||
|
||||
static int CompareFileNodes(const void * p1, const void * p2)
|
||||
{
|
||||
char * szFileName1 = *(char **)p1;
|
||||
char * szFileName2 = *(char **)p2;
|
||||
|
||||
return _stricmp(szFileName1, szFileName2);
|
||||
}
|
||||
|
||||
static int WriteListFileLine(
|
||||
TMPQFile * hf,
|
||||
const char * szLine)
|
||||
{
|
||||
char szNewLine[2] = {0x0D, 0x0A};
|
||||
size_t nLength = strlen(szLine);
|
||||
int nError;
|
||||
|
||||
nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions (listfile nodes)
|
||||
|
||||
// Adds a name into the list of all names. For each locale in the MPQ,
|
||||
// one entry will be created
|
||||
// If the file name is already there, does nothing.
|
||||
int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHash * pFirstHash;
|
||||
TMPQHash * pHash;
|
||||
bool bNameEntryCreated = false;
|
||||
|
||||
// If we have HET table, use that one
|
||||
if(ha->pHetTable != NULL)
|
||||
{
|
||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(pFileEntry, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// If we have hash table, we use it
|
||||
if(bNameEntryCreated == false && ha->pHashTable != NULL)
|
||||
{
|
||||
// Look for the first hash table entry for the file
|
||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||
|
||||
// Go while we found something
|
||||
while(pHash != NULL)
|
||||
{
|
||||
// Is it a valid file table index ?
|
||||
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
}
|
||||
|
||||
// Now find the next language version of the file
|
||||
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_CAN_NOT_COMPLETE;
|
||||
}
|
||||
|
||||
// Saves the whole listfile into the MPQ.
|
||||
int SListFileSaveToMpq(TMPQArchive * ha)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
char * szPrevItem;
|
||||
char ** SortTable = NULL;
|
||||
DWORD dwFileSize = 0;
|
||||
size_t nFileNodes = 0;
|
||||
size_t i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate the table for sorting listfile
|
||||
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
||||
if(SortTable == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Construct the sort table
|
||||
// Note: in MPQs with multiple locale versions of the same file,
|
||||
// this code causes adding multiple listfile entries.
|
||||
// Since those MPQs were last time used in Starcraft,
|
||||
// we leave it as it is.
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
{
|
||||
// Only take existing items
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
|
||||
{
|
||||
// Ignore pseudo-names
|
||||
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
|
||||
{
|
||||
SortTable[nFileNodes++] = pFileEntry->szFileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the table
|
||||
qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
|
||||
|
||||
// Now parse the table of file names again - remove duplicates
|
||||
// and count file size.
|
||||
if(nFileNodes != 0)
|
||||
{
|
||||
// Count the 0-th item
|
||||
dwFileSize += (DWORD)strlen(SortTable[0]) + 2;
|
||||
szPrevItem = SortTable[0];
|
||||
|
||||
// Count all next items
|
||||
for(i = 1; i < nFileNodes; i++)
|
||||
{
|
||||
// If the item is the same like the last one, skip it
|
||||
if(_stricmp(SortTable[i], szPrevItem))
|
||||
{
|
||||
dwFileSize += (DWORD)strlen(SortTable[i]) + 2;
|
||||
szPrevItem = SortTable[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the flags for (listfile)
|
||||
if(ha->dwFileFlags1 == 0)
|
||||
ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize);
|
||||
|
||||
// Create the listfile in the MPQ
|
||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
||||
0,
|
||||
dwFileSize,
|
||||
LANG_NEUTRAL,
|
||||
ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
|
||||
&hf);
|
||||
// Add all file names
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Each name is followed by newline ("\x0D\x0A")
|
||||
szPrevItem = SortTable[0];
|
||||
nError = WriteListFileLine(hf, SortTable[0]);
|
||||
|
||||
// Count all next items
|
||||
for(i = 1; i < nFileNodes; i++)
|
||||
{
|
||||
// If the item is the same like the last one, skip it
|
||||
if(_stricmp(SortTable[i], szPrevItem))
|
||||
{
|
||||
WriteListFileLine(hf, SortTable[i]);
|
||||
szPrevItem = SortTable[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create the listfile in the MPQ
|
||||
dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2;
|
||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
||||
0,
|
||||
dwFileSize,
|
||||
LANG_NEUTRAL,
|
||||
MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING,
|
||||
&hf);
|
||||
|
||||
// Just add "(listfile)" there
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
WriteListFileLine(hf, LISTFILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the file in the MPQ
|
||||
if(hf != NULL)
|
||||
{
|
||||
SFileAddFile_Finish(hf);
|
||||
}
|
||||
|
||||
// Free buffers
|
||||
if(nError == ERROR_SUCCESS)
|
||||
ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE;
|
||||
if(SortTable != NULL)
|
||||
STORM_FREE(SortTable);
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File functions
|
||||
|
||||
// Adds a listfile into the MPQ archive.
|
||||
// Note that the function does not remove the
|
||||
int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
char szFileName[MAX_PATH];
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Add the listfile for each MPQ in the patch chain
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Load the listfile to cache
|
||||
pCache = CreateListFileCache(hMpq, szListFile);
|
||||
if(pCache == NULL)
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the node list. Add the node for every locale in the archive
|
||||
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
|
||||
SListFileCreateNodeForAllLocales(ha, szFileName);
|
||||
|
||||
// Also, add three special files to the listfile:
|
||||
// (listfile) itself, (attributes) and (signature)
|
||||
SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME);
|
||||
SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME);
|
||||
SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME);
|
||||
|
||||
// Delete the cache
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
|
||||
// Move to the next archive in the chain
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Passing through the listfile
|
||||
|
||||
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Initialize the structure with zeros
|
||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||
|
||||
// Load the listfile to cache
|
||||
pCache = CreateListFileCache(hMpq, szListFile);
|
||||
if(pCache == NULL)
|
||||
nError = GetLastError();
|
||||
|
||||
// Allocate file mask
|
||||
if(nError == ERROR_SUCCESS && szMask != NULL)
|
||||
{
|
||||
pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1);
|
||||
if(pCache->szMask != NULL)
|
||||
strcpy(pCache->szMask, szMask);
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Perform file search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
// Read the (next) line
|
||||
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||
if(nLength == 0)
|
||||
{
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
|
||||
// If some mask entered, check it
|
||||
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup & exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
pCache = NULL;
|
||||
|
||||
SetLastError(nError);
|
||||
}
|
||||
return (HANDLE)pCache;
|
||||
}
|
||||
|
||||
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||
size_t nLength;
|
||||
bool bResult = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Read the (next) line
|
||||
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||
if(nLength == 0)
|
||||
{
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
|
||||
// If some mask entered, check it
|
||||
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
||||
{
|
||||
bResult = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool WINAPI SListFileFindClose(HANDLE hFind)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||
|
||||
if(pCache != NULL)
|
||||
{
|
||||
if(pCache->hFile != NULL)
|
||||
SFileCloseFile(pCache->hFile);
|
||||
if(pCache->szMask != NULL)
|
||||
STORM_FREE(pCache->szMask);
|
||||
|
||||
STORM_FREE(pCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,470 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */
|
||||
/* */
|
||||
/* Author : Ladislav Zezula */
|
||||
/* E-mail : ladik@zezula.net */
|
||||
/* WWW : www.zezula.net */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Archive functions of Storm.dll */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */
|
||||
/* 19.11.03 1.01 Dan Big endian handling */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Local functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
static bool IsAviFile(void * pvFileBegin)
|
||||
{
|
||||
LPDWORD AviHeader = (DWORD *)pvFileBegin;
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]);
|
||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]);
|
||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]);
|
||||
|
||||
// Test for 'RIFF', 'AVI ' or 'LIST'
|
||||
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
|
||||
}
|
||||
|
||||
static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete)
|
||||
{
|
||||
TFileBitmap * pBitmap;
|
||||
size_t nLength;
|
||||
|
||||
// Calculate the length of the bitmap in blocks and in bytes
|
||||
nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1);
|
||||
nLength = (size_t)(((nLength - 1) / 8) + 1);
|
||||
|
||||
// Allocate the file bitmap
|
||||
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
|
||||
if(pBitmap != NULL)
|
||||
{
|
||||
// Fill the structure
|
||||
pBitmap->StartOffset = ha->MpqPos;
|
||||
pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||
pBitmap->IsComplete = bFileIsComplete ? 1 : 0;
|
||||
pBitmap->BitmapSize = (DWORD)nLength;
|
||||
pBitmap->BlockSize = pMpqBitmap->dwBlockSize;
|
||||
pBitmap->Reserved = 0;
|
||||
|
||||
// Copy the file bitmap
|
||||
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
|
||||
}
|
||||
|
||||
return pBitmap;
|
||||
}
|
||||
|
||||
// This function gets the right positions of the hash table and the block table.
|
||||
static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
ULONGLONG ByteOffset;
|
||||
|
||||
// Check the begin of HET table
|
||||
if(pHeader->HetTablePos64)
|
||||
{
|
||||
ByteOffset = ha->MpqPos + pHeader->HetTablePos64;
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Check the begin of BET table
|
||||
if(pHeader->BetTablePos64)
|
||||
{
|
||||
ByteOffset = ha->MpqPos + pHeader->BetTablePos64;
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Check the begin of hash table
|
||||
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
|
||||
{
|
||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Check the begin of block table
|
||||
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
|
||||
{
|
||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Check the begin of hi-block table
|
||||
if(pHeader->HiBlockTablePos64 != 0)
|
||||
{
|
||||
ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64;
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// All OK.
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Public functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileGetLocale and SFileSetLocale
|
||||
// Set the locale for all newly opened files
|
||||
|
||||
LCID WINAPI SFileGetLocale()
|
||||
{
|
||||
return lcFileLocale;
|
||||
}
|
||||
|
||||
LCID WINAPI SFileSetLocale(LCID lcNewLocale)
|
||||
{
|
||||
lcFileLocale = lcNewLocale;
|
||||
return lcFileLocale;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileOpenArchive
|
||||
//
|
||||
// szFileName - MPQ archive file name to open
|
||||
// dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives
|
||||
// dwFlags - See MPQ_OPEN_XXX in StormLib.h
|
||||
// phMpq - Pointer to store open archive handle
|
||||
|
||||
bool WINAPI SFileOpenArchive(
|
||||
const TCHAR * szMpqName,
|
||||
DWORD dwPriority,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phMpq)
|
||||
{
|
||||
TFileStream * pStream = NULL; // Open file stream
|
||||
TMPQArchive * ha = NULL; // Archive handle
|
||||
ULONGLONG FileSize = 0; // Size of the file
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Verify the parameters
|
||||
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// One time initialization of MPQ cryptography
|
||||
InitializeMpqCryptography();
|
||||
dwPriority = dwPriority;
|
||||
|
||||
// Open the MPQ archive file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the stream
|
||||
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
|
||||
if(pStream == NULL)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Allocate the MPQhandle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_GetSize(pStream, FileSize);
|
||||
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Initialize handle structure and allocate structure for MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memset(ha, 0, sizeof(TMPQArchive));
|
||||
ha->pStream = pStream;
|
||||
pStream = NULL;
|
||||
|
||||
// Remember if the archive is open for write
|
||||
if(FileStream_IsReadOnly(ha->pStream))
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
|
||||
// Also remember if we shall check sector CRCs when reading file
|
||||
if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
|
||||
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
|
||||
}
|
||||
|
||||
// Find the offset of MPQ header within the file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ULONGLONG SearchPos = 0;
|
||||
DWORD dwHeaderID;
|
||||
|
||||
while(SearchPos < FileSize)
|
||||
{
|
||||
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
|
||||
|
||||
// Cut the bytes available, if needed
|
||||
if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
|
||||
dwBytesAvailable = (DWORD)(FileSize - SearchPos);
|
||||
|
||||
// Read the eventual MPQ header
|
||||
if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// There are AVI files from Warcraft III with 'MPQ' extension.
|
||||
if(SearchPos == 0 && IsAviFile(ha->HeaderData))
|
||||
{
|
||||
nError = ERROR_AVI_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is the MPQ user data signature, process it
|
||||
dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData);
|
||||
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL)
|
||||
{
|
||||
// Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0
|
||||
if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
|
||||
{
|
||||
// Fill the user data header
|
||||
ha->pUserData = &ha->UserData;
|
||||
memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
|
||||
BSWAP_TMPQUSERDATA(ha->pUserData);
|
||||
|
||||
// Remember the position of the user data and continue search
|
||||
ha->UserDataPos = SearchPos;
|
||||
SearchPos += ha->pUserData->dwHeaderOffs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// There must be MPQ header signature
|
||||
if(dwHeaderID == ID_MPQ)
|
||||
{
|
||||
// Save the position where the MPQ header has been found
|
||||
if(ha->pUserData == NULL)
|
||||
ha->UserDataPos = SearchPos;
|
||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
||||
ha->MpqPos = SearchPos;
|
||||
|
||||
// Now convert the header to version 4
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to the next possible offset
|
||||
SearchPos += 0x200;
|
||||
}
|
||||
|
||||
// If we haven't found MPQ header in the file, it's an error
|
||||
if(ha->pHeader == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Fix table positions according to format
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Dump the header
|
||||
// DumpMpqHeader(ha->pHeader);
|
||||
|
||||
// W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
|
||||
// and probably ignores the MPQ format version as well. The trick is to
|
||||
// fake MPQ format 2, with an improper hi-word position of hash table and block table
|
||||
// We can overcome such protectors by forcing opening the archive as MPQ v 1.0
|
||||
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
||||
{
|
||||
ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
|
||||
ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
ha->pUserData = NULL;
|
||||
}
|
||||
|
||||
// Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
|
||||
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
|
||||
// Set the size of file sector
|
||||
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
|
||||
|
||||
// Verify if any of the tables doesn't start beyond the end of the file
|
||||
nError = VerifyMpqTablePositions(ha, FileSize);
|
||||
}
|
||||
|
||||
// Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete
|
||||
if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4)
|
||||
{
|
||||
TFileBitmap * pBitmap;
|
||||
bool bFileIsComplete = true;
|
||||
|
||||
LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete);
|
||||
if(ha->pBitmap != NULL && bFileIsComplete == false)
|
||||
{
|
||||
// Convert the MPQ bitmap to the file bitmap
|
||||
pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete);
|
||||
|
||||
// Set the data bitmap into the file stream for additional checks
|
||||
FileStream_SetBitmap(ha->pStream, pBitmap);
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the hash table. Ignore the result, as hash table is no longer required
|
||||
// Read HET table. Ignore the result, as HET table is no longer required
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = LoadAnyHashTable(ha);
|
||||
}
|
||||
|
||||
// Now, build the file table. It will be built by combining
|
||||
// the block table, BET table, hi-block table, (attributes) and (listfile).
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = BuildFileTable(ha, FileSize);
|
||||
}
|
||||
|
||||
// Verify the file table, if no kind of protection was detected
|
||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
|
||||
TFileEntry * pFileEntry = ha->pFileTable;
|
||||
// ULONGLONG ArchiveSize = 0;
|
||||
ULONGLONG RawFilePos;
|
||||
|
||||
// Parse all file entries
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
{
|
||||
// If that file entry is valid, check the file position
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||
{
|
||||
// Get the 64-bit file position,
|
||||
// relative to the begin of the file
|
||||
RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;
|
||||
|
||||
// Begin of the file must be within range
|
||||
if(RawFilePos > FileSize)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// End of the file must be within range
|
||||
RawFilePos += pFileEntry->dwCmpSize;
|
||||
if(RawFilePos > FileSize)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Also, we remember end of the file
|
||||
// if(RawFilePos > ArchiveSize)
|
||||
// ArchiveSize = RawFilePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the internal listfile and include it to the file table
|
||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
|
||||
{
|
||||
// Ignore result of the operation. (listfile) is optional.
|
||||
SFileAddListFile((HANDLE)ha, NULL);
|
||||
}
|
||||
|
||||
// Load the "(attributes)" file and merge it to the file table
|
||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
|
||||
{
|
||||
// Ignore result of the operation. (attributes) is optional.
|
||||
SAttrLoadAttributes(ha);
|
||||
}
|
||||
|
||||
// Cleanup and exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
FreeMPQArchive(ha);
|
||||
SetLastError(nError);
|
||||
ha = NULL;
|
||||
}
|
||||
|
||||
*phMpq = ha;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileGetArchiveBitmap
|
||||
|
||||
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// bool SFileFlushArchive(HANDLE hMpq)
|
||||
//
|
||||
// Saves all dirty data into MPQ archive.
|
||||
// Has similar effect like SFileCloseArchive, but the archive is not closed.
|
||||
// Use on clients who keep MPQ archive open even for write operations,
|
||||
// and terminating without calling SFileCloseArchive might corrupt the archive.
|
||||
//
|
||||
|
||||
bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
int nResultError = ERROR_SUCCESS;
|
||||
int nError;
|
||||
|
||||
// Do nothing if 'hMpq' is bad parameter
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the (listfile) has been invalidated, save it
|
||||
if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)
|
||||
{
|
||||
nError = SListFileSaveToMpq(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// If the (attributes) has been invalidated, save it
|
||||
if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)
|
||||
{
|
||||
nError = SAttrFileSaveToMpq(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// Save HET table, BET table, hash table, block table, hi-block table
|
||||
if(ha->dwFlags & MPQ_FLAG_CHANGED)
|
||||
{
|
||||
nError = SaveMPQTables(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// Return the error
|
||||
if(nResultError != ERROR_SUCCESS)
|
||||
SetLastError(nResultError);
|
||||
return (nResultError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// bool SFileCloseArchive(HANDLE hMpq);
|
||||
//
|
||||
|
||||
bool WINAPI SFileCloseArchive(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
bool bResult;
|
||||
|
||||
// Flush all unsaved data to the storage
|
||||
bResult = SFileFlushArchive(hMpq);
|
||||
|
||||
// Free all memory used by MPQ archive
|
||||
FreeMPQArchive(ha);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
@@ -1,469 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description : */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Local functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
TMPQFile * hf = NULL;
|
||||
|
||||
// We have to convert the local file name to UNICODE, if needed
|
||||
#ifdef _UNICODE
|
||||
TCHAR szFileNameT[MAX_PATH];
|
||||
int i;
|
||||
|
||||
for(i = 0; szFileName[i] != 0; i++)
|
||||
szFileNameT[i] = szFileName[i];
|
||||
szFileNameT[i] = 0;
|
||||
pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
||||
|
||||
#else
|
||||
pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
||||
#endif
|
||||
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Allocate and initialize file handle
|
||||
hf = CreateMpqFile(NULL);
|
||||
if(hf != NULL)
|
||||
{
|
||||
hf->pStream = pStream;
|
||||
*phFile = hf;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
}
|
||||
}
|
||||
*phFile = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TMPQFile * hfPatch; // Pointer to patch file
|
||||
TMPQFile * hfBase = NULL; // Pointer to base open file
|
||||
TMPQFile * hfLast = NULL; // The highest file in the chain that is not patch file
|
||||
TMPQFile * hf = NULL;
|
||||
HANDLE hPatchFile;
|
||||
char szPatchFileName[MAX_PATH];
|
||||
|
||||
// Keep this flag here for future updates
|
||||
dwReserved = dwReserved;
|
||||
|
||||
// First of all, try to open the original version of the file in any of the patch chain
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Construct the name of the patch file
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase))
|
||||
{
|
||||
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
|
||||
if((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
||||
{
|
||||
hf = hfLast = hfBase;
|
||||
break;
|
||||
}
|
||||
|
||||
SFileCloseFile((HANDLE)hfBase);
|
||||
}
|
||||
|
||||
// Move to the next file in the patch chain
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
// If we couldn't find the file in any of the patches, it doesn't exist
|
||||
if(hf == NULL)
|
||||
{
|
||||
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now keep going in the patch chain and open every patch file that is there
|
||||
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
|
||||
{
|
||||
// Construct patch file name
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile))
|
||||
{
|
||||
// Remember the new version
|
||||
hfPatch = (TMPQFile *)hPatchFile;
|
||||
|
||||
// If we encountered a full replacement of the file,
|
||||
// we have to remember the highest full file
|
||||
if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
||||
hfLast = hfPatch;
|
||||
|
||||
// Set current patch to base file and move on
|
||||
hf->hfPatchFile = hfPatch;
|
||||
hf = hfPatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to free all files that are below the highest unpatched version
|
||||
while(hfBase != hfLast)
|
||||
{
|
||||
TMPQFile * hfNext = hfBase->hfPatchFile;
|
||||
|
||||
// Free the file below
|
||||
hfBase->hfPatchFile = NULL;
|
||||
FreeMPQFile(hfBase);
|
||||
|
||||
// Move the base to the next file
|
||||
hfBase = hfNext;
|
||||
}
|
||||
|
||||
// Give the updated base MPQ
|
||||
if(phFile != NULL)
|
||||
*phFile = (HANDLE)hfBase;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Public functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileEnumLocales enums all locale versions within MPQ.
|
||||
// Functions fills all available language identifiers on a file into the buffer
|
||||
// pointed by plcLocales. There must be enough entries to copy the localed,
|
||||
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
|
||||
|
||||
int WINAPI SFileEnumLocales(
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LCID * plcLocales,
|
||||
LPDWORD pdwMaxLocales,
|
||||
DWORD dwSearchScope)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHash * pFirstHash;
|
||||
TMPQHash * pHash;
|
||||
DWORD dwFileIndex = 0;
|
||||
DWORD dwLocales = 0;
|
||||
|
||||
// Test the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
return ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if(pdwMaxLocales == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Keep compiler happy
|
||||
dwSearchScope = dwSearchScope;
|
||||
|
||||
// Parse hash table entries for all locales
|
||||
if(!IsPseudoFileName(szFileName, &dwFileIndex))
|
||||
{
|
||||
// Calculate the number of locales
|
||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||
while(pHash != NULL)
|
||||
{
|
||||
dwLocales++;
|
||||
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
|
||||
}
|
||||
|
||||
// Test if there is enough space to copy the locales
|
||||
if(*pdwMaxLocales < dwLocales)
|
||||
{
|
||||
*pdwMaxLocales = dwLocales;
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
// Enum the locales
|
||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||
while(pHash != NULL)
|
||||
{
|
||||
*plcLocales++ = pHash->lcLocale;
|
||||
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There must be space for 1 locale
|
||||
if(*pdwMaxLocales < 1)
|
||||
{
|
||||
*pdwMaxLocales = 1;
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
// For nameless access, always return 1 locale
|
||||
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
||||
pHash = ha->pHashTable + pFileEntry->dwHashIndex;
|
||||
*plcLocales = pHash->lcLocale;
|
||||
dwLocales = 1;
|
||||
}
|
||||
|
||||
// Give the caller the total number of found locales
|
||||
*pdwMaxLocales = dwLocales;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileHasFile
|
||||
//
|
||||
// hMpq - Handle of opened MPQ archive
|
||||
// szFileName - Name of file to look for
|
||||
|
||||
bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pFileEntry;
|
||||
DWORD dwFlagsToCheck = MPQ_FILE_EXISTS;
|
||||
DWORD dwFileIndex = 0;
|
||||
char szPatchFileName[MAX_PATH];
|
||||
bool bIsPseudoName;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
if(!IsValidMpqHandle(ha))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Prepare the file opening
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Different processing for pseudo-names
|
||||
bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex);
|
||||
|
||||
// Walk through the MPQ and all patches
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Verify presence of the file
|
||||
pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale)
|
||||
: GetFileEntryByIndex(ha, dwFileIndex);
|
||||
// Verify the file flags
|
||||
if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS)
|
||||
return true;
|
||||
|
||||
// If this is patched archive, go to the patch
|
||||
dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
|
||||
ha = ha->haPatch;
|
||||
|
||||
// Prepare the patched file name
|
||||
if(ha != NULL)
|
||||
{
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcat(szPatchFileName, szFileName);
|
||||
szFileName = szPatchFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
SetLastError(nError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SFileOpenFileEx
|
||||
//
|
||||
// hMpq - Handle of opened MPQ archive
|
||||
// szFileName - Name of file to open
|
||||
// dwSearchScope - Where to search
|
||||
// phFile - Pointer to store opened file handle
|
||||
|
||||
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pFileEntry = NULL;
|
||||
TMPQFile * hf = NULL;
|
||||
DWORD dwFileIndex = 0;
|
||||
bool bOpenByIndex = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Don't accept NULL pointer to file handle
|
||||
if(phFile == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Prepare the file opening
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
switch(dwSearchScope)
|
||||
{
|
||||
case SFILE_OPEN_PATCHED_FILE:
|
||||
|
||||
// We want to open the updated version of the file
|
||||
return OpenPatchedFile(hMpq, szFileName, 0, phFile);
|
||||
|
||||
case SFILE_OPEN_FROM_MPQ:
|
||||
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
{
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
// First of all, check the name as-is
|
||||
if(!IsPseudoFileName(szFileName, &dwFileIndex))
|
||||
{
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpenByIndex = true;
|
||||
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case SFILE_OPEN_ANY_LOCALE:
|
||||
|
||||
// This open option is reserved for opening MPQ internal listfile.
|
||||
// No argument validation. Tries to open file with neutral locale first,
|
||||
// then any other available.
|
||||
dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
|
||||
case SFILE_OPEN_LOCAL_FILE:
|
||||
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
{
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
return OpenLocalFile(szFileName, phFile);
|
||||
|
||||
default:
|
||||
|
||||
// Don't accept any other value
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Quick return if something failed
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(nError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Test if the file was not already deleted.
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS)
|
||||
nError = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Allocate file handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Initialize file handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memset(hf, 0, sizeof(TMPQFile));
|
||||
hf->pFileEntry = pFileEntry;
|
||||
hf->dwMagic = ID_MPQ_FILE;
|
||||
hf->ha = ha;
|
||||
|
||||
hf->MpqFilePos = pFileEntry->ByteOffset;
|
||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
||||
|
||||
// If the MPQ has sector CRC enabled, enable if for the file
|
||||
if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
|
||||
hf->bCheckSectorCRCs = true;
|
||||
|
||||
// If we know the real file name, copy it to the file entry
|
||||
if(bOpenByIndex == false)
|
||||
{
|
||||
// If there is no file name yet, allocate it
|
||||
AllocateFileName(pFileEntry, szFileName);
|
||||
|
||||
// If the file is encrypted, we should detect the file key
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
{
|
||||
hf->dwFileKey = DecryptFileKey(szFileName,
|
||||
pFileEntry->ByteOffset,
|
||||
pFileEntry->dwFileSize,
|
||||
pFileEntry->dwFlags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to auto-detect the file name
|
||||
if(!SFileGetFileName(hf, NULL))
|
||||
nError = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
// If the file is actually a patch file, we have to load the patch file header
|
||||
if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||
{
|
||||
assert(hf->pPatchInfo == NULL);
|
||||
nError = AllocatePatchInfo(hf, true);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(nError);
|
||||
FreeMPQFile(hf);
|
||||
}
|
||||
|
||||
*phFile = hf;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// bool WINAPI SFileCloseFile(HANDLE hFile);
|
||||
|
||||
bool WINAPI SFileCloseFile(HANDLE hFile)
|
||||
{
|
||||
TMPQFile * hf = (TMPQFile *)hFile;
|
||||
|
||||
if(!IsValidFileHandle(hf))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Free the structure
|
||||
FreeMPQFile(hf);
|
||||
return true;
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFilePatchArchives.cpp Copyright (c) Ladislav Zezula 2010 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 18.08.10 1.00 Lad The first version of SFilePatchArchives.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
typedef struct _BLIZZARD_BSDIFF40_FILE
|
||||
{
|
||||
ULONGLONG Signature;
|
||||
ULONGLONG CtrlBlockSize;
|
||||
ULONGLONG DataBlockSize;
|
||||
ULONGLONG NewFileSize;
|
||||
} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool GetDefaultPatchPrefix(
|
||||
const TCHAR * szBaseMpqName,
|
||||
char * szBuffer)
|
||||
{
|
||||
const TCHAR * szExtension;
|
||||
const TCHAR * szDash;
|
||||
|
||||
// Ensure that both names are plain names
|
||||
szBaseMpqName = GetPlainFileNameT(szBaseMpqName);
|
||||
|
||||
// Patch prefix is for the Cataclysm MPQs, whose names
|
||||
// are like "locale-enGB.MPQ" or "speech-enGB.MPQ"
|
||||
szExtension = _tcsrchr(szBaseMpqName, _T('.'));
|
||||
szDash = _tcsrchr(szBaseMpqName, _T('-'));
|
||||
strcpy(szBuffer, "Base");
|
||||
|
||||
// If the length of the prefix doesn't match, use default one
|
||||
if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5)
|
||||
{
|
||||
// Copy the prefix
|
||||
szBuffer[0] = (char)szDash[1];
|
||||
szBuffer[1] = (char)szDash[2];
|
||||
szBuffer[2] = (char)szDash[3];
|
||||
szBuffer[3] = (char)szDash[4];
|
||||
szBuffer[4] = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed)
|
||||
{
|
||||
LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed;
|
||||
LPBYTE pbCompressedEnd = pbCompressed + cbCompressed;
|
||||
BYTE RepeatCount;
|
||||
BYTE OneByte;
|
||||
|
||||
// Cut the initial DWORD from the compressed chunk
|
||||
pbCompressed += sizeof(DWORD);
|
||||
cbCompressed -= sizeof(DWORD);
|
||||
|
||||
// Pre-fill decompressed buffer with zeros
|
||||
memset(pbDecompressed, 0, cbDecompressed);
|
||||
|
||||
// Unpack
|
||||
while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd)
|
||||
{
|
||||
OneByte = *pbCompressed++;
|
||||
|
||||
// Is it a repetition byte ?
|
||||
if(OneByte & 0x80)
|
||||
{
|
||||
RepeatCount = (OneByte & 0x7F) + 1;
|
||||
for(BYTE i = 0; i < RepeatCount; i++)
|
||||
{
|
||||
if(pbDecompressed == pbDecompressedEnd || pbCompressed == pbCompressedEnd)
|
||||
break;
|
||||
|
||||
*pbDecompressed++ = *pbCompressed++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pbDecompressed += (OneByte + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||
{
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate space for patch header and compressed data
|
||||
hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData);
|
||||
if(hf->pPatchHeader == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Load the patch data and decide if they are compressed or not
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
LPBYTE pbPatchFile = (LPBYTE)hf->pPatchHeader;
|
||||
|
||||
// Copy the patch header itself
|
||||
memcpy(pbPatchFile, pPatchHeader, sizeof(TPatchHeader));
|
||||
pbPatchFile += sizeof(TPatchHeader);
|
||||
|
||||
// Load the rest of the patch
|
||||
if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader)))
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||
{
|
||||
LPBYTE pbDecompressed = NULL;
|
||||
LPBYTE pbCompressed = NULL;
|
||||
DWORD cbDecompressed = 0;
|
||||
DWORD cbCompressed = 0;
|
||||
DWORD dwBytesRead = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate space for compressed data
|
||||
cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
||||
pbCompressed = STORM_ALLOC(BYTE, cbCompressed);
|
||||
if(pbCompressed == NULL)
|
||||
nError = ERROR_SUCCESS;
|
||||
|
||||
// Read the compressed patch data
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Load the rest of the header
|
||||
SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead);
|
||||
if(dwBytesRead != cbCompressed)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Get the uncompressed size of the patch
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader);
|
||||
hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData);
|
||||
if(hf->pPatchHeader == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Now decompress the patch data
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Copy the patch header
|
||||
memcpy(hf->pPatchHeader, pPatchHeader, sizeof(TPatchHeader));
|
||||
pbDecompressed = (LPBYTE)hf->pPatchHeader + sizeof(TPatchHeader);
|
||||
|
||||
// Uncompress or copy the patch data
|
||||
if(cbCompressed < cbDecompressed)
|
||||
{
|
||||
Decompress_RLE(pbDecompressed, cbDecompressed, pbCompressed, cbCompressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(cbCompressed == cbDecompressed);
|
||||
memcpy(pbDecompressed, pbCompressed, cbCompressed);
|
||||
}
|
||||
}
|
||||
|
||||
// Free buffers and exit
|
||||
if(pbCompressed != NULL)
|
||||
STORM_FREE(pbCompressed);
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch_COPY(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
{
|
||||
LPBYTE pbNewFileData;
|
||||
DWORD cbNewFileData;
|
||||
|
||||
// Allocate space for new file data
|
||||
cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
||||
pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData);
|
||||
if(pbNewFileData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Copy the patch data as-is
|
||||
memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData);
|
||||
|
||||
// Free the old file data
|
||||
STORM_FREE(hf->pbFileData);
|
||||
|
||||
// Put the new file data there
|
||||
hf->pbFileData = pbNewFileData;
|
||||
hf->cbFileData = cbNewFileData;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch_BSD0(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
{
|
||||
PBLIZZARD_BSDIFF40_FILE pBsdiff;
|
||||
LPDWORD pCtrlBlock;
|
||||
LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader);
|
||||
LPBYTE pDataBlock;
|
||||
LPBYTE pExtraBlock;
|
||||
LPBYTE pbNewData = NULL;
|
||||
LPBYTE pbOldData = (LPBYTE)hf->pbFileData;
|
||||
DWORD dwNewOffset = 0; // Current position to patch
|
||||
DWORD dwOldOffset = 0; // Current source position
|
||||
DWORD dwNewSize; // Patched file size
|
||||
DWORD dwOldSize = hf->cbFileData; // File size before patch
|
||||
|
||||
// Get pointer to the patch header
|
||||
// Format of BSDIFF header corresponds to original BSDIFF, which is:
|
||||
// 0000 8 bytes signature "BSDIFF40"
|
||||
// 0008 8 bytes size of the control block
|
||||
// 0010 8 bytes size of the data block
|
||||
// 0018 8 bytes new size of the patched file
|
||||
pBsdiff = (PBLIZZARD_BSDIFF40_FILE)pbPatchData;
|
||||
pbPatchData += sizeof(BLIZZARD_BSDIFF40_FILE);
|
||||
|
||||
// Get pointer to the 32-bit BSDIFF control block
|
||||
// The control block follows immediately after the BSDIFF header
|
||||
// and consists of three 32-bit integers
|
||||
// 0000 4 bytes Length to copy from the BSDIFF data block the new file
|
||||
// 0004 4 bytes Length to copy from the BSDIFF extra block
|
||||
// 0008 4 bytes Size to increment source file offset
|
||||
pCtrlBlock = (LPDWORD)pbPatchData;
|
||||
pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->CtrlBlockSize);
|
||||
|
||||
// Get the pointer to the data block
|
||||
pDataBlock = (LPBYTE)pbPatchData;
|
||||
pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->DataBlockSize);
|
||||
|
||||
// Get the pointer to the extra block
|
||||
pExtraBlock = (LPBYTE)pbPatchData;
|
||||
dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize);
|
||||
|
||||
// Allocate new buffer
|
||||
pbNewData = STORM_ALLOC(BYTE, dwNewSize);
|
||||
if(pbNewData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Now patch the file
|
||||
while(dwNewOffset < dwNewSize)
|
||||
{
|
||||
DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[0]);
|
||||
DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[1]);
|
||||
DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[2]);
|
||||
DWORD i;
|
||||
|
||||
// Sanity check
|
||||
if((dwNewOffset + dwAddDataLength) > dwNewSize)
|
||||
{
|
||||
STORM_FREE(pbNewData);
|
||||
return ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Read the diff string to the target buffer
|
||||
memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength);
|
||||
pDataBlock += dwAddDataLength;
|
||||
|
||||
// Now combine the patch data with the original file
|
||||
for(i = 0; i < dwAddDataLength; i++)
|
||||
{
|
||||
if(dwOldOffset < dwOldSize)
|
||||
pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset];
|
||||
|
||||
dwNewOffset++;
|
||||
dwOldOffset++;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if((dwNewOffset + dwMovDataLength) > dwNewSize)
|
||||
{
|
||||
STORM_FREE(pbNewData);
|
||||
return ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Copy the data from the extra block in BSDIFF patch
|
||||
memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength);
|
||||
pExtraBlock += dwMovDataLength;
|
||||
dwNewOffset += dwMovDataLength;
|
||||
|
||||
// Move the old offset
|
||||
if(dwOldMoveLength & 0x80000000)
|
||||
dwOldMoveLength = 0x80000000 - dwOldMoveLength;
|
||||
dwOldOffset += dwOldMoveLength;
|
||||
pCtrlBlock += 3;
|
||||
}
|
||||
|
||||
// Free the old file data
|
||||
STORM_FREE(hf->pbFileData);
|
||||
|
||||
// Put the new data to the fil structure
|
||||
hf->pbFileData = pbNewData;
|
||||
hf->cbFileData = dwNewSize;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int LoadMpqPatch(TMPQFile * hf)
|
||||
{
|
||||
TPatchHeader PatchHeader;
|
||||
DWORD dwBytesRead;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Read the patch header
|
||||
SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead);
|
||||
if(dwBytesRead != sizeof(TPatchHeader))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Verify the signatures in the patch header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// BSWAP the entire header, if needed
|
||||
BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6);
|
||||
PatchHeader.dwXFRM = BSWAP_INT32_UNSIGNED(PatchHeader.dwXFRM);
|
||||
PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize);
|
||||
PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType);
|
||||
|
||||
if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Read the patch, depending on patch type
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
switch(PatchHeader.dwPatchType)
|
||||
{
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = LoadMpqPatch_COPY(hf, &PatchHeader);
|
||||
break;
|
||||
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = LoadMpqPatch_BSD0(hf, &PatchHeader);
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
{
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Verify the original file before patching
|
||||
if(pPatchHeader->dwSizeBeforePatch != 0)
|
||||
{
|
||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
switch(pPatchHeader->dwPatchType)
|
||||
{
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = ApplyMpqPatch_COPY(hf, pPatchHeader);
|
||||
break;
|
||||
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = ApplyMpqPatch_BSD0(hf, pPatchHeader);
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify MD5 after patch
|
||||
if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0)
|
||||
{
|
||||
// Verify the patched file
|
||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions (StormLib internals)
|
||||
|
||||
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
|
||||
{
|
||||
TPatchHeader * pPatchHeader = (TPatchHeader *)pvData;
|
||||
BLIZZARD_BSDIFF40_FILE DiffFile;
|
||||
DWORD dwPatchType;
|
||||
|
||||
if(cbData >= sizeof(TPatchHeader) + sizeof(BLIZZARD_BSDIFF40_FILE))
|
||||
{
|
||||
dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType);
|
||||
if(dwPatchType == 0x30445342)
|
||||
{
|
||||
// Give the caller the patch file size
|
||||
if(pdwPatchedFileSize != NULL)
|
||||
{
|
||||
Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE));
|
||||
DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize);
|
||||
*pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int PatchFileData(TMPQFile * hf)
|
||||
{
|
||||
TMPQFile * hfBase = hf;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Move to the first patch
|
||||
hf = hf->hfPatchFile;
|
||||
|
||||
// Now go through all patches and patch the original data
|
||||
while(hf != NULL)
|
||||
{
|
||||
// This must be true
|
||||
assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE);
|
||||
|
||||
// Make sure that the patch data is loaded
|
||||
nError = LoadMpqPatch(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Apply the patch
|
||||
nError = ApplyMpqPatch(hfBase, hf->pPatchHeader);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Move to the next patch
|
||||
hf = hf->hfPatchFile;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
//
|
||||
// Patch prefix is the path subdirectory where the patched files are within MPQ.
|
||||
//
|
||||
// Example 1:
|
||||
// Main MPQ: locale-enGB.MPQ
|
||||
// Patch MPQ: wow-update-12694.MPQ
|
||||
// File in main MPQ: DBFilesClient\Achievement.dbc
|
||||
// File in patch MPQ: enGB\DBFilesClient\Achievement.dbc
|
||||
// Path prefix: enGB
|
||||
//
|
||||
// Example 2:
|
||||
// Main MPQ: expansion1.MPQ
|
||||
// Patch MPQ: wow-update-12694.MPQ
|
||||
// File in main MPQ: DBFilesClient\Achievement.dbc
|
||||
// File in patch MPQ: Base\DBFilesClient\Achievement.dbc
|
||||
// Path prefix: Base
|
||||
//
|
||||
|
||||
bool WINAPI SFileOpenPatchArchive(
|
||||
HANDLE hMpq,
|
||||
const TCHAR * szPatchMpqName,
|
||||
const char * szPatchPathPrefix,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
TMPQArchive * haPatch;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
HANDLE hPatchMpq = NULL;
|
||||
char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Keep compiler happy
|
||||
dwFlags = dwFlags;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szPatchMpqName == NULL || *szPatchMpqName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// If the user didn't give the patch prefix, get default one
|
||||
if(szPatchPathPrefix != NULL)
|
||||
{
|
||||
// Save length of the patch prefix
|
||||
if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// We don't allow adding patches to archives that have been open for write
|
||||
//
|
||||
// Error scenario:
|
||||
//
|
||||
// 1) Open archive for writing
|
||||
// 2) Modify or replace a file
|
||||
// 3) Add patch archive to the opened MPQ
|
||||
// 4) Read patched file
|
||||
// 5) Now what ?
|
||||
//
|
||||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!FileStream_IsReadOnly(ha->pStream))
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Open the archive like it is normal archive
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY, &hPatchMpq))
|
||||
return false;
|
||||
haPatch = (TMPQArchive *)hPatchMpq;
|
||||
|
||||
// Older WoW patches (build 13914) used to have
|
||||
// several language versions in one patch file
|
||||
// Those patches needed to have a path prefix
|
||||
// We can distinguish such patches by not having the (patch_metadata) file
|
||||
if(szPatchPathPrefix == NULL)
|
||||
{
|
||||
if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME))
|
||||
{
|
||||
GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff);
|
||||
szPatchPathPrefix = szPatchPrefixBuff;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the prefix for patch file names.
|
||||
// Make sure that there is backslash after it
|
||||
if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0)
|
||||
{
|
||||
strcpy(haPatch->szPatchPrefix, szPatchPathPrefix);
|
||||
strcat(haPatch->szPatchPrefix, "\\");
|
||||
haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix);
|
||||
}
|
||||
|
||||
// Now add the patch archive to the list of patches to the original MPQ
|
||||
while(ha != NULL)
|
||||
{
|
||||
if(ha->haPatch == NULL)
|
||||
{
|
||||
haPatch->haBase = ha;
|
||||
ha->haPatch = haPatch;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move to the next archive
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
// Should never happen
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
}
|
||||
|
||||
SetLastError(nError);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WINAPI SFileIsPatchedArchive(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
return false;
|
||||
|
||||
return (ha->haPatch != NULL);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,921 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SFileVerify.cpp Copyright (c) Ladislav Zezula 2010 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* MPQ files and MPQ archives verification. */
|
||||
/* */
|
||||
/* The MPQ signature verification has been written by Jean-Francois Roy */
|
||||
/* <bahamut@macstorm.org> and Justin Olbrantz (Quantam). */
|
||||
/* The MPQ public keys have been created by MPQKit, using OpenSSL library. */
|
||||
/* */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 04.05.10 1.00 Lad The first version of SFileVerify.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local defines
|
||||
|
||||
#define SIGNATURE_TYPE_NONE 0
|
||||
#define SIGNATURE_TYPE_WEAK 1
|
||||
#define SIGNATURE_TYPE_STRONG 2
|
||||
|
||||
#define MPQ_DIGEST_UNIT_SIZE 0x10000
|
||||
|
||||
typedef struct _MPQ_SIGNATURE_INFO
|
||||
{
|
||||
ULONGLONG BeginMpqData; // File offset where the hashing starts
|
||||
ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
|
||||
ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
|
||||
ULONGLONG EndMpqData; // File offset where the hashing ends
|
||||
ULONGLONG EndOfFile; // Size of the entire file
|
||||
BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10];
|
||||
DWORD cbSignatureSize; // Length of the signature
|
||||
int nSignatureType; // See SIGNATURE_TYPE_XXX
|
||||
|
||||
} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Known Blizzard public keys
|
||||
// Created by Jean-Francois Roy using OpenSSL
|
||||
|
||||
static const char * szBlizzardWeakPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe"
|
||||
"2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ=="
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szBlizzardStrongPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd"
|
||||
"tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD"
|
||||
"Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp"
|
||||
"kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm"
|
||||
"Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW"
|
||||
"lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk"
|
||||
"dwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWarcraft3MapPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5"
|
||||
"yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3"
|
||||
"iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0"
|
||||
"1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS"
|
||||
"gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b"
|
||||
"heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74"
|
||||
"6QIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWowPatchPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9"
|
||||
"6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa"
|
||||
"5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ"
|
||||
"bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c"
|
||||
"yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y"
|
||||
"UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv"
|
||||
"TwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWowSurveyPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe"
|
||||
"MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c"
|
||||
"63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU"
|
||||
"BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt"
|
||||
"zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a"
|
||||
"vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szStarcraft2MapPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB"
|
||||
"q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq"
|
||||
"2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT"
|
||||
"E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ"
|
||||
"7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0"
|
||||
"31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static void memrev(unsigned char *buf, size_t count)
|
||||
{
|
||||
unsigned char *r;
|
||||
|
||||
for (r = buf + count - 1; buf < r; buf++, r--)
|
||||
{
|
||||
*buf ^= *r;
|
||||
*r ^= *buf;
|
||||
*buf ^= *r;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_valid_md5(void * pvMd5)
|
||||
{
|
||||
LPDWORD Md5 = (LPDWORD)pvMd5;
|
||||
|
||||
return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false;
|
||||
}
|
||||
|
||||
static bool decode_base64_key(const char * szKeyBase64, rsa_key * key)
|
||||
{
|
||||
unsigned char decoded_key[0x200];
|
||||
const char * szBase64Begin;
|
||||
const char * szBase64End;
|
||||
unsigned long decoded_length = sizeof(decoded_key);
|
||||
unsigned long length;
|
||||
|
||||
// Find out the begin of the BASE64 data
|
||||
szBase64Begin = szKeyBase64 + strlen("-----BEGIN PUBLIC KEY-----");
|
||||
szBase64End = szBase64Begin + strlen(szBase64Begin) - strlen("-----END PUBLIC KEY-----");
|
||||
if(szBase64End[0] != '-')
|
||||
return false;
|
||||
|
||||
// decode the base64 string
|
||||
length = (unsigned long)(szBase64End - szBase64Begin);
|
||||
if(base64_decode((unsigned char *)szBase64Begin, length, decoded_key, &decoded_length) != CRYPT_OK)
|
||||
return false;
|
||||
|
||||
// Create RSA key
|
||||
if(rsa_import(decoded_key, decoded_length, key) != CRYPT_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GetPlainAnsiFileName(
|
||||
const TCHAR * szFileName,
|
||||
char * szPlainName)
|
||||
{
|
||||
const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName);
|
||||
|
||||
// Convert the plain name to ANSI
|
||||
while(*szPlainNameT != 0)
|
||||
*szPlainName++ = (char)*szPlainNameT++;
|
||||
*szPlainName = 0;
|
||||
}
|
||||
|
||||
// Calculate begin and end of the MPQ archive
|
||||
static void CalculateArchiveRange(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
ULONGLONG TempPos = 0;
|
||||
char szMapHeader[0x200];
|
||||
|
||||
// Get the MPQ begin
|
||||
pSI->BeginMpqData = ha->MpqPos;
|
||||
|
||||
// Warcraft III maps are signed from the map header to the end
|
||||
if(FileStream_Read(ha->pStream, &TempPos, szMapHeader, sizeof(szMapHeader)))
|
||||
{
|
||||
// Is it a map header ?
|
||||
if(szMapHeader[0] == 'H' && szMapHeader[1] == 'M' && szMapHeader[2] == '3' && szMapHeader[3] == 'W')
|
||||
{
|
||||
// We will have to hash since the map header
|
||||
pSI->BeginMpqData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the MPQ data end. This is stored in our MPQ header,
|
||||
// and it's been already prepared by SFileOpenArchive,
|
||||
pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||
|
||||
// Get the size of the entire file
|
||||
FileStream_GetSize(ha->pStream, pSI->EndOfFile);
|
||||
}
|
||||
|
||||
static bool QueryMpqSignatureInfo(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
ULONGLONG ExtraBytes;
|
||||
TMPQFile * hf;
|
||||
HANDLE hFile;
|
||||
DWORD dwFileSize;
|
||||
|
||||
// Calculate the range of the MPQ
|
||||
CalculateArchiveRange(ha, pSI);
|
||||
|
||||
// If there is "(signature)" file in the MPQ, it has a weak signature
|
||||
if(SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||
{
|
||||
// Get the content of the signature
|
||||
SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize);
|
||||
|
||||
// Verify the size of the signature
|
||||
hf = (TMPQFile *)hFile;
|
||||
|
||||
// We have to exclude the signature file from the digest
|
||||
pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset;
|
||||
pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize;
|
||||
dwFileSize = hf->dwDataSize;
|
||||
|
||||
// Close the file
|
||||
SFileCloseFile(hFile);
|
||||
pSI->nSignatureType = SIGNATURE_TYPE_WEAK;
|
||||
return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false;
|
||||
}
|
||||
|
||||
// If there is extra bytes beyond the end of the archive,
|
||||
// it's the strong signature
|
||||
ExtraBytes = pSI->EndOfFile - pSI->EndMpqData;
|
||||
if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4))
|
||||
{
|
||||
// Read the strong signature
|
||||
if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4)))
|
||||
return false;
|
||||
|
||||
// Check the signature header "NGIS"
|
||||
if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S')
|
||||
return false;
|
||||
|
||||
pSI->nSignatureType = SIGNATURE_TYPE_STRONG;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Succeeded, but no known signature found
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalculateMpqHashMd5(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
LPBYTE pMd5Digest)
|
||||
{
|
||||
hash_state md5_state;
|
||||
ULONGLONG BeginBuffer;
|
||||
ULONGLONG EndBuffer;
|
||||
LPBYTE pbDigestBuffer = NULL;
|
||||
|
||||
// Allocate buffer for creating the MPQ digest.
|
||||
pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE);
|
||||
if(pbDigestBuffer == NULL)
|
||||
return false;
|
||||
|
||||
// Initialize the MD5 hash state
|
||||
md5_init(&md5_state);
|
||||
|
||||
// Set the byte offset of begin of the data
|
||||
BeginBuffer = pSI->BeginMpqData;
|
||||
|
||||
// Create the digest
|
||||
for(;;)
|
||||
{
|
||||
ULONGLONG BytesRemaining;
|
||||
LPBYTE pbSigBegin = NULL;
|
||||
LPBYTE pbSigEnd = NULL;
|
||||
DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE;
|
||||
|
||||
// Check the number of bytes remaining
|
||||
BytesRemaining = pSI->EndMpqData - BeginBuffer;
|
||||
if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE)
|
||||
dwToRead = (DWORD)BytesRemaining;
|
||||
if(dwToRead == 0)
|
||||
break;
|
||||
|
||||
// Read the next chunk
|
||||
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
||||
{
|
||||
STORM_FREE(pbDigestBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the current byte offset
|
||||
EndBuffer = BeginBuffer + dwToRead;
|
||||
|
||||
// Check if the signature is within the loaded digest
|
||||
if(BeginBuffer <= pSI->BeginExclude && pSI->BeginExclude < EndBuffer)
|
||||
pbSigBegin = pbDigestBuffer + (size_t)(pSI->BeginExclude - BeginBuffer);
|
||||
if(BeginBuffer <= pSI->EndExclude && pSI->EndExclude < EndBuffer)
|
||||
pbSigEnd = pbDigestBuffer + (size_t)(pSI->EndExclude - BeginBuffer);
|
||||
|
||||
// Zero the part that belongs to the signature
|
||||
if(pbSigBegin != NULL || pbSigEnd != NULL)
|
||||
{
|
||||
if(pbSigBegin == NULL)
|
||||
pbSigBegin = pbDigestBuffer;
|
||||
if(pbSigEnd == NULL)
|
||||
pbSigEnd = pbDigestBuffer + dwToRead;
|
||||
|
||||
memset(pbSigBegin, 0, (pbSigEnd - pbSigBegin));
|
||||
}
|
||||
|
||||
// Pass the buffer to the hashing function
|
||||
md5_process(&md5_state, pbDigestBuffer, dwToRead);
|
||||
|
||||
// Move pointers
|
||||
BeginBuffer += dwToRead;
|
||||
}
|
||||
|
||||
// Finalize the MD5 hash
|
||||
md5_done(&md5_state, pMd5Digest);
|
||||
STORM_FREE(pbDigestBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AddTailToSha1(
|
||||
hash_state * psha1_state,
|
||||
const char * szTail)
|
||||
{
|
||||
unsigned char szUpperCase[0x200];
|
||||
unsigned long nLength = 0;
|
||||
|
||||
// Convert the tail to uppercase
|
||||
// Note that we don't need to terminate the string with zero
|
||||
while(*szTail != 0)
|
||||
{
|
||||
szUpperCase[nLength++] = (unsigned char)toupper(*szTail++);
|
||||
}
|
||||
|
||||
// Append the tail to the SHA1
|
||||
sha1_process(psha1_state, szUpperCase, nLength);
|
||||
}
|
||||
|
||||
static bool CalculateMpqHashSha1(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
unsigned char * sha1_tail0,
|
||||
unsigned char * sha1_tail1,
|
||||
unsigned char * sha1_tail2)
|
||||
{
|
||||
ULONGLONG BeginBuffer;
|
||||
hash_state sha1_state_temp;
|
||||
hash_state sha1_state;
|
||||
LPBYTE pbDigestBuffer = NULL;
|
||||
char szPlainName[MAX_PATH];
|
||||
|
||||
// Allocate buffer for creating the MPQ digest.
|
||||
pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE);
|
||||
if(pbDigestBuffer == NULL)
|
||||
return false;
|
||||
|
||||
// Initialize SHA1 state structure
|
||||
sha1_init(&sha1_state);
|
||||
|
||||
// Calculate begin of data to be hashed
|
||||
BeginBuffer = pSI->BeginMpqData;
|
||||
|
||||
// Create the digest
|
||||
for(;;)
|
||||
{
|
||||
ULONGLONG BytesRemaining;
|
||||
DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE;
|
||||
|
||||
// Check the number of bytes remaining
|
||||
BytesRemaining = pSI->EndMpqData - BeginBuffer;
|
||||
if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE)
|
||||
dwToRead = (DWORD)BytesRemaining;
|
||||
if(dwToRead == 0)
|
||||
break;
|
||||
|
||||
// Read the next chunk
|
||||
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
||||
{
|
||||
STORM_FREE(pbDigestBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass the buffer to the hashing function
|
||||
sha1_process(&sha1_state, pbDigestBuffer, dwToRead);
|
||||
|
||||
// Move pointers
|
||||
BeginBuffer += dwToRead;
|
||||
}
|
||||
|
||||
// Add all three known tails and generate three hashes
|
||||
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
|
||||
sha1_done(&sha1_state_temp, sha1_tail0);
|
||||
|
||||
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
|
||||
GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName);
|
||||
AddTailToSha1(&sha1_state_temp, szPlainName);
|
||||
sha1_done(&sha1_state_temp, sha1_tail1);
|
||||
|
||||
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
|
||||
AddTailToSha1(&sha1_state_temp, "ARCHIVE");
|
||||
sha1_done(&sha1_state_temp, sha1_tail2);
|
||||
|
||||
// Finalize the MD5 hash
|
||||
STORM_FREE(pbDigestBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int VerifyRawMpqData(
|
||||
TMPQArchive * ha,
|
||||
ULONGLONG ByteOffset,
|
||||
DWORD dwDataSize)
|
||||
{
|
||||
ULONGLONG DataOffset = ha->MpqPos + ByteOffset;
|
||||
LPBYTE pbDataChunk;
|
||||
LPBYTE pbMD5Array1; // Calculated MD5 array
|
||||
LPBYTE pbMD5Array2; // MD5 array loaded from the MPQ
|
||||
DWORD dwBytesInChunk;
|
||||
DWORD dwChunkCount;
|
||||
DWORD dwChunkSize = ha->pHeader->dwRawChunkSize;
|
||||
DWORD dwMD5Size;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Don't verify zero-sized blocks
|
||||
if(dwDataSize == 0)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// Get the number of data chunks to calculate MD5
|
||||
assert(dwChunkSize != 0);
|
||||
dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1;
|
||||
dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE;
|
||||
|
||||
// Allocate space for data chunk and for the MD5 array
|
||||
pbDataChunk = STORM_ALLOC(BYTE, dwChunkSize);
|
||||
if(pbDataChunk == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Allocate space for MD5 array
|
||||
pbMD5Array1 = STORM_ALLOC(BYTE, dwMD5Size);
|
||||
pbMD5Array2 = STORM_ALLOC(BYTE, dwMD5Size);
|
||||
if(pbMD5Array1 == NULL || pbMD5Array2 == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Calculate MD5 of each data chunk
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
LPBYTE pbMD5 = pbMD5Array1;
|
||||
|
||||
for(DWORD i = 0; i < dwChunkCount; i++)
|
||||
{
|
||||
// Get the number of bytes in the chunk
|
||||
dwBytesInChunk = STORMLIB_MIN(dwChunkSize, dwDataSize);
|
||||
|
||||
// Read the data chunk
|
||||
if(!FileStream_Read(ha->pStream, &DataOffset, pbDataChunk, dwBytesInChunk))
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate MD5
|
||||
CalculateDataBlockHash(pbDataChunk, dwBytesInChunk, pbMD5);
|
||||
|
||||
// Move pointers and offsets
|
||||
DataOffset += dwBytesInChunk;
|
||||
dwDataSize -= dwBytesInChunk;
|
||||
pbMD5 += MD5_DIGEST_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the MD5 array
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Read the array of MD5
|
||||
if(!FileStream_Read(ha->pStream, &DataOffset, pbMD5Array2, dwMD5Size))
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Compare the array of MD5
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Compare the MD5
|
||||
if(memcmp(pbMD5Array1, pbMD5Array2, dwMD5Size))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Free memory and return result
|
||||
if(pbMD5Array2 != NULL)
|
||||
STORM_FREE(pbMD5Array2);
|
||||
if(pbMD5Array1 != NULL)
|
||||
STORM_FREE(pbMD5Array1);
|
||||
if(pbDataChunk != NULL)
|
||||
STORM_FREE(pbDataChunk);
|
||||
return nError;
|
||||
}
|
||||
|
||||
static DWORD VerifyWeakSignature(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE];
|
||||
BYTE Md5Digest[MD5_DIGEST_SIZE];
|
||||
rsa_key key;
|
||||
int hash_idx = find_hash("md5");
|
||||
int result = 0;
|
||||
|
||||
// Calculate hash of the entire archive, skipping the (signature) file
|
||||
if(!CalculateMpqHashMd5(ha, pSI, Md5Digest))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Import the Blizzard key in OpenSSL format
|
||||
if(!decode_base64_key(szBlizzardWeakPublicKey, &key))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Verify the signature
|
||||
memcpy(RevSignature, &pSI->Signature[8], MPQ_WEAK_SIGNATURE_SIZE);
|
||||
memrev(RevSignature, MPQ_WEAK_SIGNATURE_SIZE);
|
||||
rsa_verify_hash_ex(RevSignature, MPQ_WEAK_SIGNATURE_SIZE, Md5Digest, sizeof(Md5Digest), LTC_LTC_PKCS_1_V1_5, hash_idx, 0, &result, &key);
|
||||
rsa_free(&key);
|
||||
|
||||
// Return the result
|
||||
return result ? ERROR_WEAK_SIGNATURE_OK : ERROR_WEAK_SIGNATURE_ERROR;
|
||||
}
|
||||
|
||||
static DWORD VerifyStrongSignatureWithKey(
|
||||
unsigned char * reversed_signature,
|
||||
unsigned char * padded_digest,
|
||||
const char * szPublicKey)
|
||||
{
|
||||
rsa_key key;
|
||||
int result = 0;
|
||||
|
||||
// Import the Blizzard key in OpenSSL format
|
||||
if(!decode_base64_key(szPublicKey, &key))
|
||||
{
|
||||
assert(false);
|
||||
return ERROR_VERIFY_FAILED;
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK)
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Free the key and return result
|
||||
rsa_free(&key);
|
||||
return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR;
|
||||
}
|
||||
|
||||
static DWORD VerifyStrongSignature(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE];
|
||||
unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE];
|
||||
unsigned char Sha1Digest_tail1[SHA1_DIGEST_SIZE];
|
||||
unsigned char Sha1Digest_tail2[SHA1_DIGEST_SIZE];
|
||||
unsigned char padded_digest[MPQ_STRONG_SIGNATURE_SIZE];
|
||||
DWORD dwResult;
|
||||
size_t digest_offset;
|
||||
|
||||
// Calculate SHA1 hash of the archive
|
||||
if(!CalculateMpqHashSha1(ha, pSI, Sha1Digest_tail0, Sha1Digest_tail1, Sha1Digest_tail2))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Prepare the signature for decryption
|
||||
memcpy(reversed_signature, &pSI->Signature[4], MPQ_STRONG_SIGNATURE_SIZE);
|
||||
memrev(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE);
|
||||
|
||||
// Prepare the padded digest for comparison
|
||||
digest_offset = sizeof(padded_digest) - SHA1_DIGEST_SIZE;
|
||||
memset(padded_digest, 0xbb, digest_offset);
|
||||
padded_digest[0] = 0x0b;
|
||||
|
||||
// Try Blizzard Strong public key with no SHA1 tail
|
||||
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
|
||||
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
|
||||
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szBlizzardStrongPublicKey);
|
||||
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
|
||||
return dwResult;
|
||||
|
||||
// Try War 3 map public key with plain file name as SHA1 tail
|
||||
memcpy(padded_digest + digest_offset, Sha1Digest_tail1, SHA1_DIGEST_SIZE);
|
||||
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
|
||||
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWarcraft3MapPublicKey);
|
||||
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
|
||||
return dwResult;
|
||||
|
||||
// Try WoW-TBC public key with "ARCHIVE" as SHA1 tail
|
||||
memcpy(padded_digest + digest_offset, Sha1Digest_tail2, SHA1_DIGEST_SIZE);
|
||||
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
|
||||
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowPatchPublicKey);
|
||||
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
|
||||
return dwResult;
|
||||
|
||||
// Try Survey public key with no SHA1 tail
|
||||
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
|
||||
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
|
||||
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowSurveyPublicKey);
|
||||
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
|
||||
return dwResult;
|
||||
|
||||
// Try Starcraft II public key with no SHA1 tail
|
||||
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
|
||||
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
|
||||
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szStarcraft2MapPublicKey);
|
||||
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
|
||||
return dwResult;
|
||||
|
||||
return ERROR_STRONG_SIGNATURE_ERROR;
|
||||
}
|
||||
|
||||
static DWORD VerifyFile(
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LPDWORD pdwCrc32,
|
||||
char * pMD5,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
hash_state md5_state;
|
||||
unsigned char * pFileMd5;
|
||||
unsigned char md5[MD5_DIGEST_SIZE];
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf;
|
||||
BYTE Buffer[0x1000];
|
||||
HANDLE hFile = NULL;
|
||||
DWORD dwVerifyResult = 0;
|
||||
DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
||||
DWORD dwTotalBytes = 0;
|
||||
DWORD dwBytesRead;
|
||||
DWORD dwCrc32 = 0;
|
||||
|
||||
// Fix the open type for patched archives
|
||||
if(SFileIsPatchedArchive(hMpq))
|
||||
dwSearchScope = SFILE_OPEN_PATCHED_FILE;
|
||||
|
||||
// If we have to verify raw data MD5, do it before file open
|
||||
if(dwFlags & SFILE_VERIFY_RAW_MD5)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Parse the base MPQ and all patches
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Does the archive have support for raw MD5?
|
||||
if(ha->pHeader->dwRawChunkSize != 0)
|
||||
{
|
||||
// The file has raw MD5 if the archive supports it
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5;
|
||||
|
||||
// Find file entry for the file
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// If the file's raw MD5 doesn't match, don't bother with more checks
|
||||
if(VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS)
|
||||
return dwVerifyResult | VERIFY_FILE_RAW_MD5_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next patch
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to open the file
|
||||
if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
|
||||
{
|
||||
// Get the file size
|
||||
hf = (TMPQFile *)hFile;
|
||||
pFileEntry = hf->pFileEntry;
|
||||
dwTotalBytes = SFileGetFileSize(hFile, NULL);
|
||||
|
||||
// Initialize the CRC32 and MD5 contexts
|
||||
md5_init(&md5_state);
|
||||
dwCrc32 = crc32(0, Z_NULL, 0);
|
||||
|
||||
// Also turn on sector checksum verification
|
||||
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
|
||||
hf->bCheckSectorCRCs = true;
|
||||
|
||||
// Go through entire file and update both CRC32 and MD5
|
||||
for(;;)
|
||||
{
|
||||
// Read data from file
|
||||
SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL);
|
||||
if(dwBytesRead == 0)
|
||||
{
|
||||
if(GetLastError() == ERROR_CHECKSUM_ERROR)
|
||||
dwVerifyResult |= VERIFY_FILE_SECTOR_CRC_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update CRC32 value
|
||||
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
||||
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
|
||||
|
||||
// Update MD5 value
|
||||
if(dwFlags & SFILE_VERIFY_FILE_MD5)
|
||||
md5_process(&md5_state, Buffer, dwBytesRead);
|
||||
|
||||
// Decrement the total size
|
||||
dwTotalBytes -= dwBytesRead;
|
||||
}
|
||||
|
||||
// If the file has sector checksums, indicate it in the flags
|
||||
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
|
||||
{
|
||||
if((hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->SectorChksums != NULL && hf->SectorChksums[0] != 0)
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_SECTOR_CRC;
|
||||
}
|
||||
|
||||
// Check if the entire file has been read
|
||||
// No point in checking CRC32 and MD5 if not
|
||||
// Skip checksum checks if the file has patches
|
||||
if(dwTotalBytes == 0)
|
||||
{
|
||||
// Check CRC32 and MD5 only if there is no patches
|
||||
if(hf->hfPatchFile == NULL)
|
||||
{
|
||||
// Check if the CRC32 matches.
|
||||
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
||||
{
|
||||
// Only check the CRC32 if it is valid
|
||||
if(pFileEntry->dwCrc32 != 0)
|
||||
{
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_CHECKSUM;
|
||||
if(dwCrc32 != pFileEntry->dwCrc32)
|
||||
dwVerifyResult |= VERIFY_FILE_CHECKSUM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if MD5 matches
|
||||
if(dwFlags & SFILE_VERIFY_FILE_MD5)
|
||||
{
|
||||
// Patch files have their MD5 saved in the patch info
|
||||
pFileMd5 = (hf->pPatchInfo != NULL) ? hf->pPatchInfo->md5 : pFileEntry->md5;
|
||||
md5_done(&md5_state, md5);
|
||||
|
||||
// Only check the MD5 if it is valid
|
||||
if(is_valid_md5(pFileMd5))
|
||||
{
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
|
||||
if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE))
|
||||
dwVerifyResult |= VERIFY_FILE_MD5_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Patched files are MD5-checked automatically
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwVerifyResult |= VERIFY_READ_ERROR;
|
||||
}
|
||||
|
||||
SFileCloseFile(hFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remember that the file couldn't be open
|
||||
dwVerifyResult |= VERIFY_OPEN_ERROR;
|
||||
}
|
||||
|
||||
// If the caller required CRC32 and/or MD5, give it to him
|
||||
if(pdwCrc32 != NULL)
|
||||
*pdwCrc32 = dwCrc32;
|
||||
if(pMD5 != NULL)
|
||||
memcpy(pMD5, md5, MD5_DIGEST_SIZE);
|
||||
|
||||
return dwVerifyResult;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public (exported) functions
|
||||
|
||||
bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5)
|
||||
{
|
||||
DWORD dwVerifyResult;
|
||||
DWORD dwVerifyFlags = 0;
|
||||
|
||||
if(pdwCrc32 != NULL)
|
||||
dwVerifyFlags |= SFILE_VERIFY_FILE_CRC;
|
||||
if(pMD5 != NULL)
|
||||
dwVerifyFlags |= SFILE_VERIFY_FILE_MD5;
|
||||
|
||||
dwVerifyResult = VerifyFile(hMpq,
|
||||
szFileName,
|
||||
pdwCrc32,
|
||||
pMD5,
|
||||
dwVerifyFlags);
|
||||
|
||||
// If verification failed, return zero
|
||||
if(dwVerifyResult & VERIFY_FILE_ERROR_MASK)
|
||||
{
|
||||
SetLastError(ERROR_FILE_CORRUPT);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags)
|
||||
{
|
||||
return VerifyFile(hMpq,
|
||||
szFileName,
|
||||
NULL,
|
||||
NULL,
|
||||
dwFlags);
|
||||
}
|
||||
|
||||
// Verifies raw data of the archive Only works for MPQs version 4 or newer
|
||||
int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHeader * pHeader;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
pHeader = ha->pHeader;
|
||||
|
||||
// If the archive doesn't have raw data MD5, report it as OK
|
||||
if(pHeader->dwRawChunkSize == 0)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// If we have to verify MPQ header, do it
|
||||
switch(dwWhatToVerify)
|
||||
{
|
||||
case SFILE_VERIFY_MPQ_HEADER:
|
||||
|
||||
// Only if the header is of version 4 or newer
|
||||
if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE))
|
||||
return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_HET_TABLE:
|
||||
|
||||
// Only if we have HET table
|
||||
if(pHeader->HetTablePos64 && pHeader->HetTableSize64)
|
||||
return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_BET_TABLE:
|
||||
|
||||
// Only if we have BET table
|
||||
if(pHeader->BetTablePos64 && pHeader->BetTableSize64)
|
||||
return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_HASH_TABLE:
|
||||
|
||||
// Hash table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_BLOCK_TABLE:
|
||||
|
||||
// Block table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_HIBLOCK_TABLE:
|
||||
|
||||
// It is unknown if the hi-block table is protected my MD5 or not.
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_FILE:
|
||||
|
||||
// Verify parameters
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Get the offset of a file
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
if(pFileEntry == NULL)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
|
||||
}
|
||||
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
// Verifies the archive against the signature
|
||||
DWORD WINAPI SFileVerifyArchive(HANDLE hMpq)
|
||||
{
|
||||
MPQ_SIGNATURE_INFO si;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Get the MPQ signature and signature type
|
||||
memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO));
|
||||
if(!QueryMpqSignatureInfo(ha, &si))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Verify the signature
|
||||
switch(si.nSignatureType)
|
||||
{
|
||||
case SIGNATURE_TYPE_NONE:
|
||||
return ERROR_NO_SIGNATURE;
|
||||
|
||||
case SIGNATURE_TYPE_WEAK:
|
||||
return VerifyWeakSignature(ha, &si);
|
||||
|
||||
case SIGNATURE_TYPE_STRONG:
|
||||
return VerifyStrongSignature(ha, &si);
|
||||
}
|
||||
|
||||
return ERROR_VERIFY_FAILED;
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* SCommon.h Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for encryption/decryption from Storm.dll. Included by */
|
||||
/* SFile*** functions, do not include and do not use this file directly */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 24.03.03 1.00 Lad The first version of SFileCommon.h */
|
||||
/* 12.06.04 1.00 Lad Renamed to SCommon.h */
|
||||
/* 06.09.10 1.00 Lad Renamed to StormCommon.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __STORMCOMMON_H__
|
||||
#define __STORMCOMMON_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compression support
|
||||
|
||||
// Include functions from Pkware Data Compression Library
|
||||
#include "pklib/pklib.h"
|
||||
|
||||
// Include functions from Huffmann compression
|
||||
#include "huffman/huff.h"
|
||||
|
||||
// Include functions from IMA ADPCM compression
|
||||
#include "adpcm/adpcm.h"
|
||||
|
||||
// Include functions from SPARSE compression
|
||||
#include "sparse/sparse.h"
|
||||
|
||||
// Include functions from LZMA compression
|
||||
#include "lzma/C/LzmaEnc.h"
|
||||
#include "lzma/C/LzmaDec.h"
|
||||
|
||||
// Include functions from zlib
|
||||
#ifndef __SYS_ZLIB
|
||||
#include "zlib/zlib.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
// Include functions from bzlib
|
||||
#ifndef __SYS_BZLIB
|
||||
#include "bzip2/bzlib.h"
|
||||
#else
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cryptography support
|
||||
|
||||
// Headers from LibTomCrypt
|
||||
#include "libtomcrypt/src/headers/tomcrypt.h"
|
||||
|
||||
// For HashStringJenkins
|
||||
#include "jenkins/lookup.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// StormLib private defines
|
||||
|
||||
#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE')
|
||||
|
||||
#define MPQ_WEAK_SIGNATURE_SIZE 64
|
||||
#define MPQ_STRONG_SIGNATURE_SIZE 256
|
||||
|
||||
// Prevent problems with CRT "min" and "max" functions,
|
||||
// as they are not defined on all platforms
|
||||
#define STORMLIB_MIN(a, b) ((a < b) ? a : b)
|
||||
#define STORMLIB_MAX(a, b) ((a > b) ? a : b)
|
||||
|
||||
// Macro for building 64-bit file offset from two 32-bit
|
||||
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | lo)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
// - It is not necessary to fill the allocated buffer with zeros
|
||||
// - Memory freeing function doesn't have to test the pointer to NULL.
|
||||
//
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize)
|
||||
{
|
||||
// return new BYTE[nSize];
|
||||
return HeapAlloc(GetProcessHeap(), 0, nSize);
|
||||
}
|
||||
|
||||
__inline void DebugFree(void * ptr)
|
||||
{
|
||||
// delete [] ptr;
|
||||
HeapFree(GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
#define STORM_ALLOC(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type))
|
||||
#define STORM_FREE(ptr) DebugFree(ptr)
|
||||
#else
|
||||
|
||||
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
|
||||
#define STORM_FREE(ptr) free(ptr)
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// StormLib internal global variables
|
||||
|
||||
extern LCID lcFileLocale; // Preferred file locale
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Encryption and decryption functions
|
||||
|
||||
#define MPQ_HASH_TABLE_INDEX 0x000
|
||||
#define MPQ_HASH_NAME_A 0x100
|
||||
#define MPQ_HASH_NAME_B 0x200
|
||||
#define MPQ_HASH_FILE_KEY 0x300
|
||||
|
||||
DWORD HashString(const char * szFileName, DWORD dwHashType);
|
||||
|
||||
void InitializeMpqCryptography();
|
||||
|
||||
DWORD GetHashTableSizeForFileCount(DWORD dwFileCount);
|
||||
|
||||
bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex);
|
||||
ULONGLONG HashStringJenkins(const char * szFileName);
|
||||
|
||||
int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
|
||||
|
||||
DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize);
|
||||
|
||||
void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
|
||||
void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
|
||||
|
||||
DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted);
|
||||
DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize);
|
||||
DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
|
||||
|
||||
bool IsValidMD5(LPBYTE pbMd5);
|
||||
bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
|
||||
void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handle validation functions
|
||||
|
||||
bool IsValidMpqHandle(TMPQArchive * ha);
|
||||
bool IsValidFileHandle(TMPQFile * hf);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hash table and block table manipulation
|
||||
|
||||
TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName);
|
||||
TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash);
|
||||
DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
|
||||
void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos);
|
||||
|
||||
// Functions that loads and verifies MPQ data bitmap
|
||||
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete);
|
||||
|
||||
// Functions that load the HET and BET tables
|
||||
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
|
||||
int LoadAnyHashTable(TMPQArchive * ha);
|
||||
int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize);
|
||||
int SaveMPQTables(TMPQArchive * ha);
|
||||
|
||||
TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty);
|
||||
void FreeHetTable(TMPQHetTable * pHetTable);
|
||||
|
||||
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
|
||||
void FreeBetTable(TMPQBetTable * pBetTable);
|
||||
|
||||
// Functions for finding files in the file table
|
||||
TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName);
|
||||
TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
||||
TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
||||
TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex);
|
||||
|
||||
// Allocates file name in the file entry
|
||||
void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName);
|
||||
|
||||
// Allocates new file entry in the MPQ tables. Reuses existing, if possible
|
||||
TFileEntry * FindFreeFileEntry(TMPQArchive * ha);
|
||||
TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
||||
int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName);
|
||||
void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
int FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
|
||||
// Invalidates entries for (listfile) and (attributes)
|
||||
void InvalidateInternalFiles(TMPQArchive * ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common functions - MPQ File
|
||||
|
||||
TMPQFile * CreateMpqFile(TMPQArchive * ha);
|
||||
int LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
|
||||
int AllocateSectorBuffer(TMPQFile * hf);
|
||||
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
|
||||
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
|
||||
int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile);
|
||||
void CalculateRawSectorOffset(ULONGLONG & RawFilePos, TMPQFile * hf, DWORD dwSectorOffset);
|
||||
int WritePatchInfo(TMPQFile * hf);
|
||||
int WriteSectorOffsets(TMPQFile * hf);
|
||||
int WriteSectorChecksums(TMPQFile * hf);
|
||||
int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize);
|
||||
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
|
||||
void FreeMPQFile(TMPQFile *& hf);
|
||||
|
||||
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
|
||||
int PatchFileData(TMPQFile * hf);
|
||||
|
||||
void FreeMPQArchive(TMPQArchive *& ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
|
||||
bool CheckWildCard(const char * szString, const char * szWildCard);
|
||||
const char * GetPlainFileNameA(const char * szFileName);
|
||||
const TCHAR * GetPlainFileNameT(const TCHAR * szFileName);
|
||||
bool IsInternalMpqFileName(const char * szFileName);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for adding files to the MPQ
|
||||
|
||||
int SFileAddFile_Init(
|
||||
TMPQArchive * ha,
|
||||
const char * szArchivedName,
|
||||
ULONGLONG ft,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
TMPQFile ** phf
|
||||
);
|
||||
|
||||
int SFileAddFile_Write(
|
||||
TMPQFile * hf,
|
||||
const void * pvData,
|
||||
DWORD dwSize,
|
||||
DWORD dwCompression
|
||||
);
|
||||
|
||||
int SFileAddFile_Finish(
|
||||
TMPQFile * hf
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Attributes support
|
||||
|
||||
int SAttrLoadAttributes(TMPQArchive * ha);
|
||||
int SAttrFileSaveToMpq(TMPQArchive * ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile functions
|
||||
|
||||
int SListFileSaveToMpq(TMPQArchive * ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dump data support
|
||||
|
||||
#ifdef __STORMLIB_DUMP_DATA__
|
||||
void DumpMpqHeader(TMPQHeader * pHeader);
|
||||
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable);
|
||||
|
||||
#else
|
||||
#define DumpMpqHeader(h) /* */
|
||||
#define DumpHetAndBetTable(h, b) /* */
|
||||
#endif
|
||||
|
||||
#endif // __STORMCOMMON_H__
|
||||
|
||||
@@ -1,984 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* StormLib.h Copyright (c) Ladislav Zezula 1999-2010 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* StormLib library v 7.02 */
|
||||
/* */
|
||||
/* Author : Ladislav Zezula */
|
||||
/* E-mail : ladik@zezula.net */
|
||||
/* WWW : http://www.zezula.net */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* xx.xx.99 1.00 Lad Created */
|
||||
/* 24.03.03 2.50 Lad Version 2.50 */
|
||||
/* 02.04.03 3.00 Lad Version 3.00 with compression */
|
||||
/* 11.04.03 3.01 Lad Renamed to StormLib.h for compatibility with */
|
||||
/* original headers for Storm.dll */
|
||||
/* 10.05.03 3.02 Lad Added Pkware DCL compression */
|
||||
/* 26.05.03 4.00 Lad Completed all compressions */
|
||||
/* 18.06.03 4.01 Lad Added SFileSetFileLocale */
|
||||
/* Added SFileExtractFile */
|
||||
/* 26.07.03 4.02 Lad Implemented nameless rename and delete */
|
||||
/* 26.07.03 4.03 Lad Added support for protected MPQs */
|
||||
/* 28.08.03 4.10 Lad Fixed bugs that caused StormLib incorrectly work */
|
||||
/* with Diablo I savegames and with files having full */
|
||||
/* hash table */
|
||||
/* 08.12.03 4.11 DCH Fixed bug in reading file sector larger than 0x1000 */
|
||||
/* on certain files. */
|
||||
/* Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING */
|
||||
/* (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/
|
||||
/* 21.12.03 4.50 Lad Completed port for Mac */
|
||||
/* Fixed bug in compacting (if fsize is mul of 0x1000) */
|
||||
/* Fixed bug in SCompCompress */
|
||||
/* 27.05.04 4.51 Lad Changed memory management from new/delete to our */
|
||||
/* own macros */
|
||||
/* 22.06.04 4.60 Lad Optimized search. Support for multiple listfiles. */
|
||||
/* 30.09.04 4.61 Lad Fixed some bugs (Aaargh !!!) */
|
||||
/* Correctly works if HashTableSize > BlockTableSize */
|
||||
/* 29.12.04 4.70 Lad Fixed compatibility problem with MPQs from WoW */
|
||||
/* 14.07.05 5.00 Lad Added the BZLIB compression support */
|
||||
/* Added suport of files stored as single unit */
|
||||
/* 17.04.06 5.01 Lad Converted to MS Visual Studio 8.0 */
|
||||
/* Fixed issue with protected Warcraft 3 protected maps */
|
||||
/* 15.05.06 5.02 Lad Fixed issue with WoW 1.10+ */
|
||||
/* 07.09.06 5.10 Lad Fixed processing files longer than 2GB */
|
||||
/* 22.11.06 6.00 Lad Support for MPQ archives V2 */
|
||||
/* 12.06.07 6.10 Lad Support for (attributes) file */
|
||||
/* 10.09.07 6.12 Lad Support for MPQs protected by corrupting hash table */
|
||||
/* 03.12.07 6.13 Lad Support for MPQs with hash tbl size > block tbl size */
|
||||
/* 07.04.08 6.20 Lad Added SFileFlushArchive */
|
||||
/* 09.04.08 Lad Removed FilePointer variable from MPQ handle */
|
||||
/* structure, as it caused more problems than benefits */
|
||||
/* 12.05.08 6.22 Lad Support for w3xMaster map protector */
|
||||
/* 05.10.08 6.23 Lad Support for protectors who set negative values in */
|
||||
/* the table of file blocks */
|
||||
/* 26.05.09 6.24 Lad Fixed search for multiple lang files with deleted */
|
||||
/* entries */
|
||||
/* 03.09.09 6.25 Lad Fixed decompression bug in huffmann decompression */
|
||||
/* 22.03.10 6.50 Lad New compressions in Starcraft II (LZMA, sparse) */
|
||||
/* Fixed compacting MPQs that contain single unit files */
|
||||
/* 26.04.10 7.00 Lad Major rewrite */
|
||||
/* 08.06.10 7.10 Lad Support for partial MPQs */
|
||||
/* 08.07.10 7.11 Lad Support for MPQs v 3.0 */
|
||||
/* 20.08.10 7.20 Lad Support for opening multiple MPQs in patch mode */
|
||||
/* 20.09.10 8.00 Lad MPQs v 4, HET and BET tables */
|
||||
/* 07.01.11 8.01 Lad Write support for MPQs v 3 and 4 */
|
||||
/* 15.09.11 8.04 Lad Bug fixes, testing for Diablo III MPQs */
|
||||
/* 26.04.12 8.10 Lad Support for data map, added SFileGetArchiveBitmap */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __STORMLIB_H__
|
||||
#define __STORMLIB_H__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
|
||||
#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy'
|
||||
#endif
|
||||
|
||||
#include "StormPort.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Use the apropriate library
|
||||
//
|
||||
// The library type is encoded in the library name as the following
|
||||
// StormLibXYZ.lib
|
||||
//
|
||||
// X - D for Debug version, R for Release version
|
||||
// Y - A for ANSI version, U for Unicode version
|
||||
// Z - S for static-linked CRT library, D for multithreaded DLL CRT library
|
||||
//
|
||||
|
||||
#if 0 && defined(_MSC_VER) && !defined(__STORMLIB_SELF__)
|
||||
|
||||
#ifdef _DEBUG // DEBUG VERSIONS
|
||||
#ifndef _UNICODE
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version
|
||||
#endif
|
||||
#endif
|
||||
#else // RELEASE VERSIONS
|
||||
#ifndef _UNICODE
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
|
||||
#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
|
||||
|
||||
#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file.
|
||||
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key
|
||||
#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match
|
||||
#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file
|
||||
#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing
|
||||
#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ
|
||||
|
||||
// Values for SFileCreateArchive
|
||||
#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size
|
||||
#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs
|
||||
#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size
|
||||
|
||||
#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table
|
||||
#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table
|
||||
|
||||
#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry
|
||||
#define HET_ENTRY_FREE 0x00 // HET hash value for free entry
|
||||
|
||||
#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure
|
||||
|
||||
#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix
|
||||
|
||||
// Values for SFileOpenArchive
|
||||
#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD
|
||||
#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM
|
||||
|
||||
// Values for SFileOpenFile
|
||||
#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive
|
||||
#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive
|
||||
#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use
|
||||
#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file
|
||||
|
||||
// Flags for TMPQArchive::dwFlags
|
||||
#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access
|
||||
#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed
|
||||
#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps)
|
||||
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files
|
||||
#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive
|
||||
#define MPQ_FLAG_INV_LISTFILE 0x00000020 // If set, it means that the (listfile) has been invalidated
|
||||
#define MPQ_FLAG_INV_ATTRIBUTES 0x00000040 // If set, it means that the (attributes) has been invalidated
|
||||
|
||||
// Return value for SFileGetFileSize and SFileSetFilePointer
|
||||
#define SFILE_INVALID_SIZE 0xFFFFFFFF
|
||||
#define SFILE_INVALID_POS 0xFFFFFFFF
|
||||
#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF
|
||||
|
||||
// Flags for SFileAddFile
|
||||
#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library)
|
||||
#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods)
|
||||
#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed
|
||||
#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted
|
||||
#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed
|
||||
#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure
|
||||
#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam)
|
||||
#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists.
|
||||
#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector.
|
||||
// Ignored if file is not compressed or imploded.
|
||||
#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted
|
||||
#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile)
|
||||
|
||||
#define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \
|
||||
MPQ_FILE_COMPRESS | \
|
||||
MPQ_FILE_ENCRYPTED | \
|
||||
MPQ_FILE_FIX_KEY | \
|
||||
MPQ_FILE_PATCH_FILE | \
|
||||
MPQ_FILE_SINGLE_UNIT | \
|
||||
MPQ_FILE_DELETE_MARKER | \
|
||||
MPQ_FILE_SECTOR_CRC | \
|
||||
MPQ_FILE_EXISTS)
|
||||
|
||||
// Compression types for multiple compressions
|
||||
#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only)
|
||||
#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression
|
||||
#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression
|
||||
#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III)
|
||||
#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2)
|
||||
#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono)
|
||||
#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo)
|
||||
#define MPQ_COMPRESSION_LZMA 0x12 // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags.
|
||||
#define MPQ_COMPRESSION_NEXT_SAME 0xFFFFFFFF // Same compression
|
||||
|
||||
// Constants for SFileAddWave
|
||||
#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression
|
||||
#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression
|
||||
#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression
|
||||
|
||||
// Signatures for HET and BET table
|
||||
#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a'
|
||||
#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a'
|
||||
|
||||
// Decryption keys for MPQ tables
|
||||
#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY)
|
||||
#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY)
|
||||
|
||||
// Block map defines
|
||||
#define MPQ_DATA_BITMAP_SIGNATURE 0x33767470 // Signature of the MPQ data bitmap ('ptv3')
|
||||
|
||||
// Constants for SFileGetFileInfo
|
||||
#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header)
|
||||
#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header)
|
||||
#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ
|
||||
#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries
|
||||
#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table
|
||||
#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes)
|
||||
#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *)
|
||||
#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *)
|
||||
#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive
|
||||
#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX
|
||||
#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only
|
||||
//------
|
||||
#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ
|
||||
#define SFILE_INFO_CODENAME1 101 // The first codename of the file
|
||||
#define SFILE_INFO_CODENAME2 102 // The second codename of the file
|
||||
#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ
|
||||
#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table
|
||||
#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table)
|
||||
#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table)
|
||||
#define SFILE_INFO_FLAGS 107 // File flags
|
||||
#define SFILE_INFO_POSITION 108 // File position within archive
|
||||
#define SFILE_INFO_KEY 109 // File decryption key
|
||||
#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size
|
||||
#define SFILE_INFO_FILETIME 111 // TMPQFileTime
|
||||
#define SFILE_INFO_PATCH_CHAIN 112 // Chain of patches
|
||||
|
||||
#define LISTFILE_NAME "(listfile)" // Name of internal listfile
|
||||
#define SIGNATURE_NAME "(signature)" // Name of internal signature
|
||||
#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file
|
||||
#define PATCH_METADATA_NAME "(patch_metadata)"
|
||||
|
||||
#define STORMLIB_VERSION 0x080A // Current version of StormLib (8.10)
|
||||
#define STORMLIB_VERSION_STRING "8.10"
|
||||
|
||||
#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade
|
||||
#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer
|
||||
#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta
|
||||
#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer
|
||||
|
||||
// Flags for MPQ attributes
|
||||
#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file
|
||||
#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file
|
||||
#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file
|
||||
#define MPQ_ATTRIBUTE_PATCH_BIT 0x00000008 // The "(attributes)" contains a patch bit for each file
|
||||
#define MPQ_ATTRIBUTE_ALL 0x0000000F // Summary mask
|
||||
|
||||
#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00
|
||||
|
||||
// Flags for SFileOpenArchive
|
||||
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
|
||||
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
|
||||
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
|
||||
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
|
||||
|
||||
#define STREAM_PROVIDER_LINEAR 0x00000000 // Stream is linear with no offset mapping
|
||||
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
|
||||
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted MPQ
|
||||
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
|
||||
|
||||
#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_MASK 0x0000FF00 // Mask for stream flags
|
||||
#define STREAM_OPTIONS_MASK 0x0000FFFF // Mask for all stream options
|
||||
|
||||
#define MPQ_OPEN_NO_LISTFILE 0x00010000 // Don't load the internal listfile
|
||||
#define MPQ_OPEN_NO_ATTRIBUTES 0x00020000 // Don't open the attributes
|
||||
#define MPQ_OPEN_FORCE_MPQ_V1 0x00040000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
|
||||
#define MPQ_OPEN_CHECK_SECTOR_CRC 0x00080000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
|
||||
|
||||
// Deprecated
|
||||
#define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY
|
||||
#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED
|
||||
|
||||
// Flags for SFileCreateArchive
|
||||
#define MPQ_CREATE_ATTRIBUTES 0x00100000 // Also add the (attributes) file
|
||||
#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB)
|
||||
#define MPQ_CREATE_ARCHIVE_V2 0x01000000 // Creates archive of version 2 (larger than 4 GB)
|
||||
#define MPQ_CREATE_ARCHIVE_V3 0x02000000 // Creates archive of version 3
|
||||
#define MPQ_CREATE_ARCHIVE_V4 0x03000000 // Creates archive of version 4
|
||||
#define MPQ_CREATE_ARCHIVE_VMASK 0x0F000000 // Mask for archive version
|
||||
|
||||
#define FLAGS_TO_FORMAT_SHIFT 24 // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4
|
||||
|
||||
// Flags for SFileVerifyFile
|
||||
#define SFILE_VERIFY_SECTOR_CRC 0x00000001 // Verify sector checksum for the file, if available
|
||||
#define SFILE_VERIFY_FILE_CRC 0x00000002 // Verify file CRC, if available
|
||||
#define SFILE_VERIFY_FILE_MD5 0x00000004 // Verify file MD5, if available
|
||||
#define SFILE_VERIFY_RAW_MD5 0x00000008 // Verify raw file MD5, if available
|
||||
#define SFILE_VERIFY_ALL 0x0000000F // Verify every checksum possible
|
||||
|
||||
// Return values for SFileVerifyFile
|
||||
#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file
|
||||
#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file
|
||||
#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC
|
||||
#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed
|
||||
#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32
|
||||
#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed
|
||||
#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5
|
||||
#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed
|
||||
#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5
|
||||
#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed
|
||||
#define VERIFY_FILE_ERROR_MASK (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR)
|
||||
|
||||
// Flags for SFileVerifyRawData (for MPQs version 4.0 or higher)
|
||||
#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header
|
||||
#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table
|
||||
#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table
|
||||
#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table
|
||||
#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table
|
||||
#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table
|
||||
#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file
|
||||
|
||||
// Return values for SFileVerifyArchive
|
||||
#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ
|
||||
#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory)
|
||||
#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed
|
||||
#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed
|
||||
#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed
|
||||
#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed
|
||||
|
||||
#ifndef MD5_DIGEST_SIZE
|
||||
#define MD5_DIGEST_SIZE 0x10
|
||||
#endif
|
||||
|
||||
#ifndef SHA1_DIGEST_SIZE
|
||||
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
|
||||
#endif
|
||||
|
||||
#ifndef LANG_NEUTRAL
|
||||
#define LANG_NEUTRAL 0x00 // Neutral locale
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callback functions
|
||||
|
||||
// Values for compact callback
|
||||
#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total)
|
||||
#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total)
|
||||
#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used
|
||||
#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total)
|
||||
#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used
|
||||
|
||||
typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall);
|
||||
typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes);
|
||||
|
||||
struct TFileStream;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for bit arrays used for HET and BET tables
|
||||
|
||||
struct TBitArray
|
||||
{
|
||||
void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
|
||||
void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
|
||||
|
||||
DWORD NumberOfBits; // Total number of bits that are available
|
||||
BYTE Elements[1]; // Array of elements (variable length)
|
||||
};
|
||||
|
||||
// Structure for file bitmap. Used by SFileGetArchiveBitmap
|
||||
struct TFileBitmap
|
||||
{
|
||||
ULONGLONG StartOffset; // Starting offset of the file, covered by bitmap
|
||||
ULONGLONG EndOffset; // Ending offset of the file, covered by bitmap
|
||||
DWORD IsComplete; // If nonzero, no blocks are missing
|
||||
DWORD BitmapSize; // Size of the file bitmap (in bytes)
|
||||
DWORD BlockSize; // Size of one block, in bytes
|
||||
DWORD Reserved; // Alignment
|
||||
|
||||
// Followed by file bitmap (variable length), array of BYTEs)
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures related to MPQ format
|
||||
//
|
||||
// Note: All structures in this header file are supposed to remain private
|
||||
// to StormLib. The structures may (and will) change over time, as the MPQ
|
||||
// file format evolves. Programmers directly using these structures need to
|
||||
// be aware of this. And the last, but not least, NEVER do any modifications
|
||||
// to those structures directly, always use SFile* functions.
|
||||
//
|
||||
|
||||
#define MPQ_HEADER_SIZE_V1 0x20
|
||||
#define MPQ_HEADER_SIZE_V2 0x2C
|
||||
#define MPQ_HEADER_SIZE_V3 0x44
|
||||
#define MPQ_HEADER_SIZE_V4 0xD0
|
||||
|
||||
struct TMPQUserData
|
||||
{
|
||||
// The ID_MPQ_USERDATA ('MPQ\x1B') signature
|
||||
DWORD dwID;
|
||||
|
||||
// Maximum size of the user data
|
||||
DWORD cbUserDataSize;
|
||||
|
||||
// Offset of the MPQ header, relative to the begin of this header
|
||||
DWORD dwHeaderOffs;
|
||||
|
||||
// Appears to be size of user data header (Starcraft II maps)
|
||||
DWORD cbUserDataHeader;
|
||||
};
|
||||
|
||||
// MPQ file header
|
||||
//
|
||||
// We have to make sure that the header is packed OK.
|
||||
// Reason: A 64-bit integer at the beginning of 3.0 part,
|
||||
// which is offset 0x2C
|
||||
#pragma pack(push, 1)
|
||||
struct TMPQHeader
|
||||
{
|
||||
// The ID_MPQ ('MPQ\x1A') signature
|
||||
DWORD dwID;
|
||||
|
||||
// Size of the archive header
|
||||
DWORD dwHeaderSize;
|
||||
|
||||
// 32-bit size of MPQ archive
|
||||
// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
|
||||
// is calculated as the size from the beginning of the archive to the end of the hash table,
|
||||
// block table, or hi-block table (whichever is largest).
|
||||
DWORD dwArchiveSize;
|
||||
|
||||
// 0 = Format 1 (up to The Burning Crusade)
|
||||
// 1 = Format 2 (The Burning Crusade and newer)
|
||||
// 2 = Format 3 (WoW - Cataclysm beta or newer)
|
||||
// 3 = Format 4 (WoW - Cataclysm beta or newer)
|
||||
USHORT wFormatVersion;
|
||||
|
||||
// Power of two exponent specifying the number of 512-byte disk sectors in each file sector
|
||||
// in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize.
|
||||
USHORT wSectorSize;
|
||||
|
||||
// Offset to the beginning of the hash table, relative to the beginning of the archive.
|
||||
DWORD dwHashTablePos;
|
||||
|
||||
// Offset to the beginning of the block table, relative to the beginning of the archive.
|
||||
DWORD dwBlockTablePos;
|
||||
|
||||
// Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for
|
||||
// the original MoPaQ format, or less than 2^20 for the Burning Crusade format.
|
||||
DWORD dwHashTableSize;
|
||||
|
||||
// Number of entries in the block table
|
||||
DWORD dwBlockTableSize;
|
||||
|
||||
//-- MPQ HEADER v 2 -------------------------------------------
|
||||
|
||||
// Offset to the beginning of array of 16-bit high parts of file offsets.
|
||||
ULONGLONG HiBlockTablePos64;
|
||||
|
||||
// High 16 bits of the hash table offset for large archives.
|
||||
USHORT wHashTablePosHi;
|
||||
|
||||
// High 16 bits of the block table offset for large archives.
|
||||
USHORT wBlockTablePosHi;
|
||||
|
||||
//-- MPQ HEADER v 3 -------------------------------------------
|
||||
|
||||
// 64-bit version of the archive size
|
||||
ULONGLONG ArchiveSize64;
|
||||
|
||||
// 64-bit position of the BET table
|
||||
ULONGLONG BetTablePos64;
|
||||
|
||||
// 64-bit position of the HET table
|
||||
ULONGLONG HetTablePos64;
|
||||
|
||||
//-- MPQ HEADER v 4 -------------------------------------------
|
||||
|
||||
// Compressed size of the hash table
|
||||
ULONGLONG HashTableSize64;
|
||||
|
||||
// Compressed size of the block table
|
||||
ULONGLONG BlockTableSize64;
|
||||
|
||||
// Compressed size of the hi-block table
|
||||
ULONGLONG HiBlockTableSize64;
|
||||
|
||||
// Compressed size of the HET block
|
||||
ULONGLONG HetTableSize64;
|
||||
|
||||
// Compressed size of the BET block
|
||||
ULONGLONG BetTableSize64;
|
||||
|
||||
// Size of raw data chunk to calculate MD5.
|
||||
// MD5 of each data chunk follows the raw file data.
|
||||
DWORD dwRawChunkSize;
|
||||
|
||||
// MD5 of MPQ tables
|
||||
unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption
|
||||
unsigned char MD5_HashTable[MD5_DIGEST_SIZE]; // MD5 of the hash table before decryption
|
||||
unsigned char MD5_HiBlockTable[MD5_DIGEST_SIZE]; // MD5 of the hi-block table
|
||||
unsigned char MD5_BetTable[MD5_DIGEST_SIZE]; // MD5 of the BET table before decryption
|
||||
unsigned char MD5_HetTable[MD5_DIGEST_SIZE]; // MD5 of the HET table before decryption
|
||||
unsigned char MD5_MpqHeader[MD5_DIGEST_SIZE]; // MD5 of the MPQ header from signature to (including) MD5_HetTable
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
// Hash entry. All files in the archive are searched by their hashes.
|
||||
struct TMPQHash
|
||||
{
|
||||
// The hash of the file path, using method A.
|
||||
DWORD dwName1;
|
||||
|
||||
// The hash of the file path, using method B.
|
||||
DWORD dwName2;
|
||||
|
||||
#ifdef PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
// The language of the file. This is a Windows LANGID data type, and uses the same values.
|
||||
// 0 indicates the default language (American English), or that the file is language-neutral.
|
||||
USHORT lcLocale;
|
||||
|
||||
// The platform the file is used for. 0 indicates the default platform.
|
||||
// No other values have been observed.
|
||||
// Note: wPlatform is actually just BYTE, but since it has never been used, we don't care.
|
||||
USHORT wPlatform;
|
||||
|
||||
#else
|
||||
|
||||
USHORT wPlatform;
|
||||
USHORT lcLocale;
|
||||
|
||||
#endif
|
||||
|
||||
// If the hash table entry is valid, this is the index into the block table of the file.
|
||||
// Otherwise, one of the following two values:
|
||||
// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
|
||||
// Terminates searches for a given file.
|
||||
// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
|
||||
// Does not terminate searches for a given file.
|
||||
DWORD dwBlockIndex;
|
||||
};
|
||||
|
||||
|
||||
// File description block contains informations about the file
|
||||
struct TMPQBlock
|
||||
{
|
||||
// Offset of the beginning of the file, relative to the beginning of the archive.
|
||||
DWORD dwFilePos;
|
||||
|
||||
// Compressed file size
|
||||
DWORD dwCSize;
|
||||
|
||||
// Only valid if the block is a file; otherwise meaningless, and should be 0.
|
||||
// If the file is compressed, this is the size of the uncompressed file data.
|
||||
DWORD dwFSize;
|
||||
|
||||
// Flags for the file. See MPQ_FILE_XXXX constants
|
||||
DWORD dwFlags;
|
||||
};
|
||||
|
||||
// Patch file information, preceding the sector offset table
|
||||
struct TPatchInfo
|
||||
{
|
||||
DWORD dwLength; // Length of patch info header, in bytes
|
||||
DWORD dwFlags; // Flags. 0x80000000 = MD5 (?)
|
||||
DWORD dwDataSize; // Uncompressed size of the patch file
|
||||
BYTE md5[0x10]; // MD5 of the entire patch file after decompression
|
||||
|
||||
// Followed by the sector table (variable length)
|
||||
};
|
||||
|
||||
// Header for PTCH files
|
||||
struct TPatchHeader
|
||||
{
|
||||
//-- PATCH header -----------------------------------
|
||||
DWORD dwSignature; // 'PTCH'
|
||||
DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed)
|
||||
DWORD dwSizeBeforePatch; // Size of the file before patch
|
||||
DWORD dwSizeAfterPatch; // Size of file after patch
|
||||
|
||||
//-- MD5 block --------------------------------------
|
||||
DWORD dwMD5; // 'MD5_'
|
||||
DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself
|
||||
BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file
|
||||
BYTE md5_after_patch[0x10]; // MD5 of the patched file
|
||||
|
||||
//-- XFRM block -------------------------------------
|
||||
DWORD dwXFRM; // 'XFRM'
|
||||
DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data
|
||||
DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY')
|
||||
|
||||
// Followed by the patch data
|
||||
};
|
||||
|
||||
#define SIZE_OF_XFRM_HEADER 0x0C
|
||||
|
||||
// This is the combined file entry for maintaining file list in the MPQ.
|
||||
// This structure is combined from block table, hi-block table,
|
||||
// (attributes) file and from (listfile).
|
||||
struct TFileEntry
|
||||
{
|
||||
ULONGLONG ByteOffset; // Position of the file content in the MPQ, relative to the MPQ header
|
||||
ULONGLONG FileTime; // FileTime from the (attributes) file. 0 if not present.
|
||||
ULONGLONG BetHash; // Lower part of the file name hash. Only used when the MPQ has BET table.
|
||||
DWORD dwHashIndex; // Index to the hash table. Only used when the MPQ has classic hash table
|
||||
DWORD dwHetIndex; // Index to the HET table. Only used when the MPQ has HET table
|
||||
DWORD dwFileSize; // Decompressed size of the file
|
||||
DWORD dwCmpSize; // Compressed size of the file (i.e., size of the file data in the MPQ)
|
||||
DWORD dwFlags; // File flags (from block table)
|
||||
USHORT lcLocale; // Locale ID for the file
|
||||
USHORT wPlatform; // Platform ID for the file
|
||||
DWORD dwCrc32; // CRC32 from (attributes) file. 0 if not present.
|
||||
unsigned char md5[MD5_DIGEST_SIZE]; // File MD5 from the (attributes) file. 0 if not present.
|
||||
char * szFileName; // File name. NULL if not known.
|
||||
};
|
||||
|
||||
// Common header for HET and BET tables
|
||||
struct TMPQExtTable
|
||||
{
|
||||
DWORD dwSignature; // 'HET\x1A' or 'BET\x1A'
|
||||
DWORD dwVersion; // Version. Seems to be always 1
|
||||
DWORD dwDataSize; // Size of the contained table
|
||||
|
||||
// Followed by the table header
|
||||
// Followed by the table data
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// MPQ data bitmap, can be found at (FileSize - sizeof(TMPQBlockMap))
|
||||
//
|
||||
// There is bit map of the entire MPQ before TMPQBitmap. Each 0x4000-byte
|
||||
// block is represented by one bit (including the last, eventually incomplete block).
|
||||
//
|
||||
struct TMPQBitmap
|
||||
{
|
||||
DWORD dwSignature; // 'ptv3' (MPQ_BLOCK_MAP_SIGNATURE)
|
||||
DWORD dwAlways3; // Unknown, seems to always have value of 3
|
||||
DWORD dwBuildNumber; // Game build number for that MPQ
|
||||
DWORD dwMapOffsetLo; // Low 32-bits of the offset of the bit map
|
||||
DWORD dwMapOffsetHi; // High 32-bits of the offset of the bit map
|
||||
DWORD dwBlockSize; // Size of one block (usually 0x4000 bytes)
|
||||
};
|
||||
|
||||
// Structure for parsed HET table
|
||||
struct TMPQHetTable
|
||||
{
|
||||
TBitArray * pBetIndexes; // Bit array of indexes to BET tables
|
||||
LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte
|
||||
ULONGLONG AndMask64; // AND mask used for calculating file name hash
|
||||
ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash
|
||||
|
||||
DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits)
|
||||
DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes
|
||||
DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits)
|
||||
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
|
||||
DWORD dwHashTableSize; // Number of entries in pBetHashes
|
||||
DWORD dwHashBitSize; // Effective number of bits in the hash
|
||||
};
|
||||
|
||||
// Structure for parsed BET table
|
||||
struct TMPQBetTable
|
||||
{
|
||||
TBitArray * pBetHashes; // Array of BET hashes
|
||||
TBitArray * pFileTable; // Bit-based file table
|
||||
LPDWORD pFileFlags; // Array of file flags
|
||||
|
||||
DWORD dwTableEntrySize; // Size of one table entry, in bits
|
||||
DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry
|
||||
DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry
|
||||
DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry
|
||||
DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry
|
||||
DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry
|
||||
DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry
|
||||
DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry
|
||||
DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry
|
||||
DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry
|
||||
DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry
|
||||
DWORD dwBetHashSizeTotal; // Total size of bet hash
|
||||
DWORD dwBetHashSizeExtra; // Extra bits in the bet hash
|
||||
DWORD dwBetHashSize; // Effective size of the bet hash
|
||||
DWORD dwFileCount; // Number of files (usually equal to maximum number of files)
|
||||
DWORD dwFlagCount; // Number of entries in pFileFlags
|
||||
};
|
||||
|
||||
// Archive handle structure
|
||||
struct TMPQArchive
|
||||
{
|
||||
TFileStream * pStream; // Open stream for the MPQ
|
||||
|
||||
ULONGLONG UserDataPos; // Position of user data (relative to the begin of the file)
|
||||
ULONGLONG MpqPos; // MPQ header offset (relative to the begin of the file)
|
||||
|
||||
TMPQArchive * haPatch; // Pointer to patch archive, if any
|
||||
TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any
|
||||
char szPatchPrefix[MPQ_PATCH_PREFIX_LEN]; // Prefix for file names in patch MPQs
|
||||
size_t cchPatchPrefix; // Length of the patch prefix, in characters
|
||||
|
||||
TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file)
|
||||
TMPQHeader * pHeader; // MPQ file header
|
||||
TMPQBitmap * pBitmap; // MPQ bitmap
|
||||
TMPQHash * pHashTable; // Hash table
|
||||
TMPQHetTable * pHetTable; // Het table
|
||||
TFileEntry * pFileTable; // File table
|
||||
|
||||
TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found
|
||||
BYTE HeaderData[MPQ_HEADER_SIZE_V4]; // Storage for MPQ header
|
||||
|
||||
DWORD dwHETBlockSize;
|
||||
DWORD dwBETBlockSize;
|
||||
DWORD dwFileTableSize; // Current size of the file table, e.g. index of the entry past the last occupied one
|
||||
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
|
||||
DWORD dwSectorSize; // Default size of one file sector
|
||||
DWORD dwFileFlags1; // Flags for (listfile)
|
||||
DWORD dwFileFlags2; // Flags for (attributes)
|
||||
DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX
|
||||
DWORD dwFlags; // See MPQ_FLAG_XXXXX
|
||||
};
|
||||
|
||||
// File handle structure
|
||||
struct TMPQFile
|
||||
{
|
||||
TFileStream * pStream; // File stream. Only used on local files
|
||||
TMPQArchive * ha; // Archive handle
|
||||
TFileEntry * pFileEntry; // File entry for the file
|
||||
DWORD dwFileKey; // Decryption key
|
||||
DWORD dwFilePos; // Current file position
|
||||
ULONGLONG RawFilePos; // Offset in MPQ archive (relative to file begin)
|
||||
ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header)
|
||||
DWORD dwMagic; // 'FILE'
|
||||
|
||||
TMPQFile * hfPatchFile; // Pointer to opened patch file
|
||||
TPatchHeader * pPatchHeader; // Patch header. Only used if the file is a patch file
|
||||
LPBYTE pbFileData; // Loaded and patched file data. Only used if the file is a patch file
|
||||
DWORD cbFileData; // Size of loaded patched data
|
||||
|
||||
TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table
|
||||
DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files.
|
||||
DWORD * SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector
|
||||
DWORD dwSectorCount; // Number of sectors in the file
|
||||
DWORD dwPatchedFileSize; // Size of patched file. Used when saving patch file to the MPQ
|
||||
DWORD dwDataSize; // Size of data in the file (on patch files, this differs from file size in block table entry)
|
||||
|
||||
LPBYTE pbFileSector; // Last loaded file sector. For single unit files, entire file content
|
||||
DWORD dwSectorOffs; // File position of currently loaded file sector
|
||||
DWORD dwSectorSize; // Size of the file sector. For single unit files, this is equal to the file size
|
||||
|
||||
unsigned char hctx[HASH_STATE_SIZE];// Hash state for MD5. Used when saving file to MPQ
|
||||
DWORD dwCrc32; // CRC32 value, used when saving file to MPQ
|
||||
|
||||
bool bLoadedSectorCRCs; // If true, we already tried to load sector CRCs
|
||||
bool bCheckSectorCRCs; // If true, then SFileReadFile will check sector CRCs when reading the file
|
||||
bool bIsWriteHandle; // If true, this handle has been created by SFileCreateFile
|
||||
bool bErrorOccured; // If true, then at least one error occured during saving the file to the archive
|
||||
};
|
||||
|
||||
// Structure for SFileFindFirstFile and SFileFindNextFile
|
||||
typedef struct _SFILE_FIND_DATA
|
||||
{
|
||||
char cFileName[MAX_PATH]; // Full name of the found file
|
||||
char * szPlainName; // Plain name of the found file
|
||||
DWORD dwHashIndex; // Hash table index for the file
|
||||
DWORD dwBlockIndex; // Block table index for the file
|
||||
DWORD dwFileSize; // File size in bytes
|
||||
DWORD dwFileFlags; // MPQ file flags
|
||||
DWORD dwCompSize; // Compressed file size
|
||||
DWORD dwFileTimeLo; // Low 32-bits of the file time (0 if not present)
|
||||
DWORD dwFileTimeHi; // High 32-bits of the file time (0 if not present)
|
||||
LCID lcLocale; // Locale version
|
||||
|
||||
} SFILE_FIND_DATA, *PSFILE_FIND_DATA;
|
||||
|
||||
typedef struct _SFILE_CREATE_MPQ
|
||||
{
|
||||
DWORD cbSize; // Size of this structure, in bytes
|
||||
DWORD dwMpqVersion; // Version of the MPQ to be created
|
||||
void *pvUserData; // Reserved, must be NULL
|
||||
DWORD cbUserData; // Reserved, must be 0
|
||||
DWORD dwStreamFlags; // Stream flags for creating the MPQ
|
||||
DWORD dwFileFlags1; // File flags for (listfile). 0 = default
|
||||
DWORD dwFileFlags2; // File flags for (attributes). 0 = default
|
||||
DWORD dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created
|
||||
DWORD dwSectorSize; // Sector size for compressed files
|
||||
DWORD dwRawChunkSize; // Size of raw data chunk
|
||||
DWORD dwMaxFileCount; // File limit for the MPQ
|
||||
|
||||
} SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Stream support - functions
|
||||
|
||||
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
TCHAR * FileStream_GetFileName(TFileStream * pStream);
|
||||
bool FileStream_IsReadOnly(TFileStream * pStream);
|
||||
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);
|
||||
bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite);
|
||||
bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset);
|
||||
bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset);
|
||||
bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize);
|
||||
bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize);
|
||||
bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT);
|
||||
bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream);
|
||||
bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap);
|
||||
bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
|
||||
void FileStream_Close(TFileStream * pStream);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions prototypes for Storm.dll
|
||||
|
||||
// Typedefs for functions exported by Storm.dll
|
||||
typedef LCID (WINAPI * SFILESETLOCALE)(LCID);
|
||||
typedef bool (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *);
|
||||
typedef bool (WINAPI * SFILECLOSEARCHIVE)(HANDLE);
|
||||
typedef bool (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *);
|
||||
typedef bool (WINAPI * SFILECLOSEFILE)(HANDLE);
|
||||
typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD);
|
||||
typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD);
|
||||
typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for manipulation with StormLib global flags
|
||||
|
||||
LCID WINAPI SFileGetLocale();
|
||||
LCID WINAPI SFileSetLocale(LCID lcNewLocale);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for archive manipulation
|
||||
|
||||
bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq);
|
||||
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq);
|
||||
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq);
|
||||
|
||||
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
|
||||
bool WINAPI SFileFlushArchive(HANDLE hMpq);
|
||||
bool WINAPI SFileCloseArchive(HANDLE hMpq);
|
||||
|
||||
// Adds another listfile into MPQ. The currently added listfile(s) remain,
|
||||
// so you can use this API to combining more listfiles.
|
||||
// Note that this function is internally called by SFileFindFirstFile
|
||||
int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile);
|
||||
|
||||
// Archive compacting
|
||||
bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvData);
|
||||
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile = NULL, bool bReserved = 0);
|
||||
|
||||
// Changing the maximum file count
|
||||
DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq);
|
||||
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount);
|
||||
|
||||
// Changing (attributes) file
|
||||
DWORD WINAPI SFileGetAttributes(HANDLE hMpq);
|
||||
bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags);
|
||||
bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for manipulation with patch archives
|
||||
|
||||
bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags);
|
||||
bool WINAPI SFileIsPatchedArchive(HANDLE hMpq);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for file manipulation
|
||||
|
||||
// Reading from MPQ file
|
||||
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile);
|
||||
DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh = NULL);
|
||||
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
|
||||
bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead = NULL, LPOVERLAPPED lpOverlapped = NULL);
|
||||
bool WINAPI SFileCloseFile(HANDLE hFile);
|
||||
|
||||
// Retrieving info about the file
|
||||
bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName);
|
||||
bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName);
|
||||
bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded = NULL);
|
||||
|
||||
// High-level extract function
|
||||
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for file and archive verification
|
||||
|
||||
// Generates file CRC32
|
||||
bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5);
|
||||
|
||||
// Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags).
|
||||
// For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5
|
||||
DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags);
|
||||
|
||||
// Verifies raw data of the archive. Only works for MPQs version 4 or newer
|
||||
int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName);
|
||||
|
||||
// Verifies the signature, if present
|
||||
DWORD WINAPI SFileVerifyArchive(HANDLE hMpq);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for file searching
|
||||
|
||||
HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile);
|
||||
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData);
|
||||
bool WINAPI SFileFindClose(HANDLE hFind);
|
||||
|
||||
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData);
|
||||
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData);
|
||||
bool WINAPI SListFileFindClose(HANDLE hFind);
|
||||
|
||||
// Locale support
|
||||
int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for adding files to the MPQ
|
||||
|
||||
bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile);
|
||||
bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression);
|
||||
bool WINAPI SFileFinishFile(HANDLE hFile);
|
||||
|
||||
bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME);
|
||||
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags);
|
||||
bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality);
|
||||
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ);
|
||||
bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName);
|
||||
bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale);
|
||||
bool WINAPI SFileSetDataCompression(DWORD DataCompression);
|
||||
|
||||
bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvData);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compression and decompression
|
||||
|
||||
int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
|
||||
int WINAPI SCompExplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
|
||||
int WINAPI SCompCompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel);
|
||||
int WINAPI SCompDecompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
|
||||
int WINAPI SCompDecompress2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Non-Windows support for SetLastError/GetLastError
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
|
||||
void SetLastError(int err);
|
||||
int GetLastError();
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions from Storm.dll. They use slightly different names for keeping
|
||||
// possibility to use them together with StormLib (StormXXX instead of SFileXXX)
|
||||
|
||||
#ifdef __LINK_STORM_DLL__
|
||||
#define STORM_ALTERNATE_NAMES // Force storm_dll.h to use alternate fnc names
|
||||
#include "..\storm_dll\storm_dll.h"
|
||||
#endif // __LINK_STORM_DLL__
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __STORMLIB_H__
|
||||
@@ -1,358 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* adpcm.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* This module contains implementation of adpcm decompression method used by */
|
||||
/* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing */
|
||||
/* his sources. */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
|
||||
/* 20.05.03 2.00 Lad Added compression */
|
||||
/* 19.11.03 2.01 Dan Big endian handling */
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "adpcm.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
typedef union _BYTE_AND_WORD_PTR
|
||||
{
|
||||
short * pw;
|
||||
unsigned char * pb;
|
||||
} BYTE_AND_WORD_PTR;
|
||||
|
||||
typedef union _WORD_AND_BYTE_ARRAY
|
||||
{
|
||||
short w;
|
||||
unsigned char b[2];
|
||||
} WORD_AND_BYTE_ARRAY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tables necessary dor decompression
|
||||
|
||||
static long Table1503F120[] =
|
||||
{
|
||||
0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006,
|
||||
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
||||
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
||||
0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008
|
||||
};
|
||||
|
||||
static long step_table[] =
|
||||
{
|
||||
0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E,
|
||||
0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F,
|
||||
0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042,
|
||||
0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F,
|
||||
0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133,
|
||||
0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292,
|
||||
0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583,
|
||||
0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0,
|
||||
0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954,
|
||||
0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B,
|
||||
0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462,
|
||||
0x00007FFF
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CompressWave
|
||||
|
||||
// 1500EF70
|
||||
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
|
||||
// ECX EDX
|
||||
{
|
||||
WORD_AND_BYTE_ARRAY Wcmp;
|
||||
BYTE_AND_WORD_PTR out; // Pointer to the output buffer
|
||||
long SInt32Array1[2];
|
||||
long SInt32Array2[2];
|
||||
long SInt32Array3[2];
|
||||
long nBytesRemains = dwOutLength; // Number of bytes remaining
|
||||
long nWordsRemains; // Number of words remaining
|
||||
// unsigned char * pbSaveOutBuffer; // Copy of output buffer (actually not used)
|
||||
unsigned long dwBitBuff;
|
||||
unsigned long dwStopBit;
|
||||
unsigned long dwBit;
|
||||
unsigned long ebx;
|
||||
unsigned long esi;
|
||||
long nTableValue;
|
||||
long nOneWord;
|
||||
long var_1C;
|
||||
long var_2C;
|
||||
int nLength;
|
||||
int nIndex;
|
||||
int nValue;
|
||||
int i, chnl;
|
||||
|
||||
// If less than 2 bytes remain, don't decompress anything
|
||||
// pbSaveOutBuffer = pbOutBuffer;
|
||||
out.pb = pbOutBuffer;
|
||||
if(nBytesRemains < 2)
|
||||
return 2;
|
||||
|
||||
Wcmp.b[1] = (unsigned char)(nCmpLevel - 1);
|
||||
Wcmp.b[0] = (unsigned char)0;
|
||||
|
||||
*out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w);
|
||||
if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains)
|
||||
return (int)(out.pb - pbOutBuffer + (nChannels * 2));
|
||||
|
||||
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
||||
|
||||
for(i = 0; i < nChannels; i++)
|
||||
{
|
||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||
SInt32Array2[i] = nOneWord;
|
||||
}
|
||||
|
||||
// Weird. But it's there
|
||||
nLength = dwInLength;
|
||||
if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx;
|
||||
nLength++;
|
||||
|
||||
nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer);
|
||||
nLength = (nLength < 0) ? 0 : nLength;
|
||||
|
||||
nIndex = nChannels - 1; // edi
|
||||
nWordsRemains = dwInLength / 2; // eax
|
||||
|
||||
// ebx - nChannels
|
||||
// ecx - pwOutPos
|
||||
for(chnl = nChannels; chnl < nWordsRemains; chnl++)
|
||||
{
|
||||
// 1500F030
|
||||
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
|
||||
return (int)(out.pb - pbOutBuffer + 2);
|
||||
|
||||
// Switch index
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
|
||||
// Load one word from the input stream
|
||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord
|
||||
SInt32Array3[nIndex] = nOneWord;
|
||||
|
||||
// esi - SInt32Array2[nIndex]
|
||||
// eax - nValue
|
||||
nValue = nOneWord - SInt32Array2[nIndex];
|
||||
nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue;
|
||||
|
||||
ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40;
|
||||
|
||||
// esi - SInt32Array2[nIndex]
|
||||
// edx - step_table[SInt32Array2[nIndex]]
|
||||
// edi - (step_table[SInt32Array1[nIndex]] >> nCmpLevel)
|
||||
nTableValue = step_table[SInt32Array1[nIndex]];
|
||||
dwStopBit = (unsigned long)nCmpLevel;
|
||||
|
||||
// edi - nIndex;
|
||||
if(nValue < (nTableValue >> nCmpLevel))
|
||||
{
|
||||
if(SInt32Array1[nIndex] != 0)
|
||||
SInt32Array1[nIndex]--;
|
||||
*out.pb++ = 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(nValue > nTableValue * 2)
|
||||
{
|
||||
if(SInt32Array1[nIndex] >= 0x58 || nLength == 0)
|
||||
break;
|
||||
|
||||
SInt32Array1[nIndex] += 8;
|
||||
if(SInt32Array1[nIndex] > 0x58)
|
||||
SInt32Array1[nIndex] = 0x58;
|
||||
|
||||
nTableValue = step_table[SInt32Array1[nIndex]];
|
||||
*out.pb++ = 0x81;
|
||||
nLength--;
|
||||
}
|
||||
|
||||
var_2C = nTableValue >> Wcmp.b[1];
|
||||
dwBitBuff = 0;
|
||||
|
||||
esi = (1 << (dwStopBit - 2));
|
||||
dwStopBit = (esi <= 0x20) ? esi : 0x20;
|
||||
|
||||
for(var_1C = 0, dwBit = 1; ; dwBit <<= 1)
|
||||
{
|
||||
// esi = var_1C + nTableValue;
|
||||
if((var_1C + nTableValue) <= nValue)
|
||||
{
|
||||
var_1C += nTableValue;
|
||||
dwBitBuff |= dwBit;
|
||||
}
|
||||
if(dwBit == dwStopBit)
|
||||
break;
|
||||
|
||||
nTableValue >>= 1;
|
||||
}
|
||||
|
||||
nValue = SInt32Array2[nIndex];
|
||||
if(ebx != 0)
|
||||
{
|
||||
nValue -= (var_1C + var_2C);
|
||||
if(nValue < -32768)
|
||||
nValue = -32768;
|
||||
}
|
||||
else
|
||||
{
|
||||
nValue += (var_1C + var_2C);
|
||||
if(nValue > 32767)
|
||||
nValue = 32767;
|
||||
}
|
||||
|
||||
SInt32Array2[nIndex] = nValue;
|
||||
*out.pb++ = (unsigned char)(dwBitBuff | ebx);
|
||||
nTableValue = Table1503F120[dwBitBuff & 0x1F];
|
||||
SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue;
|
||||
if(SInt32Array1[nIndex] < 0)
|
||||
SInt32Array1[nIndex] = 0;
|
||||
else if(SInt32Array1[nIndex] > 0x58)
|
||||
SInt32Array1[nIndex] = 0x58;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// DecompressADPCM
|
||||
|
||||
// 1500F230
|
||||
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
|
||||
{
|
||||
BYTE_AND_WORD_PTR out; // Output buffer
|
||||
BYTE_AND_WORD_PTR in;
|
||||
unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength);
|
||||
long SInt32Array1[2];
|
||||
long SInt32Array2[2];
|
||||
long nOneWord;
|
||||
int nIndex;
|
||||
int i;
|
||||
|
||||
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
||||
out.pb = pbOutBuffer;
|
||||
in.pb = pbInBuffer;
|
||||
in.pw++;
|
||||
|
||||
// Fill the Uint32Array2 array by channel values.
|
||||
for(i = 0; i < nChannels; i++)
|
||||
{
|
||||
nOneWord = BSWAP_INT16_SIGNED(*in.pw++);
|
||||
SInt32Array2[i] = nOneWord;
|
||||
if(dwOutLength < 2)
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||
dwOutLength -= sizeof(short);
|
||||
}
|
||||
|
||||
// Get the initial index
|
||||
nIndex = nChannels - 1;
|
||||
|
||||
// Perform the decompression
|
||||
while(in.pb < pbInBufferEnd)
|
||||
{
|
||||
unsigned char nOneByte = *in.pb++;
|
||||
|
||||
// Switch index
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
|
||||
// 1500F2A2: Get one byte from input buffer
|
||||
if(nOneByte & 0x80)
|
||||
{
|
||||
switch(nOneByte & 0x7F)
|
||||
{
|
||||
case 0: // 1500F315
|
||||
if(SInt32Array1[nIndex] != 0)
|
||||
SInt32Array1[nIndex]--;
|
||||
|
||||
if(dwOutLength < 2)
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]);
|
||||
dwOutLength -= sizeof(unsigned short);
|
||||
break;
|
||||
|
||||
case 1: // 1500F2E8
|
||||
SInt32Array1[nIndex] += 8;
|
||||
if(SInt32Array1[nIndex] > 0x58)
|
||||
SInt32Array1[nIndex] = 0x58;
|
||||
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 2: // 1500F41E
|
||||
break;
|
||||
|
||||
default: // 1500F2C4
|
||||
SInt32Array1[nIndex] -= 8;
|
||||
if(SInt32Array1[nIndex] < 0)
|
||||
SInt32Array1[nIndex] = 0;
|
||||
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1500F349
|
||||
long temp1 = step_table[SInt32Array1[nIndex]]; // EDI
|
||||
long temp2 = temp1 >> pbInBuffer[1]; // ESI
|
||||
long temp3 = SInt32Array2[nIndex]; // ECX
|
||||
|
||||
if(nOneByte & 0x01) // EBX = nOneByte
|
||||
temp2 += (temp1 >> 0);
|
||||
|
||||
if(nOneByte & 0x02)
|
||||
temp2 += (temp1 >> 1);
|
||||
|
||||
if(nOneByte & 0x04)
|
||||
temp2 += (temp1 >> 2);
|
||||
|
||||
if(nOneByte & 0x08)
|
||||
temp2 += (temp1 >> 3);
|
||||
|
||||
if(nOneByte & 0x10)
|
||||
temp2 += (temp1 >> 4);
|
||||
|
||||
if(nOneByte & 0x20)
|
||||
temp2 += (temp1 >> 5);
|
||||
|
||||
if(nOneByte & 0x40)
|
||||
{
|
||||
temp3 = temp3 - temp2;
|
||||
if(temp3 <= -32768)
|
||||
temp3 = -32768;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp3 = temp3 + temp2;
|
||||
if(temp3 >= 32767)
|
||||
temp3 = 32767;
|
||||
}
|
||||
|
||||
SInt32Array2[nIndex] = temp3;
|
||||
if(dwOutLength < 2)
|
||||
break;
|
||||
|
||||
// Store the output 16-bit value
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]);
|
||||
dwOutLength -= 2;
|
||||
|
||||
SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F];
|
||||
|
||||
if(SInt32Array1[nIndex] < 0)
|
||||
SInt32Array1[nIndex] = 0;
|
||||
else if(SInt32Array1[nIndex] > 0x58)
|
||||
SInt32Array1[nIndex] = 0x58;
|
||||
}
|
||||
}
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* adpcm.h Copyright (c) Ladislav Zezula 2003 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Header file for adpcm decompress functions */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 31.03.03 1.00 Lad The first version of adpcm.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __ADPCM_H__
|
||||
#define __ADPCM_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
|
||||
#include "../StormPort.h"
|
||||
|
||||
int CompressADPCM (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels);
|
||||
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels);
|
||||
|
||||
#endif // __ADPCM_H__
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,282 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Public header file for the library. ---*/
|
||||
/*--- bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#ifndef _BZLIB_H
|
||||
#define _BZLIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BZ_RUN 0
|
||||
#define BZ_FLUSH 1
|
||||
#define BZ_FINISH 2
|
||||
|
||||
#define BZ_OK 0
|
||||
#define BZ_RUN_OK 1
|
||||
#define BZ_FLUSH_OK 2
|
||||
#define BZ_FINISH_OK 3
|
||||
#define BZ_STREAM_END 4
|
||||
#define BZ_SEQUENCE_ERROR (-1)
|
||||
#define BZ_PARAM_ERROR (-2)
|
||||
#define BZ_MEM_ERROR (-3)
|
||||
#define BZ_DATA_ERROR (-4)
|
||||
#define BZ_DATA_ERROR_MAGIC (-5)
|
||||
#define BZ_IO_ERROR (-6)
|
||||
#define BZ_UNEXPECTED_EOF (-7)
|
||||
#define BZ_OUTBUFF_FULL (-8)
|
||||
#define BZ_CONFIG_ERROR (-9)
|
||||
|
||||
typedef
|
||||
struct {
|
||||
char *next_in;
|
||||
unsigned int avail_in;
|
||||
unsigned int total_in_lo32;
|
||||
unsigned int total_in_hi32;
|
||||
|
||||
char *next_out;
|
||||
unsigned int avail_out;
|
||||
unsigned int total_out_lo32;
|
||||
unsigned int total_out_hi32;
|
||||
|
||||
void *state;
|
||||
|
||||
void *(*bzalloc)(void *,int,int);
|
||||
void (*bzfree)(void *,void *);
|
||||
void *opaque;
|
||||
}
|
||||
bz_stream;
|
||||
|
||||
|
||||
#ifndef BZ_IMPORT
|
||||
#define BZ_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
/* Need a definitition for FILE */
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# ifdef small
|
||||
/* windows.h define small to char */
|
||||
# undef small
|
||||
# endif
|
||||
# ifdef BZ_EXPORT
|
||||
# define BZ_API(func) WINAPI func
|
||||
# define BZ_EXTERN extern
|
||||
# else
|
||||
/* import windows dll dynamically */
|
||||
# define BZ_API(func) (WINAPI * func)
|
||||
# define BZ_EXTERN
|
||||
# endif
|
||||
#else
|
||||
# define BZ_API(func) func
|
||||
# define BZ_EXTERN extern
|
||||
#endif
|
||||
|
||||
|
||||
/*-- Core (low-level) library functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
|
||||
bz_stream* strm,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompress) (
|
||||
bz_stream* strm,
|
||||
int action
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
|
||||
bz_stream *strm,
|
||||
int verbosity,
|
||||
int small
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
|
||||
bz_stream *strm
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*-- High(er) level library functions --*/
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
#define BZ_MAX_UNUSED 5000
|
||||
|
||||
typedef void BZFILE;
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int verbosity,
|
||||
int small,
|
||||
void* unused,
|
||||
int nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void** unused,
|
||||
int* nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzRead) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWrite) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in,
|
||||
unsigned int* nbytes_out
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in_lo32,
|
||||
unsigned int* nbytes_in_hi32,
|
||||
unsigned int* nbytes_out_lo32,
|
||||
unsigned int* nbytes_out_hi32
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/*-- Utility functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int small,
|
||||
int verbosity
|
||||
);
|
||||
|
||||
|
||||
/*--
|
||||
Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
|
||||
to support better zlib compatibility.
|
||||
This code is not _officially_ part of libbzip2 (yet);
|
||||
I haven't tested it, documented it, or considered the
|
||||
threading-safeness of it.
|
||||
If this code breaks, please contact both Yoshioka and me.
|
||||
--*/
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
|
||||
void
|
||||
);
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
|
||||
const char *path,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
|
||||
int fd,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzread) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzwrite) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzflush) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzclose) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
|
||||
BZFILE *b,
|
||||
int *errnum
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,509 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Private header file for the library. ---*/
|
||||
/*--- bzlib_private.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#ifndef _BZLIB_PRIVATE_H
|
||||
#define _BZLIB_PRIVATE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "bzlib.h"
|
||||
|
||||
|
||||
|
||||
/*-- General stuff. --*/
|
||||
|
||||
#define BZ_VERSION "1.0.5, 10-Dec-2007"
|
||||
|
||||
typedef char Char;
|
||||
typedef unsigned char Bool;
|
||||
typedef unsigned char UChar;
|
||||
typedef int Int32;
|
||||
typedef unsigned int UInt32;
|
||||
typedef short Int16;
|
||||
typedef unsigned short UInt16;
|
||||
|
||||
#define True ((Bool)1)
|
||||
#define False ((Bool)0)
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define __inline__ /* */
|
||||
#endif
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
|
||||
extern void BZ2_bz__AssertH__fail ( int errcode );
|
||||
#define AssertH(cond,errcode) \
|
||||
{ if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }
|
||||
|
||||
#if BZ_DEBUG
|
||||
#define AssertD(cond,msg) \
|
||||
{ if (!(cond)) { \
|
||||
fprintf ( stderr, \
|
||||
"\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
|
||||
exit(1); \
|
||||
}}
|
||||
#else
|
||||
#define AssertD(cond,msg) /* */
|
||||
#endif
|
||||
|
||||
#define VPrintf0(zf) \
|
||||
fprintf(stderr,zf)
|
||||
#define VPrintf1(zf,za1) \
|
||||
fprintf(stderr,zf,za1)
|
||||
#define VPrintf2(zf,za1,za2) \
|
||||
fprintf(stderr,zf,za1,za2)
|
||||
#define VPrintf3(zf,za1,za2,za3) \
|
||||
fprintf(stderr,zf,za1,za2,za3)
|
||||
#define VPrintf4(zf,za1,za2,za3,za4) \
|
||||
fprintf(stderr,zf,za1,za2,za3,za4)
|
||||
#define VPrintf5(zf,za1,za2,za3,za4,za5) \
|
||||
fprintf(stderr,zf,za1,za2,za3,za4,za5)
|
||||
|
||||
#else
|
||||
|
||||
extern void bz_internal_error ( int errcode );
|
||||
#define AssertH(cond,errcode) \
|
||||
{ if (!(cond)) bz_internal_error ( errcode ); }
|
||||
#define AssertD(cond,msg) do { } while (0)
|
||||
#define VPrintf0(zf) do { } while (0)
|
||||
#define VPrintf1(zf,za1) do { } while (0)
|
||||
#define VPrintf2(zf,za1,za2) do { } while (0)
|
||||
#define VPrintf3(zf,za1,za2,za3) do { } while (0)
|
||||
#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0)
|
||||
#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
|
||||
#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp))
|
||||
|
||||
|
||||
/*-- Header bytes. --*/
|
||||
|
||||
#define BZ_HDR_B 0x42 /* 'B' */
|
||||
#define BZ_HDR_Z 0x5a /* 'Z' */
|
||||
#define BZ_HDR_h 0x68 /* 'h' */
|
||||
#define BZ_HDR_0 0x30 /* '0' */
|
||||
|
||||
/*-- Constants for the back end. --*/
|
||||
|
||||
#define BZ_MAX_ALPHA_SIZE 258
|
||||
#define BZ_MAX_CODE_LEN 23
|
||||
|
||||
#define BZ_RUNA 0
|
||||
#define BZ_RUNB 1
|
||||
|
||||
#define BZ_N_GROUPS 6
|
||||
#define BZ_G_SIZE 50
|
||||
#define BZ_N_ITERS 4
|
||||
|
||||
#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
|
||||
|
||||
|
||||
|
||||
/*-- Stuff for randomising repetitive blocks. --*/
|
||||
|
||||
extern Int32 BZ2_rNums[512];
|
||||
|
||||
#define BZ_RAND_DECLS \
|
||||
Int32 rNToGo; \
|
||||
Int32 rTPos \
|
||||
|
||||
#define BZ_RAND_INIT_MASK \
|
||||
s->rNToGo = 0; \
|
||||
s->rTPos = 0 \
|
||||
|
||||
#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)
|
||||
|
||||
#define BZ_RAND_UPD_MASK \
|
||||
if (s->rNToGo == 0) { \
|
||||
s->rNToGo = BZ2_rNums[s->rTPos]; \
|
||||
s->rTPos++; \
|
||||
if (s->rTPos == 512) s->rTPos = 0; \
|
||||
} \
|
||||
s->rNToGo--;
|
||||
|
||||
|
||||
|
||||
/*-- Stuff for doing CRCs. --*/
|
||||
|
||||
extern UInt32 BZ2_crc32Table[256];
|
||||
|
||||
#define BZ_INITIALISE_CRC(crcVar) \
|
||||
{ \
|
||||
crcVar = 0xffffffffL; \
|
||||
}
|
||||
|
||||
#define BZ_FINALISE_CRC(crcVar) \
|
||||
{ \
|
||||
crcVar = ~(crcVar); \
|
||||
}
|
||||
|
||||
#define BZ_UPDATE_CRC(crcVar,cha) \
|
||||
{ \
|
||||
crcVar = (crcVar << 8) ^ \
|
||||
BZ2_crc32Table[(crcVar >> 24) ^ \
|
||||
((UChar)cha)]; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-- States and modes for compression. --*/
|
||||
|
||||
#define BZ_M_IDLE 1
|
||||
#define BZ_M_RUNNING 2
|
||||
#define BZ_M_FLUSHING 3
|
||||
#define BZ_M_FINISHING 4
|
||||
|
||||
#define BZ_S_OUTPUT 1
|
||||
#define BZ_S_INPUT 2
|
||||
|
||||
#define BZ_N_RADIX 2
|
||||
#define BZ_N_QSORT 12
|
||||
#define BZ_N_SHELL 18
|
||||
#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
|
||||
|
||||
|
||||
|
||||
|
||||
/*-- Structure holding all the compression-side stuff. --*/
|
||||
|
||||
typedef
|
||||
struct {
|
||||
/* pointer back to the struct bz_stream */
|
||||
bz_stream* strm;
|
||||
|
||||
/* mode this stream is in, and whether inputting */
|
||||
/* or outputting data */
|
||||
Int32 mode;
|
||||
Int32 state;
|
||||
|
||||
/* remembers avail_in when flush/finish requested */
|
||||
UInt32 avail_in_expect;
|
||||
|
||||
/* for doing the block sorting */
|
||||
UInt32* arr1;
|
||||
UInt32* arr2;
|
||||
UInt32* ftab;
|
||||
Int32 origPtr;
|
||||
|
||||
/* aliases for arr1 and arr2 */
|
||||
UInt32* ptr;
|
||||
UChar* block;
|
||||
UInt16* mtfv;
|
||||
UChar* zbits;
|
||||
|
||||
/* for deciding when to use the fallback sorting algorithm */
|
||||
Int32 workFactor;
|
||||
|
||||
/* run-length-encoding of the input */
|
||||
UInt32 state_in_ch;
|
||||
Int32 state_in_len;
|
||||
BZ_RAND_DECLS;
|
||||
|
||||
/* input and output limits and current posns */
|
||||
Int32 nblock;
|
||||
Int32 nblockMAX;
|
||||
Int32 numZ;
|
||||
Int32 state_out_pos;
|
||||
|
||||
/* map of bytes used in block */
|
||||
Int32 nInUse;
|
||||
Bool inUse[256];
|
||||
UChar unseqToSeq[256];
|
||||
|
||||
/* the buffer for bit stream creation */
|
||||
UInt32 bsBuff;
|
||||
Int32 bsLive;
|
||||
|
||||
/* block and combined CRCs */
|
||||
UInt32 blockCRC;
|
||||
UInt32 combinedCRC;
|
||||
|
||||
/* misc administratium */
|
||||
Int32 verbosity;
|
||||
Int32 blockNo;
|
||||
Int32 blockSize100k;
|
||||
|
||||
/* stuff for coding the MTF values */
|
||||
Int32 nMTF;
|
||||
Int32 mtfFreq [BZ_MAX_ALPHA_SIZE];
|
||||
UChar selector [BZ_MAX_SELECTORS];
|
||||
UChar selectorMtf[BZ_MAX_SELECTORS];
|
||||
|
||||
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
/* second dimension: only 3 needed; 4 makes index calculations faster */
|
||||
UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4];
|
||||
|
||||
}
|
||||
EState;
|
||||
|
||||
|
||||
|
||||
/*-- externs for compression. --*/
|
||||
|
||||
extern void
|
||||
BZ2_blockSort ( EState* );
|
||||
|
||||
extern void
|
||||
BZ2_compressBlock ( EState*, Bool );
|
||||
|
||||
extern void
|
||||
BZ2_bsInitWrite ( EState* );
|
||||
|
||||
extern void
|
||||
BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );
|
||||
|
||||
extern void
|
||||
BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );
|
||||
|
||||
|
||||
|
||||
/*-- states for decompression. --*/
|
||||
|
||||
#define BZ_X_IDLE 1
|
||||
#define BZ_X_OUTPUT 2
|
||||
|
||||
#define BZ_X_MAGIC_1 10
|
||||
#define BZ_X_MAGIC_2 11
|
||||
#define BZ_X_MAGIC_3 12
|
||||
#define BZ_X_MAGIC_4 13
|
||||
#define BZ_X_BLKHDR_1 14
|
||||
#define BZ_X_BLKHDR_2 15
|
||||
#define BZ_X_BLKHDR_3 16
|
||||
#define BZ_X_BLKHDR_4 17
|
||||
#define BZ_X_BLKHDR_5 18
|
||||
#define BZ_X_BLKHDR_6 19
|
||||
#define BZ_X_BCRC_1 20
|
||||
#define BZ_X_BCRC_2 21
|
||||
#define BZ_X_BCRC_3 22
|
||||
#define BZ_X_BCRC_4 23
|
||||
#define BZ_X_RANDBIT 24
|
||||
#define BZ_X_ORIGPTR_1 25
|
||||
#define BZ_X_ORIGPTR_2 26
|
||||
#define BZ_X_ORIGPTR_3 27
|
||||
#define BZ_X_MAPPING_1 28
|
||||
#define BZ_X_MAPPING_2 29
|
||||
#define BZ_X_SELECTOR_1 30
|
||||
#define BZ_X_SELECTOR_2 31
|
||||
#define BZ_X_SELECTOR_3 32
|
||||
#define BZ_X_CODING_1 33
|
||||
#define BZ_X_CODING_2 34
|
||||
#define BZ_X_CODING_3 35
|
||||
#define BZ_X_MTF_1 36
|
||||
#define BZ_X_MTF_2 37
|
||||
#define BZ_X_MTF_3 38
|
||||
#define BZ_X_MTF_4 39
|
||||
#define BZ_X_MTF_5 40
|
||||
#define BZ_X_MTF_6 41
|
||||
#define BZ_X_ENDHDR_2 42
|
||||
#define BZ_X_ENDHDR_3 43
|
||||
#define BZ_X_ENDHDR_4 44
|
||||
#define BZ_X_ENDHDR_5 45
|
||||
#define BZ_X_ENDHDR_6 46
|
||||
#define BZ_X_CCRC_1 47
|
||||
#define BZ_X_CCRC_2 48
|
||||
#define BZ_X_CCRC_3 49
|
||||
#define BZ_X_CCRC_4 50
|
||||
|
||||
|
||||
|
||||
/*-- Constants for the fast MTF decoder. --*/
|
||||
|
||||
#define MTFA_SIZE 4096
|
||||
#define MTFL_SIZE 16
|
||||
|
||||
|
||||
|
||||
/*-- Structure holding all the decompression-side stuff. --*/
|
||||
|
||||
typedef
|
||||
struct {
|
||||
/* pointer back to the struct bz_stream */
|
||||
bz_stream* strm;
|
||||
|
||||
/* state indicator for this stream */
|
||||
Int32 state;
|
||||
|
||||
/* for doing the final run-length decoding */
|
||||
UChar state_out_ch;
|
||||
Int32 state_out_len;
|
||||
Bool blockRandomised;
|
||||
BZ_RAND_DECLS;
|
||||
|
||||
/* the buffer for bit stream reading */
|
||||
UInt32 bsBuff;
|
||||
Int32 bsLive;
|
||||
|
||||
/* misc administratium */
|
||||
Int32 blockSize100k;
|
||||
Bool smallDecompress;
|
||||
Int32 currBlockNo;
|
||||
Int32 verbosity;
|
||||
|
||||
/* for undoing the Burrows-Wheeler transform */
|
||||
Int32 origPtr;
|
||||
UInt32 tPos;
|
||||
Int32 k0;
|
||||
Int32 unzftab[256];
|
||||
Int32 nblock_used;
|
||||
Int32 cftab[257];
|
||||
Int32 cftabCopy[257];
|
||||
|
||||
/* for undoing the Burrows-Wheeler transform (FAST) */
|
||||
UInt32 *tt;
|
||||
|
||||
/* for undoing the Burrows-Wheeler transform (SMALL) */
|
||||
UInt16 *ll16;
|
||||
UChar *ll4;
|
||||
|
||||
/* stored and calculated CRCs */
|
||||
UInt32 storedBlockCRC;
|
||||
UInt32 storedCombinedCRC;
|
||||
UInt32 calculatedBlockCRC;
|
||||
UInt32 calculatedCombinedCRC;
|
||||
|
||||
/* map of bytes used in block */
|
||||
Int32 nInUse;
|
||||
Bool inUse[256];
|
||||
Bool inUse16[16];
|
||||
UChar seqToUnseq[256];
|
||||
|
||||
/* for decoding the MTF values */
|
||||
UChar mtfa [MTFA_SIZE];
|
||||
Int32 mtfbase[256 / MTFL_SIZE];
|
||||
UChar selector [BZ_MAX_SELECTORS];
|
||||
UChar selectorMtf[BZ_MAX_SELECTORS];
|
||||
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
|
||||
Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 minLens[BZ_N_GROUPS];
|
||||
|
||||
/* save area for scalars in the main decompress code */
|
||||
Int32 save_i;
|
||||
Int32 save_j;
|
||||
Int32 save_t;
|
||||
Int32 save_alphaSize;
|
||||
Int32 save_nGroups;
|
||||
Int32 save_nSelectors;
|
||||
Int32 save_EOB;
|
||||
Int32 save_groupNo;
|
||||
Int32 save_groupPos;
|
||||
Int32 save_nextSym;
|
||||
Int32 save_nblockMAX;
|
||||
Int32 save_nblock;
|
||||
Int32 save_es;
|
||||
Int32 save_N;
|
||||
Int32 save_curr;
|
||||
Int32 save_zt;
|
||||
Int32 save_zn;
|
||||
Int32 save_zvec;
|
||||
Int32 save_zj;
|
||||
Int32 save_gSel;
|
||||
Int32 save_gMinlen;
|
||||
Int32* save_gLimit;
|
||||
Int32* save_gBase;
|
||||
Int32* save_gPerm;
|
||||
|
||||
}
|
||||
DState;
|
||||
|
||||
|
||||
|
||||
/*-- Macros for decompression. --*/
|
||||
|
||||
#define BZ_GET_FAST(cccc) \
|
||||
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
|
||||
if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
|
||||
s->tPos = s->tt[s->tPos]; \
|
||||
cccc = (UChar)(s->tPos & 0xff); \
|
||||
s->tPos >>= 8;
|
||||
|
||||
#define BZ_GET_FAST_C(cccc) \
|
||||
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
|
||||
if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \
|
||||
c_tPos = c_tt[c_tPos]; \
|
||||
cccc = (UChar)(c_tPos & 0xff); \
|
||||
c_tPos >>= 8;
|
||||
|
||||
#define SET_LL4(i,n) \
|
||||
{ if (((i) & 0x1) == 0) \
|
||||
s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \
|
||||
s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \
|
||||
}
|
||||
|
||||
#define GET_LL4(i) \
|
||||
((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)
|
||||
|
||||
#define SET_LL(i,n) \
|
||||
{ s->ll16[i] = (UInt16)(n & 0x0000ffff); \
|
||||
SET_LL4(i, n >> 16); \
|
||||
}
|
||||
|
||||
#define GET_LL(i) \
|
||||
(((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))
|
||||
|
||||
#define BZ_GET_SMALL(cccc) \
|
||||
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
|
||||
if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
|
||||
cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \
|
||||
s->tPos = GET_LL(s->tPos);
|
||||
|
||||
|
||||
/*-- externs for decompression. --*/
|
||||
|
||||
extern Int32
|
||||
BZ2_indexIntoF ( Int32, Int32* );
|
||||
|
||||
extern Int32
|
||||
BZ2_decompress ( DState* );
|
||||
|
||||
extern void
|
||||
BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
|
||||
Int32, Int32, Int32 );
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/
|
||||
|
||||
#ifdef BZ_NO_STDIO
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end bzlib_private.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,672 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Compression machinery (not incl block sorting) ---*/
|
||||
/*--- compress.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/* CHANGES
|
||||
0.9.0 -- original version.
|
||||
0.9.0a/b -- no changes in this file.
|
||||
0.9.0c -- changed setting of nGroups in sendMTFValues()
|
||||
so as to do a bit better on small files
|
||||
*/
|
||||
|
||||
#include "bzlib_private.h"
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
/*--- Bit stream I/O ---*/
|
||||
/*---------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
void BZ2_bsInitWrite ( EState* s )
|
||||
{
|
||||
s->bsLive = 0;
|
||||
s->bsBuff = 0;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void bsFinishWrite ( EState* s )
|
||||
{
|
||||
while (s->bsLive > 0) {
|
||||
s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
|
||||
s->numZ++;
|
||||
s->bsBuff <<= 8;
|
||||
s->bsLive -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
#define bsNEEDW(nz) \
|
||||
{ \
|
||||
while (s->bsLive >= 8) { \
|
||||
s->zbits[s->numZ] \
|
||||
= (UChar)(s->bsBuff >> 24); \
|
||||
s->numZ++; \
|
||||
s->bsBuff <<= 8; \
|
||||
s->bsLive -= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
__inline__
|
||||
void bsW ( EState* s, Int32 n, UInt32 v )
|
||||
{
|
||||
bsNEEDW ( n );
|
||||
s->bsBuff |= (v << (32 - s->bsLive - n));
|
||||
s->bsLive += n;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void bsPutUInt32 ( EState* s, UInt32 u )
|
||||
{
|
||||
bsW ( s, 8, (u >> 24) & 0xffL );
|
||||
bsW ( s, 8, (u >> 16) & 0xffL );
|
||||
bsW ( s, 8, (u >> 8) & 0xffL );
|
||||
bsW ( s, 8, u & 0xffL );
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void bsPutUChar ( EState* s, UChar c )
|
||||
{
|
||||
bsW( s, 8, (UInt32)c );
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
/*--- The back end proper ---*/
|
||||
/*---------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void makeMaps_e ( EState* s )
|
||||
{
|
||||
Int32 i;
|
||||
s->nInUse = 0;
|
||||
for (i = 0; i < 256; i++)
|
||||
if (s->inUse[i]) {
|
||||
s->unseqToSeq[i] = s->nInUse;
|
||||
s->nInUse++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void generateMTFValues ( EState* s )
|
||||
{
|
||||
UChar yy[256];
|
||||
Int32 i, j;
|
||||
Int32 zPend;
|
||||
Int32 wr;
|
||||
Int32 EOB;
|
||||
|
||||
/*
|
||||
After sorting (eg, here),
|
||||
s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
|
||||
and
|
||||
((UChar*)s->arr2) [ 0 .. s->nblock-1 ]
|
||||
holds the original block data.
|
||||
|
||||
The first thing to do is generate the MTF values,
|
||||
and put them in
|
||||
((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
|
||||
Because there are strictly fewer or equal MTF values
|
||||
than block values, ptr values in this area are overwritten
|
||||
with MTF values only when they are no longer needed.
|
||||
|
||||
The final compressed bitstream is generated into the
|
||||
area starting at
|
||||
(UChar*) (&((UChar*)s->arr2)[s->nblock])
|
||||
|
||||
These storage aliases are set up in bzCompressInit(),
|
||||
except for the last one, which is arranged in
|
||||
compressBlock().
|
||||
*/
|
||||
UInt32* ptr = s->ptr;
|
||||
UChar* block = s->block;
|
||||
UInt16* mtfv = s->mtfv;
|
||||
|
||||
makeMaps_e ( s );
|
||||
EOB = s->nInUse+1;
|
||||
|
||||
for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
|
||||
|
||||
wr = 0;
|
||||
zPend = 0;
|
||||
for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
|
||||
|
||||
for (i = 0; i < s->nblock; i++) {
|
||||
UChar ll_i;
|
||||
AssertD ( wr <= i, "generateMTFValues(1)" );
|
||||
j = ptr[i]-1; if (j < 0) j += s->nblock;
|
||||
ll_i = s->unseqToSeq[block[j]];
|
||||
AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
|
||||
|
||||
if (yy[0] == ll_i) {
|
||||
zPend++;
|
||||
} else {
|
||||
|
||||
if (zPend > 0) {
|
||||
zPend--;
|
||||
while (True) {
|
||||
if (zPend & 1) {
|
||||
mtfv[wr] = BZ_RUNB; wr++;
|
||||
s->mtfFreq[BZ_RUNB]++;
|
||||
} else {
|
||||
mtfv[wr] = BZ_RUNA; wr++;
|
||||
s->mtfFreq[BZ_RUNA]++;
|
||||
}
|
||||
if (zPend < 2) break;
|
||||
zPend = (zPend - 2) / 2;
|
||||
};
|
||||
zPend = 0;
|
||||
}
|
||||
{
|
||||
register UChar rtmp;
|
||||
register UChar* ryy_j;
|
||||
register UChar rll_i;
|
||||
rtmp = yy[1];
|
||||
yy[1] = yy[0];
|
||||
ryy_j = &(yy[1]);
|
||||
rll_i = ll_i;
|
||||
while ( rll_i != rtmp ) {
|
||||
register UChar rtmp2;
|
||||
ryy_j++;
|
||||
rtmp2 = rtmp;
|
||||
rtmp = *ryy_j;
|
||||
*ryy_j = rtmp2;
|
||||
};
|
||||
yy[0] = rtmp;
|
||||
j = ryy_j - &(yy[0]);
|
||||
mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (zPend > 0) {
|
||||
zPend--;
|
||||
while (True) {
|
||||
if (zPend & 1) {
|
||||
mtfv[wr] = BZ_RUNB; wr++;
|
||||
s->mtfFreq[BZ_RUNB]++;
|
||||
} else {
|
||||
mtfv[wr] = BZ_RUNA; wr++;
|
||||
s->mtfFreq[BZ_RUNA]++;
|
||||
}
|
||||
if (zPend < 2) break;
|
||||
zPend = (zPend - 2) / 2;
|
||||
};
|
||||
zPend = 0;
|
||||
}
|
||||
|
||||
mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
|
||||
|
||||
s->nMTF = wr;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
#define BZ_LESSER_ICOST 0
|
||||
#define BZ_GREATER_ICOST 15
|
||||
|
||||
static
|
||||
void sendMTFValues ( EState* s )
|
||||
{
|
||||
Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
|
||||
Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
|
||||
Int32 nGroups, nBytes;
|
||||
|
||||
/*--
|
||||
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
is a global since the decoder also needs it.
|
||||
|
||||
Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
|
||||
are also globals only used in this proc.
|
||||
Made global to keep stack frame size small.
|
||||
--*/
|
||||
|
||||
|
||||
UInt16 cost[BZ_N_GROUPS];
|
||||
Int32 fave[BZ_N_GROUPS];
|
||||
|
||||
UInt16* mtfv = s->mtfv;
|
||||
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf3( " %d in block, %d after MTF & 1-2 coding, "
|
||||
"%d+2 syms in use\n",
|
||||
s->nblock, s->nMTF, s->nInUse );
|
||||
|
||||
alphaSize = s->nInUse+2;
|
||||
for (t = 0; t < BZ_N_GROUPS; t++)
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
s->len[t][v] = BZ_GREATER_ICOST;
|
||||
|
||||
/*--- Decide how many coding tables to use ---*/
|
||||
AssertH ( s->nMTF > 0, 3001 );
|
||||
if (s->nMTF < 200) nGroups = 2; else
|
||||
if (s->nMTF < 600) nGroups = 3; else
|
||||
if (s->nMTF < 1200) nGroups = 4; else
|
||||
if (s->nMTF < 2400) nGroups = 5; else
|
||||
nGroups = 6;
|
||||
|
||||
/*--- Generate an initial set of coding tables ---*/
|
||||
{
|
||||
Int32 nPart, remF, tFreq, aFreq;
|
||||
|
||||
nPart = nGroups;
|
||||
remF = s->nMTF;
|
||||
gs = 0;
|
||||
while (nPart > 0) {
|
||||
tFreq = remF / nPart;
|
||||
ge = gs-1;
|
||||
aFreq = 0;
|
||||
while (aFreq < tFreq && ge < alphaSize-1) {
|
||||
ge++;
|
||||
aFreq += s->mtfFreq[ge];
|
||||
}
|
||||
|
||||
if (ge > gs
|
||||
&& nPart != nGroups && nPart != 1
|
||||
&& ((nGroups-nPart) % 2 == 1)) {
|
||||
aFreq -= s->mtfFreq[ge];
|
||||
ge--;
|
||||
}
|
||||
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf5( " initial group %d, [%d .. %d], "
|
||||
"has %d syms (%4.1f%%)\n",
|
||||
nPart, gs, ge, aFreq,
|
||||
(100.0 * (float)aFreq) / (float)(s->nMTF) );
|
||||
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
if (v >= gs && v <= ge)
|
||||
s->len[nPart-1][v] = BZ_LESSER_ICOST; else
|
||||
s->len[nPart-1][v] = BZ_GREATER_ICOST;
|
||||
|
||||
nPart--;
|
||||
gs = ge+1;
|
||||
remF -= aFreq;
|
||||
}
|
||||
}
|
||||
|
||||
/*---
|
||||
Iterate up to BZ_N_ITERS times to improve the tables.
|
||||
---*/
|
||||
for (iter = 0; iter < BZ_N_ITERS; iter++) {
|
||||
|
||||
for (t = 0; t < nGroups; t++) fave[t] = 0;
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
s->rfreq[t][v] = 0;
|
||||
|
||||
/*---
|
||||
Set up an auxiliary length table which is used to fast-track
|
||||
the common case (nGroups == 6).
|
||||
---*/
|
||||
if (nGroups == 6) {
|
||||
for (v = 0; v < alphaSize; v++) {
|
||||
s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
|
||||
s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
|
||||
s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
|
||||
}
|
||||
}
|
||||
|
||||
nSelectors = 0;
|
||||
totc = 0;
|
||||
gs = 0;
|
||||
while (True) {
|
||||
|
||||
/*--- Set group start & end marks. --*/
|
||||
if (gs >= s->nMTF) break;
|
||||
ge = gs + BZ_G_SIZE - 1;
|
||||
if (ge >= s->nMTF) ge = s->nMTF-1;
|
||||
|
||||
/*--
|
||||
Calculate the cost of this group as coded
|
||||
by each of the coding tables.
|
||||
--*/
|
||||
for (t = 0; t < nGroups; t++) cost[t] = 0;
|
||||
|
||||
if (nGroups == 6 && 50 == ge-gs+1) {
|
||||
/*--- fast track the common case ---*/
|
||||
register UInt32 cost01, cost23, cost45;
|
||||
register UInt16 icv;
|
||||
cost01 = cost23 = cost45 = 0;
|
||||
|
||||
# define BZ_ITER(nn) \
|
||||
icv = mtfv[gs+(nn)]; \
|
||||
cost01 += s->len_pack[icv][0]; \
|
||||
cost23 += s->len_pack[icv][1]; \
|
||||
cost45 += s->len_pack[icv][2]; \
|
||||
|
||||
BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4);
|
||||
BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9);
|
||||
BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
|
||||
BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
|
||||
BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
|
||||
BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
|
||||
BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
|
||||
BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
|
||||
BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
|
||||
BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
|
||||
|
||||
# undef BZ_ITER
|
||||
|
||||
cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
|
||||
cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
|
||||
cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
|
||||
|
||||
} else {
|
||||
/*--- slow version which correctly handles all situations ---*/
|
||||
for (i = gs; i <= ge; i++) {
|
||||
UInt16 icv = mtfv[i];
|
||||
for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
|
||||
}
|
||||
}
|
||||
|
||||
/*--
|
||||
Find the coding table which is best for this group,
|
||||
and record its identity in the selector table.
|
||||
--*/
|
||||
bc = 999999999; bt = -1;
|
||||
for (t = 0; t < nGroups; t++)
|
||||
if (cost[t] < bc) { bc = cost[t]; bt = t; };
|
||||
totc += bc;
|
||||
fave[bt]++;
|
||||
s->selector[nSelectors] = bt;
|
||||
nSelectors++;
|
||||
|
||||
/*--
|
||||
Increment the symbol frequencies for the selected table.
|
||||
--*/
|
||||
if (nGroups == 6 && 50 == ge-gs+1) {
|
||||
/*--- fast track the common case ---*/
|
||||
|
||||
# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
|
||||
|
||||
BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4);
|
||||
BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9);
|
||||
BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
|
||||
BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
|
||||
BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
|
||||
BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
|
||||
BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
|
||||
BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
|
||||
BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
|
||||
BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
|
||||
|
||||
# undef BZ_ITUR
|
||||
|
||||
} else {
|
||||
/*--- slow version which correctly handles all situations ---*/
|
||||
for (i = gs; i <= ge; i++)
|
||||
s->rfreq[bt][ mtfv[i] ]++;
|
||||
}
|
||||
|
||||
gs = ge+1;
|
||||
}
|
||||
if (s->verbosity >= 3) {
|
||||
VPrintf2 ( " pass %d: size is %d, grp uses are ",
|
||||
iter+1, totc/8 );
|
||||
for (t = 0; t < nGroups; t++)
|
||||
VPrintf1 ( "%d ", fave[t] );
|
||||
VPrintf0 ( "\n" );
|
||||
}
|
||||
|
||||
/*--
|
||||
Recompute the tables based on the accumulated frequencies.
|
||||
--*/
|
||||
/* maxLen was changed from 20 to 17 in bzip2-1.0.3. See
|
||||
comment in huffman.c for details. */
|
||||
for (t = 0; t < nGroups; t++)
|
||||
BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]),
|
||||
alphaSize, 17 /*20*/ );
|
||||
}
|
||||
|
||||
|
||||
AssertH( nGroups < 8, 3002 );
|
||||
AssertH( nSelectors < 32768 &&
|
||||
nSelectors <= (2 + (900000 / BZ_G_SIZE)),
|
||||
3003 );
|
||||
|
||||
|
||||
/*--- Compute MTF values for the selectors. ---*/
|
||||
{
|
||||
UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
|
||||
for (i = 0; i < nGroups; i++) pos[i] = i;
|
||||
for (i = 0; i < nSelectors; i++) {
|
||||
ll_i = s->selector[i];
|
||||
j = 0;
|
||||
tmp = pos[j];
|
||||
while ( ll_i != tmp ) {
|
||||
j++;
|
||||
tmp2 = tmp;
|
||||
tmp = pos[j];
|
||||
pos[j] = tmp2;
|
||||
};
|
||||
pos[0] = tmp;
|
||||
s->selectorMtf[i] = j;
|
||||
}
|
||||
};
|
||||
|
||||
/*--- Assign actual codes for the tables. --*/
|
||||
for (t = 0; t < nGroups; t++) {
|
||||
minLen = 32;
|
||||
maxLen = 0;
|
||||
for (i = 0; i < alphaSize; i++) {
|
||||
if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
|
||||
if (s->len[t][i] < minLen) minLen = s->len[t][i];
|
||||
}
|
||||
AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
|
||||
AssertH ( !(minLen < 1), 3005 );
|
||||
BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]),
|
||||
minLen, maxLen, alphaSize );
|
||||
}
|
||||
|
||||
/*--- Transmit the mapping table. ---*/
|
||||
{
|
||||
Bool inUse16[16];
|
||||
for (i = 0; i < 16; i++) {
|
||||
inUse16[i] = False;
|
||||
for (j = 0; j < 16; j++)
|
||||
if (s->inUse[i * 16 + j]) inUse16[i] = True;
|
||||
}
|
||||
|
||||
nBytes = s->numZ;
|
||||
for (i = 0; i < 16; i++)
|
||||
if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (inUse16[i])
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
|
||||
}
|
||||
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes );
|
||||
}
|
||||
|
||||
/*--- Now the selectors. ---*/
|
||||
nBytes = s->numZ;
|
||||
bsW ( s, 3, nGroups );
|
||||
bsW ( s, 15, nSelectors );
|
||||
for (i = 0; i < nSelectors; i++) {
|
||||
for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
|
||||
bsW(s,1,0);
|
||||
}
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf1( "selectors %d, ", s->numZ-nBytes );
|
||||
|
||||
/*--- Now the coding tables. ---*/
|
||||
nBytes = s->numZ;
|
||||
|
||||
for (t = 0; t < nGroups; t++) {
|
||||
Int32 curr = s->len[t][0];
|
||||
bsW ( s, 5, curr );
|
||||
for (i = 0; i < alphaSize; i++) {
|
||||
while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
|
||||
while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
|
||||
bsW ( s, 1, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
|
||||
|
||||
/*--- And finally, the block data proper ---*/
|
||||
nBytes = s->numZ;
|
||||
selCtr = 0;
|
||||
gs = 0;
|
||||
while (True) {
|
||||
if (gs >= s->nMTF) break;
|
||||
ge = gs + BZ_G_SIZE - 1;
|
||||
if (ge >= s->nMTF) ge = s->nMTF-1;
|
||||
AssertH ( s->selector[selCtr] < nGroups, 3006 );
|
||||
|
||||
if (nGroups == 6 && 50 == ge-gs+1) {
|
||||
/*--- fast track the common case ---*/
|
||||
UInt16 mtfv_i;
|
||||
UChar* s_len_sel_selCtr
|
||||
= &(s->len[s->selector[selCtr]][0]);
|
||||
Int32* s_code_sel_selCtr
|
||||
= &(s->code[s->selector[selCtr]][0]);
|
||||
|
||||
# define BZ_ITAH(nn) \
|
||||
mtfv_i = mtfv[gs+(nn)]; \
|
||||
bsW ( s, \
|
||||
s_len_sel_selCtr[mtfv_i], \
|
||||
s_code_sel_selCtr[mtfv_i] )
|
||||
|
||||
BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4);
|
||||
BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9);
|
||||
BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
|
||||
BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
|
||||
BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
|
||||
BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
|
||||
BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
|
||||
BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
|
||||
BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
|
||||
BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
|
||||
|
||||
# undef BZ_ITAH
|
||||
|
||||
} else {
|
||||
/*--- slow version which correctly handles all situations ---*/
|
||||
for (i = gs; i <= ge; i++) {
|
||||
bsW ( s,
|
||||
s->len [s->selector[selCtr]] [mtfv[i]],
|
||||
s->code [s->selector[selCtr]] [mtfv[i]] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gs = ge+1;
|
||||
selCtr++;
|
||||
}
|
||||
AssertH( selCtr == nSelectors, 3007 );
|
||||
|
||||
if (s->verbosity >= 3)
|
||||
VPrintf1( "codes %d\n", s->numZ-nBytes );
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
void BZ2_compressBlock ( EState* s, Bool is_last_block )
|
||||
{
|
||||
if (s->nblock > 0) {
|
||||
|
||||
BZ_FINALISE_CRC ( s->blockCRC );
|
||||
s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
|
||||
s->combinedCRC ^= s->blockCRC;
|
||||
if (s->blockNo > 1) s->numZ = 0;
|
||||
|
||||
if (s->verbosity >= 2)
|
||||
VPrintf4( " block %d: crc = 0x%08x, "
|
||||
"combined CRC = 0x%08x, size = %d\n",
|
||||
s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
|
||||
|
||||
BZ2_blockSort ( s );
|
||||
}
|
||||
|
||||
s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
|
||||
|
||||
/*-- If this is the first block, create the stream header. --*/
|
||||
if (s->blockNo == 1) {
|
||||
BZ2_bsInitWrite ( s );
|
||||
bsPutUChar ( s, BZ_HDR_B );
|
||||
bsPutUChar ( s, BZ_HDR_Z );
|
||||
bsPutUChar ( s, BZ_HDR_h );
|
||||
bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
|
||||
}
|
||||
|
||||
if (s->nblock > 0) {
|
||||
|
||||
bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
|
||||
bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
|
||||
bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
|
||||
|
||||
/*-- Now the block's CRC, so it is in a known place. --*/
|
||||
bsPutUInt32 ( s, s->blockCRC );
|
||||
|
||||
/*--
|
||||
Now a single bit indicating (non-)randomisation.
|
||||
As of version 0.9.5, we use a better sorting algorithm
|
||||
which makes randomisation unnecessary. So always set
|
||||
the randomised bit to 'no'. Of course, the decoder
|
||||
still needs to be able to handle randomised blocks
|
||||
so as to maintain backwards compatibility with
|
||||
older versions of bzip2.
|
||||
--*/
|
||||
bsW(s,1,0);
|
||||
|
||||
bsW ( s, 24, s->origPtr );
|
||||
generateMTFValues ( s );
|
||||
sendMTFValues ( s );
|
||||
}
|
||||
|
||||
|
||||
/*-- If this is the last block, add the stream trailer. --*/
|
||||
if (is_last_block) {
|
||||
|
||||
bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
|
||||
bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
|
||||
bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
|
||||
bsPutUInt32 ( s, s->combinedCRC );
|
||||
if (s->verbosity >= 2)
|
||||
VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC );
|
||||
bsFinishWrite ( s );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end compress.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,104 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Table for doing CRCs ---*/
|
||||
/*--- crctable.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#include "bzlib_private.h"
|
||||
|
||||
/*--
|
||||
I think this is an implementation of the AUTODIN-II,
|
||||
Ethernet & FDDI 32-bit CRC standard. Vaguely derived
|
||||
from code by Rob Warnock, in Section 51 of the
|
||||
comp.compression FAQ.
|
||||
--*/
|
||||
|
||||
UInt32 BZ2_crc32Table[256] = {
|
||||
|
||||
/*-- Ugly, innit? --*/
|
||||
|
||||
0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
|
||||
0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
|
||||
0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
|
||||
0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
|
||||
0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
|
||||
0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
|
||||
0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
|
||||
0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
|
||||
0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
|
||||
0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
|
||||
0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
|
||||
0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
|
||||
0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
|
||||
0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
|
||||
0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
|
||||
0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
|
||||
0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
|
||||
0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
|
||||
0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
|
||||
0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
|
||||
0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
|
||||
0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
|
||||
0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
|
||||
0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
|
||||
0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
|
||||
0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
|
||||
0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
|
||||
0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
|
||||
0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
|
||||
0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
|
||||
0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
|
||||
0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
|
||||
0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
|
||||
0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
|
||||
0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
|
||||
0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
|
||||
0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
|
||||
0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
|
||||
0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
|
||||
0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
|
||||
0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
|
||||
0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
|
||||
0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
|
||||
0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
|
||||
0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
|
||||
0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
|
||||
0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
|
||||
0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
|
||||
0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
|
||||
0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
|
||||
0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
|
||||
0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
|
||||
0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
|
||||
0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
|
||||
0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
|
||||
0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
|
||||
0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
|
||||
0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
|
||||
0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
|
||||
0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
|
||||
0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
|
||||
0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
|
||||
0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
|
||||
0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end crctable.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,626 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Decompression machinery ---*/
|
||||
/*--- decompress.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#include "bzlib_private.h"
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
static
|
||||
void makeMaps_d ( DState* s )
|
||||
{
|
||||
Int32 i;
|
||||
s->nInUse = 0;
|
||||
for (i = 0; i < 256; i++)
|
||||
if (s->inUse[i]) {
|
||||
s->seqToUnseq[s->nInUse] = i;
|
||||
s->nInUse++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
#define RETURN(rrr) \
|
||||
{ retVal = rrr; goto save_state_and_return; };
|
||||
|
||||
#define GET_BITS(lll,vvv,nnn) \
|
||||
case lll: s->state = lll; \
|
||||
while (True) { \
|
||||
if (s->bsLive >= nnn) { \
|
||||
UInt32 v; \
|
||||
v = (s->bsBuff >> \
|
||||
(s->bsLive-nnn)) & ((1 << nnn)-1); \
|
||||
s->bsLive -= nnn; \
|
||||
vvv = v; \
|
||||
break; \
|
||||
} \
|
||||
if (s->strm->avail_in == 0) RETURN(BZ_OK); \
|
||||
s->bsBuff \
|
||||
= (s->bsBuff << 8) | \
|
||||
((UInt32) \
|
||||
(*((UChar*)(s->strm->next_in)))); \
|
||||
s->bsLive += 8; \
|
||||
s->strm->next_in++; \
|
||||
s->strm->avail_in--; \
|
||||
s->strm->total_in_lo32++; \
|
||||
if (s->strm->total_in_lo32 == 0) \
|
||||
s->strm->total_in_hi32++; \
|
||||
}
|
||||
|
||||
#define GET_UCHAR(lll,uuu) \
|
||||
GET_BITS(lll,uuu,8)
|
||||
|
||||
#define GET_BIT(lll,uuu) \
|
||||
GET_BITS(lll,uuu,1)
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
#define GET_MTF_VAL(label1,label2,lval) \
|
||||
{ \
|
||||
if (groupPos == 0) { \
|
||||
groupNo++; \
|
||||
if (groupNo >= nSelectors) \
|
||||
RETURN(BZ_DATA_ERROR); \
|
||||
groupPos = BZ_G_SIZE; \
|
||||
gSel = s->selector[groupNo]; \
|
||||
gMinlen = s->minLens[gSel]; \
|
||||
gLimit = &(s->limit[gSel][0]); \
|
||||
gPerm = &(s->perm[gSel][0]); \
|
||||
gBase = &(s->base[gSel][0]); \
|
||||
} \
|
||||
groupPos--; \
|
||||
zn = gMinlen; \
|
||||
GET_BITS(label1, zvec, zn); \
|
||||
while (1) { \
|
||||
if (zn > 20 /* the longest code */) \
|
||||
RETURN(BZ_DATA_ERROR); \
|
||||
if (zvec <= gLimit[zn]) break; \
|
||||
zn++; \
|
||||
GET_BIT(label2, zj); \
|
||||
zvec = (zvec << 1) | zj; \
|
||||
}; \
|
||||
if (zvec - gBase[zn] < 0 \
|
||||
|| zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \
|
||||
RETURN(BZ_DATA_ERROR); \
|
||||
lval = gPerm[zvec - gBase[zn]]; \
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
Int32 BZ2_decompress ( DState* s )
|
||||
{
|
||||
UChar uc;
|
||||
Int32 retVal;
|
||||
Int32 minLen, maxLen;
|
||||
bz_stream* strm = s->strm;
|
||||
|
||||
/* stuff that needs to be saved/restored */
|
||||
Int32 i;
|
||||
Int32 j;
|
||||
Int32 t;
|
||||
Int32 alphaSize;
|
||||
Int32 nGroups;
|
||||
Int32 nSelectors;
|
||||
Int32 EOB;
|
||||
Int32 groupNo;
|
||||
Int32 groupPos;
|
||||
Int32 nextSym;
|
||||
Int32 nblockMAX;
|
||||
Int32 nblock;
|
||||
Int32 es;
|
||||
Int32 N;
|
||||
Int32 curr;
|
||||
Int32 zt;
|
||||
Int32 zn;
|
||||
Int32 zvec;
|
||||
Int32 zj;
|
||||
Int32 gSel;
|
||||
Int32 gMinlen;
|
||||
Int32* gLimit;
|
||||
Int32* gBase;
|
||||
Int32* gPerm;
|
||||
|
||||
if (s->state == BZ_X_MAGIC_1) {
|
||||
/*initialise the save area*/
|
||||
s->save_i = 0;
|
||||
s->save_j = 0;
|
||||
s->save_t = 0;
|
||||
s->save_alphaSize = 0;
|
||||
s->save_nGroups = 0;
|
||||
s->save_nSelectors = 0;
|
||||
s->save_EOB = 0;
|
||||
s->save_groupNo = 0;
|
||||
s->save_groupPos = 0;
|
||||
s->save_nextSym = 0;
|
||||
s->save_nblockMAX = 0;
|
||||
s->save_nblock = 0;
|
||||
s->save_es = 0;
|
||||
s->save_N = 0;
|
||||
s->save_curr = 0;
|
||||
s->save_zt = 0;
|
||||
s->save_zn = 0;
|
||||
s->save_zvec = 0;
|
||||
s->save_zj = 0;
|
||||
s->save_gSel = 0;
|
||||
s->save_gMinlen = 0;
|
||||
s->save_gLimit = NULL;
|
||||
s->save_gBase = NULL;
|
||||
s->save_gPerm = NULL;
|
||||
}
|
||||
|
||||
/*restore from the save area*/
|
||||
i = s->save_i;
|
||||
j = s->save_j;
|
||||
t = s->save_t;
|
||||
alphaSize = s->save_alphaSize;
|
||||
nGroups = s->save_nGroups;
|
||||
nSelectors = s->save_nSelectors;
|
||||
EOB = s->save_EOB;
|
||||
groupNo = s->save_groupNo;
|
||||
groupPos = s->save_groupPos;
|
||||
nextSym = s->save_nextSym;
|
||||
nblockMAX = s->save_nblockMAX;
|
||||
nblock = s->save_nblock;
|
||||
es = s->save_es;
|
||||
N = s->save_N;
|
||||
curr = s->save_curr;
|
||||
zt = s->save_zt;
|
||||
zn = s->save_zn;
|
||||
zvec = s->save_zvec;
|
||||
zj = s->save_zj;
|
||||
gSel = s->save_gSel;
|
||||
gMinlen = s->save_gMinlen;
|
||||
gLimit = s->save_gLimit;
|
||||
gBase = s->save_gBase;
|
||||
gPerm = s->save_gPerm;
|
||||
|
||||
retVal = BZ_OK;
|
||||
|
||||
switch (s->state) {
|
||||
|
||||
GET_UCHAR(BZ_X_MAGIC_1, uc);
|
||||
if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
|
||||
|
||||
GET_UCHAR(BZ_X_MAGIC_2, uc);
|
||||
if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
|
||||
|
||||
GET_UCHAR(BZ_X_MAGIC_3, uc)
|
||||
if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
|
||||
|
||||
GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
|
||||
if (s->blockSize100k < (BZ_HDR_0 + 1) ||
|
||||
s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
|
||||
s->blockSize100k -= BZ_HDR_0;
|
||||
|
||||
if (s->smallDecompress) {
|
||||
s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
|
||||
s->ll4 = BZALLOC(
|
||||
((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
|
||||
);
|
||||
if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
|
||||
} else {
|
||||
s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
|
||||
if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
|
||||
}
|
||||
|
||||
GET_UCHAR(BZ_X_BLKHDR_1, uc);
|
||||
|
||||
if (uc == 0x17) goto endhdr_2;
|
||||
if (uc != 0x31) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_BLKHDR_2, uc);
|
||||
if (uc != 0x41) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_BLKHDR_3, uc);
|
||||
if (uc != 0x59) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_BLKHDR_4, uc);
|
||||
if (uc != 0x26) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_BLKHDR_5, uc);
|
||||
if (uc != 0x53) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_BLKHDR_6, uc);
|
||||
if (uc != 0x59) RETURN(BZ_DATA_ERROR);
|
||||
|
||||
s->currBlockNo++;
|
||||
if (s->verbosity >= 2)
|
||||
VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo );
|
||||
|
||||
s->storedBlockCRC = 0;
|
||||
GET_UCHAR(BZ_X_BCRC_1, uc);
|
||||
s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_BCRC_2, uc);
|
||||
s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_BCRC_3, uc);
|
||||
s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_BCRC_4, uc);
|
||||
s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
|
||||
|
||||
GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
|
||||
|
||||
s->origPtr = 0;
|
||||
GET_UCHAR(BZ_X_ORIGPTR_1, uc);
|
||||
s->origPtr = (s->origPtr << 8) | ((Int32)uc);
|
||||
GET_UCHAR(BZ_X_ORIGPTR_2, uc);
|
||||
s->origPtr = (s->origPtr << 8) | ((Int32)uc);
|
||||
GET_UCHAR(BZ_X_ORIGPTR_3, uc);
|
||||
s->origPtr = (s->origPtr << 8) | ((Int32)uc);
|
||||
|
||||
if (s->origPtr < 0)
|
||||
RETURN(BZ_DATA_ERROR);
|
||||
if (s->origPtr > 10 + 100000*s->blockSize100k)
|
||||
RETURN(BZ_DATA_ERROR);
|
||||
|
||||
/*--- Receive the mapping table ---*/
|
||||
for (i = 0; i < 16; i++) {
|
||||
GET_BIT(BZ_X_MAPPING_1, uc);
|
||||
if (uc == 1)
|
||||
s->inUse16[i] = True; else
|
||||
s->inUse16[i] = False;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i++) s->inUse[i] = False;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (s->inUse16[i])
|
||||
for (j = 0; j < 16; j++) {
|
||||
GET_BIT(BZ_X_MAPPING_2, uc);
|
||||
if (uc == 1) s->inUse[i * 16 + j] = True;
|
||||
}
|
||||
makeMaps_d ( s );
|
||||
if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
|
||||
alphaSize = s->nInUse+2;
|
||||
|
||||
/*--- Now the selectors ---*/
|
||||
GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
|
||||
if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
|
||||
GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
|
||||
if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
|
||||
for (i = 0; i < nSelectors; i++) {
|
||||
j = 0;
|
||||
while (True) {
|
||||
GET_BIT(BZ_X_SELECTOR_3, uc);
|
||||
if (uc == 0) break;
|
||||
j++;
|
||||
if (j >= nGroups) RETURN(BZ_DATA_ERROR);
|
||||
}
|
||||
s->selectorMtf[i] = j;
|
||||
}
|
||||
|
||||
/*--- Undo the MTF values for the selectors. ---*/
|
||||
{
|
||||
UChar pos[BZ_N_GROUPS], tmp, v;
|
||||
for (v = 0; v < nGroups; v++) pos[v] = v;
|
||||
|
||||
for (i = 0; i < nSelectors; i++) {
|
||||
v = s->selectorMtf[i];
|
||||
tmp = pos[v];
|
||||
while (v > 0) { pos[v] = pos[v-1]; v--; }
|
||||
pos[0] = tmp;
|
||||
s->selector[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*--- Now the coding tables ---*/
|
||||
for (t = 0; t < nGroups; t++) {
|
||||
GET_BITS(BZ_X_CODING_1, curr, 5);
|
||||
for (i = 0; i < alphaSize; i++) {
|
||||
while (True) {
|
||||
if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
|
||||
GET_BIT(BZ_X_CODING_2, uc);
|
||||
if (uc == 0) break;
|
||||
GET_BIT(BZ_X_CODING_3, uc);
|
||||
if (uc == 0) curr++; else curr--;
|
||||
}
|
||||
s->len[t][i] = curr;
|
||||
}
|
||||
}
|
||||
|
||||
/*--- Create the Huffman decoding tables ---*/
|
||||
for (t = 0; t < nGroups; t++) {
|
||||
minLen = 32;
|
||||
maxLen = 0;
|
||||
for (i = 0; i < alphaSize; i++) {
|
||||
if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
|
||||
if (s->len[t][i] < minLen) minLen = s->len[t][i];
|
||||
}
|
||||
BZ2_hbCreateDecodeTables (
|
||||
&(s->limit[t][0]),
|
||||
&(s->base[t][0]),
|
||||
&(s->perm[t][0]),
|
||||
&(s->len[t][0]),
|
||||
minLen, maxLen, alphaSize
|
||||
);
|
||||
s->minLens[t] = minLen;
|
||||
}
|
||||
|
||||
/*--- Now the MTF values ---*/
|
||||
|
||||
EOB = s->nInUse+1;
|
||||
nblockMAX = 100000 * s->blockSize100k;
|
||||
groupNo = -1;
|
||||
groupPos = 0;
|
||||
|
||||
for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
|
||||
|
||||
/*-- MTF init --*/
|
||||
{
|
||||
Int32 ii, jj, kk;
|
||||
kk = MTFA_SIZE-1;
|
||||
for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
|
||||
for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
|
||||
s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
|
||||
kk--;
|
||||
}
|
||||
s->mtfbase[ii] = kk + 1;
|
||||
}
|
||||
}
|
||||
/*-- end MTF init --*/
|
||||
|
||||
nblock = 0;
|
||||
GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
|
||||
|
||||
while (True) {
|
||||
|
||||
if (nextSym == EOB) break;
|
||||
|
||||
if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
|
||||
|
||||
es = -1;
|
||||
N = 1;
|
||||
do {
|
||||
if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
|
||||
if (nextSym == BZ_RUNB) es = es + (1+1) * N;
|
||||
N = N * 2;
|
||||
GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
|
||||
}
|
||||
while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
|
||||
|
||||
es++;
|
||||
uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
|
||||
s->unzftab[uc] += es;
|
||||
|
||||
if (s->smallDecompress)
|
||||
while (es > 0) {
|
||||
if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
|
||||
s->ll16[nblock] = (UInt16)uc;
|
||||
nblock++;
|
||||
es--;
|
||||
}
|
||||
else
|
||||
while (es > 0) {
|
||||
if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
|
||||
s->tt[nblock] = (UInt32)uc;
|
||||
nblock++;
|
||||
es--;
|
||||
};
|
||||
|
||||
continue;
|
||||
|
||||
} else {
|
||||
|
||||
if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
|
||||
|
||||
/*-- uc = MTF ( nextSym-1 ) --*/
|
||||
{
|
||||
Int32 ii, jj, kk, pp, lno, off;
|
||||
UInt32 nn;
|
||||
nn = (UInt32)(nextSym - 1);
|
||||
|
||||
if (nn < MTFL_SIZE) {
|
||||
/* avoid general-case expense */
|
||||
pp = s->mtfbase[0];
|
||||
uc = s->mtfa[pp+nn];
|
||||
while (nn > 3) {
|
||||
Int32 z = pp+nn;
|
||||
s->mtfa[(z) ] = s->mtfa[(z)-1];
|
||||
s->mtfa[(z)-1] = s->mtfa[(z)-2];
|
||||
s->mtfa[(z)-2] = s->mtfa[(z)-3];
|
||||
s->mtfa[(z)-3] = s->mtfa[(z)-4];
|
||||
nn -= 4;
|
||||
}
|
||||
while (nn > 0) {
|
||||
s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
|
||||
};
|
||||
s->mtfa[pp] = uc;
|
||||
} else {
|
||||
/* general case */
|
||||
lno = nn / MTFL_SIZE;
|
||||
off = nn % MTFL_SIZE;
|
||||
pp = s->mtfbase[lno] + off;
|
||||
uc = s->mtfa[pp];
|
||||
while (pp > s->mtfbase[lno]) {
|
||||
s->mtfa[pp] = s->mtfa[pp-1]; pp--;
|
||||
};
|
||||
s->mtfbase[lno]++;
|
||||
while (lno > 0) {
|
||||
s->mtfbase[lno]--;
|
||||
s->mtfa[s->mtfbase[lno]]
|
||||
= s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
|
||||
lno--;
|
||||
}
|
||||
s->mtfbase[0]--;
|
||||
s->mtfa[s->mtfbase[0]] = uc;
|
||||
if (s->mtfbase[0] == 0) {
|
||||
kk = MTFA_SIZE-1;
|
||||
for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
|
||||
for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
|
||||
s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
|
||||
kk--;
|
||||
}
|
||||
s->mtfbase[ii] = kk + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*-- end uc = MTF ( nextSym-1 ) --*/
|
||||
|
||||
s->unzftab[s->seqToUnseq[uc]]++;
|
||||
if (s->smallDecompress)
|
||||
s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
|
||||
s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]);
|
||||
nblock++;
|
||||
|
||||
GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we know what nblock is, we can do a better sanity
|
||||
check on s->origPtr.
|
||||
*/
|
||||
if (s->origPtr < 0 || s->origPtr >= nblock)
|
||||
RETURN(BZ_DATA_ERROR);
|
||||
|
||||
/*-- Set up cftab to facilitate generation of T^(-1) --*/
|
||||
s->cftab[0] = 0;
|
||||
for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
|
||||
for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
|
||||
for (i = 0; i <= 256; i++) {
|
||||
if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
|
||||
/* s->cftab[i] can legitimately be == nblock */
|
||||
RETURN(BZ_DATA_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
s->state_out_len = 0;
|
||||
s->state_out_ch = 0;
|
||||
BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
|
||||
s->state = BZ_X_OUTPUT;
|
||||
if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
|
||||
|
||||
if (s->smallDecompress) {
|
||||
|
||||
/*-- Make a copy of cftab, used in generation of T --*/
|
||||
for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
|
||||
|
||||
/*-- compute the T vector --*/
|
||||
for (i = 0; i < nblock; i++) {
|
||||
uc = (UChar)(s->ll16[i]);
|
||||
SET_LL(i, s->cftabCopy[uc]);
|
||||
s->cftabCopy[uc]++;
|
||||
}
|
||||
|
||||
/*-- Compute T^(-1) by pointer reversal on T --*/
|
||||
i = s->origPtr;
|
||||
j = GET_LL(i);
|
||||
do {
|
||||
Int32 tmp = GET_LL(j);
|
||||
SET_LL(j, i);
|
||||
i = j;
|
||||
j = tmp;
|
||||
}
|
||||
while (i != s->origPtr);
|
||||
|
||||
s->tPos = s->origPtr;
|
||||
s->nblock_used = 0;
|
||||
if (s->blockRandomised) {
|
||||
BZ_RAND_INIT_MASK;
|
||||
BZ_GET_SMALL(s->k0); s->nblock_used++;
|
||||
BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
|
||||
} else {
|
||||
BZ_GET_SMALL(s->k0); s->nblock_used++;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/*-- compute the T^(-1) vector --*/
|
||||
for (i = 0; i < nblock; i++) {
|
||||
uc = (UChar)(s->tt[i] & 0xff);
|
||||
s->tt[s->cftab[uc]] |= (i << 8);
|
||||
s->cftab[uc]++;
|
||||
}
|
||||
|
||||
s->tPos = s->tt[s->origPtr] >> 8;
|
||||
s->nblock_used = 0;
|
||||
if (s->blockRandomised) {
|
||||
BZ_RAND_INIT_MASK;
|
||||
BZ_GET_FAST(s->k0); s->nblock_used++;
|
||||
BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
|
||||
} else {
|
||||
BZ_GET_FAST(s->k0); s->nblock_used++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RETURN(BZ_OK);
|
||||
|
||||
|
||||
|
||||
endhdr_2:
|
||||
|
||||
GET_UCHAR(BZ_X_ENDHDR_2, uc);
|
||||
if (uc != 0x72) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_ENDHDR_3, uc);
|
||||
if (uc != 0x45) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_ENDHDR_4, uc);
|
||||
if (uc != 0x38) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_ENDHDR_5, uc);
|
||||
if (uc != 0x50) RETURN(BZ_DATA_ERROR);
|
||||
GET_UCHAR(BZ_X_ENDHDR_6, uc);
|
||||
if (uc != 0x90) RETURN(BZ_DATA_ERROR);
|
||||
|
||||
s->storedCombinedCRC = 0;
|
||||
GET_UCHAR(BZ_X_CCRC_1, uc);
|
||||
s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_CCRC_2, uc);
|
||||
s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_CCRC_3, uc);
|
||||
s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
|
||||
GET_UCHAR(BZ_X_CCRC_4, uc);
|
||||
s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
|
||||
|
||||
s->state = BZ_X_IDLE;
|
||||
RETURN(BZ_STREAM_END);
|
||||
|
||||
default: AssertH ( False, 4001 );
|
||||
}
|
||||
|
||||
AssertH ( False, 4002 );
|
||||
|
||||
save_state_and_return:
|
||||
|
||||
s->save_i = i;
|
||||
s->save_j = j;
|
||||
s->save_t = t;
|
||||
s->save_alphaSize = alphaSize;
|
||||
s->save_nGroups = nGroups;
|
||||
s->save_nSelectors = nSelectors;
|
||||
s->save_EOB = EOB;
|
||||
s->save_groupNo = groupNo;
|
||||
s->save_groupPos = groupPos;
|
||||
s->save_nextSym = nextSym;
|
||||
s->save_nblockMAX = nblockMAX;
|
||||
s->save_nblock = nblock;
|
||||
s->save_es = es;
|
||||
s->save_N = N;
|
||||
s->save_curr = curr;
|
||||
s->save_zt = zt;
|
||||
s->save_zn = zn;
|
||||
s->save_zvec = zvec;
|
||||
s->save_zj = zj;
|
||||
s->save_gSel = gSel;
|
||||
s->save_gMinlen = gMinlen;
|
||||
s->save_gLimit = gLimit;
|
||||
s->save_gBase = gBase;
|
||||
s->save_gPerm = gPerm;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end decompress.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,205 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Huffman coding low-level stuff ---*/
|
||||
/*--- huffman.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#include "bzlib_private.h"
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
#define WEIGHTOF(zz0) ((zz0) & 0xffffff00)
|
||||
#define DEPTHOF(zz1) ((zz1) & 0x000000ff)
|
||||
#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
|
||||
|
||||
#define ADDWEIGHTS(zw1,zw2) \
|
||||
(WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \
|
||||
(1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
|
||||
|
||||
#define UPHEAP(z) \
|
||||
{ \
|
||||
Int32 zz, tmp; \
|
||||
zz = z; tmp = heap[zz]; \
|
||||
while (weight[tmp] < weight[heap[zz >> 1]]) { \
|
||||
heap[zz] = heap[zz >> 1]; \
|
||||
zz >>= 1; \
|
||||
} \
|
||||
heap[zz] = tmp; \
|
||||
}
|
||||
|
||||
#define DOWNHEAP(z) \
|
||||
{ \
|
||||
Int32 zz, yy, tmp; \
|
||||
zz = z; tmp = heap[zz]; \
|
||||
while (True) { \
|
||||
yy = zz << 1; \
|
||||
if (yy > nHeap) break; \
|
||||
if (yy < nHeap && \
|
||||
weight[heap[yy+1]] < weight[heap[yy]]) \
|
||||
yy++; \
|
||||
if (weight[tmp] < weight[heap[yy]]) break; \
|
||||
heap[zz] = heap[yy]; \
|
||||
zz = yy; \
|
||||
} \
|
||||
heap[zz] = tmp; \
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
void BZ2_hbMakeCodeLengths ( UChar *len,
|
||||
Int32 *freq,
|
||||
Int32 alphaSize,
|
||||
Int32 maxLen )
|
||||
{
|
||||
/*--
|
||||
Nodes and heap entries run from 1. Entry 0
|
||||
for both the heap and nodes is a sentinel.
|
||||
--*/
|
||||
Int32 nNodes, nHeap, n1, n2, i, j, k;
|
||||
Bool tooLong;
|
||||
|
||||
Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ];
|
||||
Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ];
|
||||
Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ];
|
||||
|
||||
for (i = 0; i < alphaSize; i++)
|
||||
weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
|
||||
|
||||
while (True) {
|
||||
|
||||
nNodes = alphaSize;
|
||||
nHeap = 0;
|
||||
|
||||
heap[0] = 0;
|
||||
weight[0] = 0;
|
||||
parent[0] = -2;
|
||||
|
||||
for (i = 1; i <= alphaSize; i++) {
|
||||
parent[i] = -1;
|
||||
nHeap++;
|
||||
heap[nHeap] = i;
|
||||
UPHEAP(nHeap);
|
||||
}
|
||||
|
||||
AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 );
|
||||
|
||||
while (nHeap > 1) {
|
||||
n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
|
||||
n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
|
||||
nNodes++;
|
||||
parent[n1] = parent[n2] = nNodes;
|
||||
weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
|
||||
parent[nNodes] = -1;
|
||||
nHeap++;
|
||||
heap[nHeap] = nNodes;
|
||||
UPHEAP(nHeap);
|
||||
}
|
||||
|
||||
AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 );
|
||||
|
||||
tooLong = False;
|
||||
for (i = 1; i <= alphaSize; i++) {
|
||||
j = 0;
|
||||
k = i;
|
||||
while (parent[k] >= 0) { k = parent[k]; j++; }
|
||||
len[i-1] = j;
|
||||
if (j > maxLen) tooLong = True;
|
||||
}
|
||||
|
||||
if (! tooLong) break;
|
||||
|
||||
/* 17 Oct 04: keep-going condition for the following loop used
|
||||
to be 'i < alphaSize', which missed the last element,
|
||||
theoretically leading to the possibility of the compressor
|
||||
looping. However, this count-scaling step is only needed if
|
||||
one of the generated Huffman code words is longer than
|
||||
maxLen, which up to and including version 1.0.2 was 20 bits,
|
||||
which is extremely unlikely. In version 1.0.3 maxLen was
|
||||
changed to 17 bits, which has minimal effect on compression
|
||||
ratio, but does mean this scaling step is used from time to
|
||||
time, enough to verify that it works.
|
||||
|
||||
This means that bzip2-1.0.3 and later will only produce
|
||||
Huffman codes with a maximum length of 17 bits. However, in
|
||||
order to preserve backwards compatibility with bitstreams
|
||||
produced by versions pre-1.0.3, the decompressor must still
|
||||
handle lengths of up to 20. */
|
||||
|
||||
for (i = 1; i <= alphaSize; i++) {
|
||||
j = weight[i] >> 8;
|
||||
j = 1 + (j / 2);
|
||||
weight[i] = j << 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
void BZ2_hbAssignCodes ( Int32 *code,
|
||||
UChar *length,
|
||||
Int32 minLen,
|
||||
Int32 maxLen,
|
||||
Int32 alphaSize )
|
||||
{
|
||||
Int32 n, vec, i;
|
||||
|
||||
vec = 0;
|
||||
for (n = minLen; n <= maxLen; n++) {
|
||||
for (i = 0; i < alphaSize; i++)
|
||||
if (length[i] == n) { code[i] = vec; vec++; };
|
||||
vec <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------*/
|
||||
void BZ2_hbCreateDecodeTables ( Int32 *limit,
|
||||
Int32 *base,
|
||||
Int32 *perm,
|
||||
UChar *length,
|
||||
Int32 minLen,
|
||||
Int32 maxLen,
|
||||
Int32 alphaSize )
|
||||
{
|
||||
Int32 pp, i, j, vec;
|
||||
|
||||
pp = 0;
|
||||
for (i = minLen; i <= maxLen; i++)
|
||||
for (j = 0; j < alphaSize; j++)
|
||||
if (length[j] == i) { perm[pp] = j; pp++; };
|
||||
|
||||
for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
|
||||
for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
|
||||
|
||||
for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
|
||||
|
||||
for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
|
||||
vec = 0;
|
||||
|
||||
for (i = minLen; i <= maxLen; i++) {
|
||||
vec += (base[i+1] - base[i]);
|
||||
limit[i] = vec-1;
|
||||
vec <<= 1;
|
||||
}
|
||||
for (i = minLen + 1; i <= maxLen; i++)
|
||||
base[i] = ((limit[i-1] + 1) << 1) - base[i];
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end huffman.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Table for randomising repetitive blocks ---*/
|
||||
/*--- randtable.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.0.5 of 10 December 2007
|
||||
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#include "bzlib_private.h"
|
||||
|
||||
|
||||
/*---------------------------------------------*/
|
||||
Int32 BZ2_rNums[512] = {
|
||||
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
|
||||
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
|
||||
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
|
||||
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
|
||||
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
|
||||
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
|
||||
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
|
||||
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
|
||||
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
|
||||
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
|
||||
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
|
||||
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
|
||||
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
|
||||
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
|
||||
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
|
||||
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
|
||||
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
|
||||
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
|
||||
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
|
||||
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
|
||||
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
|
||||
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
|
||||
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
|
||||
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
|
||||
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
|
||||
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
|
||||
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
|
||||
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
|
||||
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
|
||||
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
|
||||
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
|
||||
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
|
||||
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
|
||||
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
|
||||
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
|
||||
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
|
||||
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
|
||||
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
|
||||
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
|
||||
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
|
||||
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
|
||||
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
|
||||
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
|
||||
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
|
||||
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
|
||||
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
|
||||
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
|
||||
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
|
||||
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
|
||||
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
|
||||
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
|
||||
936, 638
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end randtable.c ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user