aboutsummaryrefslogtreecommitdiff
path: root/dep/StormLib/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2012-07-05 14:16:44 +0200
committerShauren <shauren.trinity@gmail.com>2012-07-05 14:16:44 +0200
commit32ba32c4ebe56ba931c8638460c24cd57ae29a75 (patch)
tree2f757648b85ec8989a7dfc573b954b9b4d718650 /dep/StormLib/src
parentc95905ddbb22e2b5b5362b790aa851ef10d4e27e (diff)
parented6f3e2deff55f913f9646db5f540b7704088478 (diff)
Merge branch '4.x' of github.com:TrinityCore/TrinityCore into 4.3.4
Diffstat (limited to 'dep/StormLib/src')
-rw-r--r--dep/StormLib/src/FileStream.cpp2525
-rw-r--r--dep/StormLib/src/FileStream.h189
-rw-r--r--dep/StormLib/src/SBaseCommon.cpp64
-rw-r--r--dep/StormLib/src/SBaseDumpData.cpp70
-rw-r--r--dep/StormLib/src/SBaseFileTable.cpp611
-rw-r--r--dep/StormLib/src/SCompression.cpp259
-rw-r--r--dep/StormLib/src/SFileAddFile.cpp139
-rw-r--r--dep/StormLib/src/SFileAttributes.cpp84
-rw-r--r--dep/StormLib/src/SFileCompactArchive.cpp46
-rw-r--r--dep/StormLib/src/SFileCreateArchive.cpp69
-rw-r--r--dep/StormLib/src/SFileExtractFile.cpp2
-rw-r--r--dep/StormLib/src/SFileFindFile.cpp10
-rw-r--r--dep/StormLib/src/SFileListFile.cpp7
-rw-r--r--dep/StormLib/src/SFileOpenArchive.cpp91
-rw-r--r--dep/StormLib/src/SFileOpenFileEx.cpp51
-rw-r--r--dep/StormLib/src/SFilePatchArchives.cpp6
-rw-r--r--dep/StormLib/src/SFileReadFile.cpp108
-rw-r--r--dep/StormLib/src/SFileVerify.cpp81
-rw-r--r--dep/StormLib/src/StormCommon.h12
-rw-r--r--dep/StormLib/src/StormLib.h629
-rw-r--r--dep/StormLib/src/StormPort.h48
-rw-r--r--dep/StormLib/src/adpcm/adpcm.cpp31
-rw-r--r--dep/StormLib/src/huffman/huff.cpp20
-rw-r--r--dep/StormLib/src/huffman/huff.h2
-rw-r--r--dep/StormLib/src/lzma/info.txt1
-rw-r--r--dep/StormLib/src/sparse/sparse.cpp4
26 files changed, 3121 insertions, 2038 deletions
diff --git a/dep/StormLib/src/FileStream.cpp b/dep/StormLib/src/FileStream.cpp
index b8de102cd2e..413f2acb3a2 100644
--- a/dep/StormLib/src/FileStream.cpp
+++ b/dep/StormLib/src/FileStream.cpp
@@ -16,6 +16,11 @@
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
+#include "FileStream.h"
+
+#ifdef _MSC_VER
+#pragma comment(lib, "wininet.lib")
+#endif
//-----------------------------------------------------------------------------
// Local defines
@@ -25,77 +30,11 @@
#endif
#ifdef _MSC_VER
-#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
+#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
#endif
//-----------------------------------------------------------------------------
-// Local structures
-
-// Structure describing the PART file header
-typedef struct _PART_FILE_HEADER
-{
- DWORD PartialVersion; // Always set to 2
- char GameBuildNumber[8]; // Minimum build number of the game that can use this MPQ
- DWORD Unknown0C;
- DWORD Unknown10;
- DWORD Unknown14; // Often contains 0x1C (size of the rest of the header ?)
- DWORD Unknown18;
- DWORD ZeroValue1C; // Seems to always be zero
- DWORD ZeroValue20; // Seems to always be zero
- DWORD ZeroValue24; // Seems to always be zero
- DWORD FileSizeLo; // Low 32 bits of the file size
- DWORD FileSizeHi; // High 32 bits of the 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 Unknown0C;
- DWORD Unknown10;
-
-} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
-
-struct TPartFileStream : 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
-
- PART_FILE_MAP_ENTRY PartMap[1]; // File map, variable length
-};
-
-#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
-
-struct TEncryptedStream : public TFileStream
-{
- BYTE Key[MPQE_CHUNK_SIZE]; // File key
-};
-
-static bool IsPartHeader(PPART_FILE_HEADER pPartHdr)
-{
- // Version number must be 2
- if(pPartHdr->PartialVersion == 2)
- {
- // GameBuildNumber must be anm ASCII number
- if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2]))
- {
- // Block size must be power of 2
- if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0)
- return true;
- }
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Non-Windows support for LastError
+// Local functions - platform-specific functions
#ifndef PLATFORM_WINDOWS
static int nLastError = ERROR_SUCCESS;
@@ -111,639 +50,1087 @@ void SetLastError(int nError)
}
#endif
-//-----------------------------------------------------------------------------
-// Local functions - platform-specific functions
-
#ifndef PLATFORM_LITTLE_ENDIAN
void ConvertPartHeader(void * partHeader)
{
PPART_FILE_HEADER theHeader = (PPART_FILE_HEADER)partHeader;
theHeader->PartialVersion = SwapUInt32(theHeader->PartialVersion);
- theHeader->Unknown0C = SwapUInt32(theHeader->Unknown0C);
- theHeader->Unknown10 = SwapUInt32(theHeader->Unknown10);
- theHeader->Unknown14 = SwapUInt32(theHeader->Unknown14);
- theHeader->Unknown18 = SwapUInt32(theHeader->Unknown18);
- theHeader->Unknown1C = SwapUInt32(theHeader->Unknown1C);
- theHeader->Unknown20 = SwapUInt32(theHeader->Unknown20);
- theHeader->ZeroValue = SwapUInt32(theHeader->ZeroValue);
+ theHeader->Flags = SwapUInt32(theHeader->Flags);
theHeader->FileSizeLo = SwapUInt32(theHeader->FileSizeLo);
theHeader->FileSizeHi = SwapUInt32(theHeader->FileSizeHi);
theHeader->BlockSize = SwapUInt32(theHeader->BlockSize);
}
#endif
-#ifdef PLATFORM_MAC
-static void ConvertUTCDateTimeToFileTime(const UTCDateTimePtr inTime, ULONGLONG * pFT)
-{
- UInt64 intTime = ((UInt64)inTime->highSeconds << 32) + inTime->lowSeconds;
- intTime *= 10000000;
- intTime += 0x0153b281e0fb4000ull;
+//-----------------------------------------------------------------------------
+// Preparing file bitmap for a complete file of a given size
- *pFT = intTime;
-}
+#define DEFAULT_BLOCK_SIZE 0x4000
-static OSErr FSOpenDFCompat(FSRef *ref, char permission, short *refNum)
+static bool Dummy_GetBitmap(
+ TFileStream * pStream,
+ TFileBitmap * pBitmap,
+ DWORD Length,
+ LPDWORD LengthNeeded)
{
- HFSUniStr255 forkName;
- OSErr theErr;
- Boolean isFolder, wasChanged;
-
- theErr = FSResolveAliasFile(ref, true, &isFolder, &wasChanged);
- if (theErr != noErr)
+ ULONGLONG FileSize = 0;
+ DWORD TotalLength;
+ DWORD BlockCount;
+ DWORD BitmapSize;
+ DWORD LastByte;
+ bool bResult = false;
+
+ // Get file size and calculate bitmap length
+ FileStream_GetSize(pStream, FileSize);
+ BlockCount = (DWORD)(((FileSize - 1) / DEFAULT_BLOCK_SIZE) + 1);
+ BitmapSize = (DWORD)(((BlockCount - 1) / 8) + 1);
+
+ // Calculate and give the total length
+ TotalLength = sizeof(TFileBitmap) + BitmapSize;
+ if(LengthNeeded != NULL)
+ *LengthNeeded = TotalLength;
+
+ // Has the caller given enough space for storing the structure?
+ if(Length >= sizeof(TFileBitmap))
{
- return theErr;
+ memset(pBitmap, 0, sizeof(TFileBitmap));
+ pBitmap->EndOffset = FileSize;
+ pBitmap->IsComplete = 1;
+ pBitmap->BitmapSize = BitmapSize;
+ pBitmap->BlockSize = DEFAULT_BLOCK_SIZE;
+ bResult = true;
}
-
- FSGetDataForkName(&forkName);
-#ifdef PLATFORM_64BIT
- theErr = FSOpenFork(ref, forkName.length, forkName.unicode, permission, (FSIORefNum *)refNum);
-#else
- theErr = FSOpenFork(ref, forkName.length, forkName.unicode, permission, refNum);
-#endif
- return theErr;
-}
-#endif
-#ifdef PLATFORM_LINUX
-// time_t is number of seconds since 1.1.1970, UTC.
-// 1 second = 10000000 (decimal) in FILETIME
-static void ConvertTimeTToFileTime(ULONGLONG * pFileTime, time_t crt_time)
-{
- // Set the start to 1.1.1970 00:00:00
- *pFileTime = 0x019DB1DED53E8000ULL + (10000000 * crt_time);
+ // Do we have enough space to fill the bitmap as well?
+ if(Length >= TotalLength)
+ {
+ LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1);
+
+ // Fill the full blocks
+ memset(pbBitmap, 0xFF, (BlockCount / 8));
+ pbBitmap += (BlockCount / 8);
+ bResult = true;
+
+ // Supply the last block
+ if(BlockCount & 7)
+ {
+ LastByte = (1 << (BlockCount & 7)) - 1;
+ pbBitmap[0] = (BYTE)LastByte;
+ }
+ }
+
+ return bResult;
}
-#endif
-static HANDLE CreateNewFile(
- const TCHAR * szFileName) // Name of the file to open
+//-----------------------------------------------------------------------------
+// Local functions - base file support
+
+static bool BaseFile_Read(
+ 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
{
- HANDLE hFile = INVALID_HANDLE_VALUE; // Pre-set the file handle to INVALID_HANDLE_VALUE
+ ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
+ DWORD dwBytesRead = 0; // Must be set by platform-specific code
#ifdef PLATFORM_WINDOWS
{
- DWORD dwShareMode = FILE_SHARE_READ;
-
- if(dwGlobalFlags & SFILE_FLAG_ALLOW_WRITE_SHARE)
- dwShareMode |= FILE_SHARE_WRITE;
-
- hFile = CreateFile(szFileName,
- GENERIC_READ | GENERIC_WRITE,
- dwShareMode,
- NULL,
- CREATE_ALWAYS,
- 0,
- NULL);
- }
-#endif
+ // Note: StormLib no longer supports Windows 9x.
+ // Thus, we can use the OVERLAPPED structure to specify
+ // file offset to read from file. This allows us to skip
+ // one system call to SetFilePointer
-#ifdef PLATFORM_MAC
- {
- FSRef theParentRef;
- FSRef theFileRef;
- OSErr theErr;
- short fileRef;
-
- theErr = FSPathMakeRef((const UInt8 *)szFileName, &theFileRef, NULL);
-
- if (theErr == noErr)
- FSDeleteObject(&theFileRef);
-
- // Create the FSRef for the parent directory.
- UInt8 folderName[MAX_PATH];
- memset(&theFileRef, 0, sizeof(FSRef));
- CFStringRef filePathCFString = CFStringCreateWithCString(NULL, szFileName, kCFStringEncodingUTF8);
- CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, filePathCFString, kCFURLPOSIXPathStyle, false);
- CFURLRef folderURL = CFURLCreateCopyDeletingLastPathComponent(NULL, fileURL);
- CFURLGetFileSystemRepresentation(folderURL, true, folderName, MAX_PATH);
- theErr = FSPathMakeRef(folderName, &theParentRef, NULL);
- CFRelease(fileURL);
- CFRelease(folderURL);
-
- if (theErr != noErr)
+ // Update the byte offset
+ pStream->Base.File.FilePos = ByteOffset;
+
+ // Read the data
+ if(dwBytesToRead != 0)
{
- nLastError = theErr;
- return INVALID_HANDLE_VALUE;
+ OVERLAPPED Overlapped;
+
+ Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32);
+ Overlapped.Offset = (DWORD)ByteOffset;
+ Overlapped.hEvent = NULL;
+ if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped))
+ return false;
}
-
- // Create the file
- UniChar unicodeFileName[256];
- fileURL = CFURLCreateWithFileSystemPath(NULL, filePathCFString, kCFURLPOSIXPathStyle, false);
- CFStringRef fileNameCFString = CFURLCopyLastPathComponent(fileURL);
- CFStringGetCharacters(fileNameCFString, CFRangeMake(0, CFStringGetLength(fileNameCFString)),
- unicodeFileName);
- theErr = FSCreateFileUnicode(&theParentRef, CFStringGetLength(fileNameCFString), unicodeFileName,
- kFSCatInfoNone, NULL, &theFileRef, NULL);
- CFRelease(fileNameCFString);
- CFRelease(filePathCFString);
- CFRelease(fileURL);
- if (theErr != noErr)
+/*
+ // If the byte offset is different from the current file position,
+ // we have to update the file position
+ if(ByteOffset != pStream->Base.File.FilePos)
{
- nLastError = theErr;
- return INVALID_HANDLE_VALUE;
+ LONG ByteOffsetHi = (LONG)(ByteOffset >> 32);
+
+ SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN);
+ pStream->Base.File.FilePos = ByteOffset;
}
- theErr = FSOpenDFCompat(&theFileRef, fsRdWrPerm, &fileRef);
- if(theErr != noErr)
+ // Read the data
+ if(dwBytesToRead != 0)
{
- nLastError = theErr;
- return INVALID_HANDLE_VALUE;
+ if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL))
+ return false;
}
-
- hFile = (HANDLE)(int)fileRef;
+*/
}
#endif
-#ifdef PLATFORM_LINUX
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
{
- intptr_t handle;
+ ssize_t bytes_read;
- handle = open(szFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if(handle == -1)
+ // If the byte offset is different from the current file position,
+ // we have to update the file position
+ if(ByteOffset != pStream->Base.File.FilePos)
{
- nLastError = errno;
- return INVALID_HANDLE_VALUE;
+ lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET);
+ pStream->Base.File.FilePos = ByteOffset;
}
- hFile = (HANDLE)handle;
+ // Perform the read operation
+ if(dwBytesToRead != 0)
+ {
+ bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead);
+ if(bytes_read == -1)
+ {
+ nLastError = errno;
+ return false;
+ }
+
+ dwBytesRead = (DWORD)(size_t)bytes_read;
+ }
}
#endif
- // Return the file handle
- return hFile;
+ // Increment the current file position by number of bytes read
+ // If the number of bytes read doesn't match to required amount, return false
+ pStream->Base.File.FilePos = ByteOffset + dwBytesRead;
+ if(dwBytesRead != dwBytesToRead)
+ SetLastError(ERROR_HANDLE_EOF);
+ return (dwBytesRead == dwBytesToRead);
}
-static HANDLE OpenExistingFile(
- const TCHAR * szFileName, // Name of the file to open
- bool bWriteAccess) // false = read-only, true = read/write
+/**
+ * \a pStream Pointer to an open stream
+ * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position
+ * \a pvBuffer Pointer to data to be written
+ * \a dwBytesToWrite Number of bytes to write to the file
+ */
+
+static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)
{
- HANDLE hFile = INVALID_HANDLE_VALUE; // Pre-set the file handle to INVALID_HANDLE_VALUE
+ ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
+ DWORD dwBytesWritten = 0; // Must be set by platform-specific code
#ifdef PLATFORM_WINDOWS
{
- DWORD dwShareMode = FILE_SHARE_READ;
-
- if(dwGlobalFlags & SFILE_FLAG_ALLOW_WRITE_SHARE)
- dwShareMode |= FILE_SHARE_WRITE;
-
- hFile = CreateFile(szFileName,
- bWriteAccess ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
- dwShareMode,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
- }
-#endif
+ // Note: StormLib no longer supports Windows 9x.
+ // Thus, we can use the OVERLAPPED structure to specify
+ // file offset to read from file. This allows us to skip
+ // one system call to SetFilePointer
-#ifdef PLATFORM_MAC
- {
- FSRef theFileRef;
- OSErr theErr;
- short fileRef;
- char permission = bWriteAccess ? fsRdWrPerm : fsRdPerm;
+ // Update the byte offset
+ pStream->Base.File.FilePos = ByteOffset;
- theErr = FSPathMakeRef((const UInt8 *)szFileName, &theFileRef, NULL);
- if(theErr != noErr)
+ // Read the data
+ if(dwBytesToWrite != 0)
{
- nLastError = theErr;
- return INVALID_HANDLE_VALUE;
- }
+ OVERLAPPED Overlapped;
- theErr = FSOpenDFCompat(&theFileRef, permission, &fileRef);
- if (theErr != noErr)
+ Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32);
+ Overlapped.Offset = (DWORD)ByteOffset;
+ Overlapped.hEvent = NULL;
+ if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped))
+ return false;
+ }
+/*
+ // If the byte offset is different from the current file position,
+ // we have to update the file position
+ if(ByteOffset != pStream->Base.File.FilePos)
{
- nLastError = theErr;
- return INVALID_HANDLE_VALUE;
+ LONG ByteOffsetHi = (LONG)(ByteOffset >> 32);
+
+ SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN);
+ pStream->Base.File.FilePos = ByteOffset;
}
- hFile = (HANDLE)(int)fileRef;
+ // Read the data
+ if(dwBytesToWrite != 0)
+ {
+ if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL))
+ return false;
+ }
+*/
}
#endif
-#ifdef PLATFORM_LINUX
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
{
- int oflag = bWriteAccess ? O_RDWR : O_RDONLY;
- intptr_t handle;
+ ssize_t bytes_written;
- handle = open(szFileName, oflag | O_LARGEFILE);
- if(handle == -1)
+ // If the byte offset is different from the current file position,
+ // we have to update the file position
+ if(ByteOffset != pStream->Base.File.FilePos)
{
- nLastError = errno;
- return INVALID_HANDLE_VALUE;
+ lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET);
+ pStream->Base.File.FilePos = ByteOffset;
}
- hFile = (HANDLE)handle;
+ // Perform the read operation
+ bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite);
+ if(bytes_written == -1)
+ {
+ nLastError = errno;
+ return false;
+ }
+
+ dwBytesWritten = (DWORD)(size_t)bytes_written;
}
#endif
- // Return the file handle
- return hFile;
+ // Increment the current file position by number of bytes read
+ pStream->Base.File.FilePos = ByteOffset + dwBytesWritten;
+
+ // Also modify the file size, if needed
+ if(pStream->Base.File.FilePos > pStream->Base.File.FileSize)
+ pStream->Base.File.FileSize = pStream->Base.File.FilePos;
+
+ if(dwBytesWritten != dwBytesToWrite)
+ SetLastError(ERROR_DISK_FULL);
+ return (dwBytesWritten == dwBytesToWrite);
}
-static void CloseTheFile(HANDLE hFile)
+static bool BaseFile_GetPos(
+ TFileStream * pStream, // Pointer to an open stream
+ ULONGLONG & ByteOffset) // Pointer to file byte offset
{
-#ifdef PLATFORM_WINDOWS
- CloseHandle(hFile);
-#endif
-
-#ifdef PLATFORM_MAC
- FSCloseFork((short)(long)hFile);
-#endif
+ ByteOffset = pStream->Base.File.FilePos;
+ return true;
+}
-#ifdef PLATFORM_LINUX
- close((intptr_t)hFile);
-#endif
+static bool BaseFile_GetSize(
+ TFileStream * pStream, // Pointer to an open stream
+ ULONGLONG & FileSize) // Pointer where to store file size
+{
+ FileSize = pStream->Base.File.FileSize;
+ return true;
}
/**
- * Renames a file to another name.
- * Note that the "szNewFile" file usually exists when this function is called,
- * so the function must deal with it properly
+ * \a pStream Pointer to an open stream
+ * \a NewFileSize New size of the file
*/
-static bool RenameFile(const TCHAR * szExistingFile, const TCHAR * szNewFile)
+static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)
{
#ifdef PLATFORM_WINDOWS
- // Delete the original stream file. Don't check the result value,
- // because if the file doesn't exist, it would fail
- DeleteFile(szNewFile);
+ {
+ LONG FileSizeHi = (LONG)(NewFileSize >> 32);
+ LONG FileSizeLo;
+ DWORD dwNewPos;
+ bool bResult;
- // Rename the new file to the old stream's file
- return (bool)MoveFile(szExistingFile, szNewFile);
-#endif
+ // Set the position at the new file size
+ dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN);
+ if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS)
+ return false;
-#ifdef PLATFORM_MAC
- OSErr theErr;
- FSRef fromFileRef;
- FSRef toFileRef;
-
- if (FSPathMakeRef((const UInt8 *)szNewFile, &toFileRef, NULL) == noErr)
- FSDeleteObject(&toFileRef);
-
- // Get the path to the old file
- theErr = FSPathMakeRef((const UInt8 *)szExistingFile, &fromFileRef, NULL);
- if (theErr != noErr)
- {
- nLastError = theErr;
- return false;
+ // Set the current file pointer as the end of the file
+ bResult = (bool)SetEndOfFile(pStream->Base.File.hFile);
+
+ // Restore the file position
+ FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32);
+ FileSizeLo = (LONG)(pStream->Base.File.FilePos);
+ SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN);
+ return bResult;
}
+#endif
- // Get a CFString for the new file name
- CFStringRef newFileNameCFString = CFStringCreateWithCString(NULL, szNewFile, kCFStringEncodingUTF8);
- CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, newFileNameCFString, kCFURLPOSIXPathStyle, false);
- CFRelease(newFileNameCFString);
- newFileNameCFString = CFURLCopyLastPathComponent(fileURL);
- CFRelease(fileURL);
-
- // Convert CFString to Unicode and rename the file
- UniChar unicodeFileName[256];
- CFStringGetCharacters(newFileNameCFString, CFRangeMake(0, CFStringGetLength(newFileNameCFString)),
- unicodeFileName);
- theErr = FSRenameUnicode(&fromFileRef, CFStringGetLength(newFileNameCFString), unicodeFileName,
- kTextEncodingUnknown, NULL);
- if (theErr != noErr)
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
{
- CFRelease(newFileNameCFString);
- nLastError = theErr;
- return false;
+ if(ftruncate((intptr_t)pStream->Base.File.hFile, (off_t)NewFileSize) == -1)
+ {
+ nLastError = errno;
+ return false;
+ }
+
+ return true;
}
-
- CFRelease(newFileNameCFString);
-
+#endif
+}
+
+static bool BaseFile_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)
+{
+ *pFileTime = pStream->Base.File.FileTime;
return true;
+}
+
+// Renames the file pointed by pStream so that it contains data from pNewStream
+static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream)
+{
+#ifdef PLATFORM_WINDOWS
+ // Delete the original stream file. Don't check the result value,
+ // because if the file doesn't exist, it would fail
+ DeleteFile(pStream->szFileName);
+
+ // Rename the new file to the old stream's file
+ return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName);
#endif
-#ifdef PLATFORM_LINUX
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
// "rename" on Linux also works if the target file exists
- if(rename(szExistingFile, szNewFile) == -1)
+ if(rename(pNewStream->szFileName, pStream->szFileName) == -1)
{
nLastError = errno;
return false;
}
-
+
return true;
#endif
}
-//-----------------------------------------------------------------------------
-// Stream functions - normal file stream
-
-static bool File_GetPos(
- TFileStream * pStream, // Pointer to an open stream
- ULONGLONG & ByteOffset) // Pointer to file byte offset
+static void BaseFile_Close(TFileStream * pStream)
{
- ByteOffset = pStream->RawFilePos;
- return true;
+ if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE)
+ {
+#ifdef PLATFORM_WINDOWS
+ CloseHandle(pStream->Base.File.hFile);
+#endif
+
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
+ close((intptr_t)pStream->Base.File.hFile);
+#endif
+ }
+
+ // Also invalidate the handle
+ pStream->Base.File.hFile = INVALID_HANDLE_VALUE;
}
-static bool File_Read(
- 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
+static bool BaseFile_Create(
+ TFileStream * pStream,
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
{
- DWORD dwBytesRead = 0; // Must be set by platform-specific code
+#ifdef PLATFORM_WINDOWS
+ {
+ DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0;
+
+ pStream->Base.File.hFile = CreateFile(szFileName,
+ GENERIC_READ | GENERIC_WRITE,
+ dwWriteShare | FILE_SHARE_READ,
+ NULL,
+ CREATE_ALWAYS,
+ 0,
+ NULL);
+ if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE)
+ return false;
+ }
+#endif
- // If the byte offset is not entered, use the current position
- if(pByteOffset == NULL)
- pByteOffset = &pStream->RawFilePos;
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
+ {
+ intptr_t handle;
+
+ handle = open(szFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if(handle == -1)
+ {
+ nLastError = errno;
+ return false;
+ }
+
+ pStream->Base.File.hFile = (HANDLE)handle;
+ }
+#endif
+
+ // Fill-in the entry points
+ pStream->BaseRead = BaseFile_Read;
+ pStream->BaseWrite = BaseFile_Write;
+ pStream->BaseGetPos = BaseFile_GetPos;
+ pStream->BaseGetSize = BaseFile_GetSize;
+ pStream->BaseSetSize = BaseFile_SetSize;
+ pStream->BaseSetSize = BaseFile_SetSize;
+ pStream->BaseGetTime = BaseFile_GetTime;
+ pStream->BaseClose = BaseFile_Close;
+
+ // Reset the file position
+ pStream->Base.File.FileSize = 0;
+ pStream->Base.File.FilePos = 0;
+ pStream->dwFlags = dwStreamFlags;
+ return true;
+}
+static bool BaseFile_Open(
+ TFileStream * pStream,
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
+{
#ifdef PLATFORM_WINDOWS
{
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
- {
- LONG ByteOffsetHi = (LONG)(*pByteOffset >> 32);
- LONG ByteOffsetLo = (LONG)(*pByteOffset);
+ ULARGE_INTEGER FileSize;
+ DWORD dwDesiredAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? GENERIC_READ : GENERIC_ALL;
+ DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0;
+
+ // Open the file
+ pStream->Base.File.hFile = CreateFile(szFileName,
+ dwDesiredAccess,
+ dwWriteShare | FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE)
+ return false;
- SetFilePointer(pStream->hFile, ByteOffsetLo, &ByteOffsetHi, FILE_BEGIN);
- pStream->RawFilePos = *pByteOffset;
- }
+ // Query the file size
+ FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart);
+ pStream->Base.File.FileSize = FileSize.QuadPart;
- // Read the data
- if(dwBytesToRead != 0)
- {
- if(!ReadFile(pStream->hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL))
- return false;
- }
+ // Query last write time
+ GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime);
}
#endif
-#ifdef PLATFORM_MAC
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
{
- ByteCount nBytesToRead = (ByteCount)dwBytesToRead;
- ByteCount nBytesRead = 0;
- OSErr theErr;
+ struct stat fileinfo;
+ int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR;
+ intptr_t handle;
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
+ // Open the file
+ handle = open(szFileName, oflag);
+ if(handle == -1)
{
- FSSetForkPosition((short)(long)pStream->hFile, fsFromStart, (SInt64)(*pByteOffset));
- pStream->RawFilePos = *pByteOffset;
+ nLastError = errno;
+ return false;
}
- // Read the data
- if(nBytesToRead != 0)
+ // Get the file size
+ if(fstat(handle, &fileinfo) == -1)
{
- theErr = FSReadFork((short)(long)pStream->hFile, fsAtMark, 0, nBytesToRead, pvBuffer, &nBytesRead);
- if (theErr != noErr && theErr != eofErr)
- {
- nLastError = theErr;
- return false;
- }
- dwBytesRead = (DWORD)nBytesRead;
+ nLastError = errno;
+ return false;
}
+
+ // time_t is number of seconds since 1.1.1970, UTC.
+ // 1 second = 10000000 (decimal) in FILETIME
+ // Set the start to 1.1.1970 00:00:00
+ pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
+ pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size;
+ pStream->Base.File.hFile = (HANDLE)handle;
}
#endif
-#ifdef PLATFORM_LINUX
- {
- ssize_t bytes_read;
+ // Fill-in the entry points
+ pStream->BaseRead = BaseFile_Read;
+ pStream->BaseWrite = BaseFile_Write;
+ pStream->BaseGetPos = BaseFile_GetPos;
+ pStream->BaseGetSize = BaseFile_GetSize;
+ pStream->BaseSetSize = BaseFile_SetSize;
+ pStream->BaseGetTime = BaseFile_GetTime;
+ pStream->BaseClose = BaseFile_Close;
+
+ // Reset the file position
+ pStream->Base.File.FilePos = 0;
+ pStream->dwFlags = dwStreamFlags;
+ return true;
+}
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
- {
- lseek64((intptr_t)pStream->hFile, (off64_t)(*pByteOffset), SEEK_SET);
- pStream->RawFilePos = *pByteOffset;
- }
+//-----------------------------------------------------------------------------
+// Local functions - base memory-mapped file support
- // Perform the read operation
- if(dwBytesToRead != 0)
- {
- bytes_read = read((intptr_t)pStream->hFile, pvBuffer, (size_t)dwBytesToRead);
- if(bytes_read == -1)
- {
- nLastError = errno;
- return false;
- }
+static bool BaseMap_Read(
+ 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
+{
+ ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos;
- dwBytesRead = (DWORD)(size_t)bytes_read;
- }
+ // Do we have to read anything at all?
+ if(dwBytesToRead != 0)
+ {
+ // Don't allow reading past file size
+ if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize)
+ return false;
+
+ // Copy the required data
+ memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead);
}
-#endif
- // Increment the current file position by number of bytes read
- // If the number of bytes read doesn't match to required amount, return false
- pStream->RawFilePos = *pByteOffset + dwBytesRead;
- if(dwBytesRead != dwBytesToRead)
- SetLastError(ERROR_HANDLE_EOF);
- return (dwBytesRead == dwBytesToRead);
+ // Move the current file position
+ pStream->Base.Map.FilePos += dwBytesToRead;
+ return true;
}
-/**
- * \a pStream Pointer to an open stream
- * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position
- * \a pvBuffer Pointer to data to be written
- * \a dwBytesToWrite Number of bytes to write to the file
- */
+static bool BaseMap_GetPos(
+ TFileStream * pStream, // Pointer to an open stream
+ ULONGLONG & ByteOffset) // Pointer to file byte offset
+{
+ ByteOffset = pStream->Base.Map.FilePos;
+ return true;
+}
-static bool File_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)
+static bool BaseMap_GetSize(
+ TFileStream * pStream, // Pointer to an open stream
+ ULONGLONG & FileSize) // Pointer where to store file size
{
- DWORD dwBytesWritten = 0; // Must be set by platform-specific code
+ FileSize = pStream->Base.Map.FileSize;
+ return true;
+}
- // If the byte offset is not entered, use the current position
- if(pByteOffset == NULL)
- pByteOffset = &pStream->RawFilePos;
+static bool BaseMap_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)
+{
+ *pFileTime = pStream->Base.Map.FileTime;
+ return true;
+}
+static void BaseMap_Close(TFileStream * pStream)
+{
+#ifdef PLATFORM_WINDOWS
+ if(pStream->Base.Map.pbFile != NULL)
+ UnmapViewOfFile(pStream->Base.Map.pbFile);
+#endif
+
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
+ if(pStream->Base.Map.pbFile != NULL)
+ munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize);
+#endif
+
+ pStream->Base.Map.pbFile = NULL;
+}
+
+static bool BaseMap_Open(
+ TFileStream * pStream,
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
+{
#ifdef PLATFORM_WINDOWS
+
+ ULARGE_INTEGER FileSize;
+ HANDLE hFile;
+ HANDLE hMap;
+ bool bResult = false;
+
+ // Open the file for read access
+ hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if(hFile != NULL)
{
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
+ // Retrieve file size. Don't allow mapping file of a zero size.
+ FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
+ if(FileSize.QuadPart != 0)
{
- LONG ByteOffsetHi = (LONG)(*pByteOffset >> 32);
- LONG ByteOffsetLo = (LONG)(*pByteOffset);
+ // Retrieve file time
+ GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime);
+
+ // Now create mapping object
+ hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if(hMap != NULL)
+ {
+ // Map the entire view into memory
+ // Note that this operation will fail if the file can't fit
+ // into usermode address space
+ pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
+ if(pStream->Base.Map.pbFile != NULL)
+ {
+ pStream->Base.Map.FileSize = FileSize.QuadPart;
+ pStream->Base.Map.FilePos = 0;
+ bResult = true;
+ }
- SetFilePointer(pStream->hFile, ByteOffsetLo, &ByteOffsetHi, FILE_BEGIN);
- pStream->RawFilePos = *pByteOffset;
+ // Close the map handle
+ CloseHandle(hMap);
+ }
}
- // Read the data
- if(!WriteFile(pStream->hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL))
- return false;
+ // Close the file handle
+ CloseHandle(hFile);
}
+
+ // If the file is not there and is not available for random access,
+ // report error
+ if(bResult == false)
+ return false;
#endif
-#ifdef PLATFORM_MAC
- {
- ByteCount nBytesToWrite = (ByteCount)dwBytesToWrite;
- ByteCount nBytesWritten = 0;
- OSErr theErr;
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
+ struct stat fileinfo;
+ intptr_t handle;
+ bool bResult = false;
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
+ // Open the file
+ handle = open(szFileName, O_RDONLY);
+ if(handle != -1)
+ {
+ // Get the file size
+ if(fstat(handle, &fileinfo) != -1)
{
- FSSetForkPosition((short)(long)pStream->hFile, fsFromStart, (SInt64)(*pByteOffset));
- pStream->RawFilePos = *pByteOffset;
+ pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0);
+ if(pStream->Base.Map.pbFile != NULL)
+ {
+ // time_t is number of seconds since 1.1.1970, UTC.
+ // 1 second = 10000000 (decimal) in FILETIME
+ // Set the start to 1.1.1970 00:00:00
+ pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
+ pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size;
+ pStream->Base.Map.FilePos = 0;
+ bResult = true;
+ }
}
+ close(handle);
+ }
- theErr = FSWriteFork((short)(long)pStream->hFile, fsAtMark, 0, nBytesToWrite, pvBuffer, &nBytesWritten);
- if (theErr != noErr)
- {
- nLastError = theErr;
- return false;
- }
- dwBytesWritten = (DWORD)nBytesWritten;
+ // Did the mapping fail?
+ if(bResult == false)
+ {
+ nLastError = errno;
+ return false;
}
#endif
-#ifdef PLATFORM_LINUX
+ // Fill-in entry points
+ pStream->BaseRead = BaseMap_Read;
+ pStream->BaseGetPos = BaseMap_GetPos;
+ pStream->BaseGetSize = BaseMap_GetSize;
+ pStream->BaseGetTime = BaseMap_GetTime;
+ pStream->BaseClose = BaseMap_Close;
+ pStream->dwFlags = dwStreamFlags;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Local functions - base HTTP file support
+
+static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR * szServerName)
+{
+ // Check for HTTP
+ if(!_tcsnicmp(szFileName, _T("http://"), 7))
+ szFileName += 7;
+
+ // Cut off the server name
+ if(szServerName != NULL)
{
- ssize_t bytes_written;
+ while(szFileName[0] != 0 && szFileName[0] != _T('/'))
+ *szServerName++ = *szFileName++;
+ *szServerName = 0;
+ }
+ else
+ {
+ while(szFileName[0] != 0 && szFileName[0] != _T('/'))
+ *szFileName++;
+ }
- // If the byte offset is different from the current file position,
- // we have to update the file position
- if(*pByteOffset != pStream->RawFilePos)
- {
- lseek64((intptr_t)pStream->hFile, (off64_t)(*pByteOffset), SEEK_SET);
- pStream->RawFilePos = *pByteOffset;
- }
+ // Return the remainder
+ return szFileName;
+}
- // Perform the read operation
- bytes_written = write((intptr_t)pStream->hFile, pvBuffer, (size_t)dwBytesToWrite);
- if(bytes_written == -1)
+static bool BaseHttp_Read(
+ 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
+{
+#ifdef PLATFORM_WINDOWS
+ ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos;
+ DWORD dwTotalBytesRead = 0;
+
+ // Do we have to read anything at all?
+ if(dwBytesToRead != 0)
+ {
+ HINTERNET hRequest;
+ LPCTSTR szFileName;
+ LPBYTE pbBuffer = (LPBYTE)pvBuffer;
+ TCHAR szRangeRequest[0x80];
+ DWORD dwStartOffset = (DWORD)ByteOffset;
+ DWORD dwEndOffset = dwStartOffset + dwBytesToRead;
+ BYTE Buffer[0x200];
+
+ // Open HTTP request to the file
+ szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL);
+ hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
+ if(hRequest != NULL)
{
- nLastError = errno;
- return false;
- }
+ // Add range request to the HTTP headers
+ // http://www.clevercomponents.com/articles/article015/resuming.asp
+ _stprintf(szRangeRequest, _T("Range: bytes=%d-%d"), dwStartOffset, dwEndOffset);
+ HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
- dwBytesWritten = (DWORD)(size_t)bytes_written;
+ // Send the request to the server
+ if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
+ {
+ while(dwTotalBytesRead < dwBytesToRead)
+ {
+ DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead;
+ DWORD dwBlockBytesRead = 0;
+
+ // Read the block from the file
+ if(dwBlockBytesToRead > sizeof(Buffer))
+ dwBlockBytesToRead = sizeof(Buffer);
+ InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead);
+
+ // Check for end
+ if(dwBlockBytesRead == 0)
+ break;
+
+ // Move buffers
+ dwTotalBytesRead += dwBlockBytesRead;
+ pbBuffer += dwBlockBytesRead;
+ }
+ }
+ InternetCloseHandle(hRequest);
+ }
}
-#endif
// Increment the current file position by number of bytes read
- pStream->RawFilePos = *pByteOffset + dwBytesWritten;
- if(dwBytesWritten != dwBytesToWrite)
- SetLastError(ERROR_DISK_FULL);
- return (dwBytesWritten == dwBytesToWrite);
+ pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead;
+
+ // If the number of bytes read doesn't match the required amount, return false
+ if(dwTotalBytesRead != dwBytesToRead)
+ SetLastError(ERROR_HANDLE_EOF);
+ return (dwTotalBytesRead == dwBytesToRead);
+
+#else
+
+ // Not supported
+ pStream = pStream;
+ pByteOffset = pByteOffset;
+ pvBuffer = pvBuffer;
+ dwBytesToRead = dwBytesToRead;
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return false;
+
+#endif
+}
+
+static bool BaseHttp_GetPos(
+ TFileStream * pStream, // Pointer to an open stream
+ ULONGLONG & ByteOffset) // Pointer to file byte offset
+{
+ ByteOffset = pStream->Base.Http.FilePos;
+ return true;
}
-static bool File_GetSize(
+static bool BaseHttp_GetSize(
TFileStream * pStream, // Pointer to an open stream
ULONGLONG & FileSize) // Pointer where to store file size
{
-#ifdef PLATFORM_WINDOWS
- DWORD FileSizeHi = 0;
- DWORD FileSizeLo;
-
- FileSizeLo = GetFileSize(pStream->hFile, &FileSizeHi);
- if(FileSizeLo == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS)
- return false;
+ FileSize = pStream->Base.Http.FileSize;
+ return true;
+}
- FileSize = MAKE_OFFSET64(FileSizeHi, FileSizeLo);
+static bool BaseHttp_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)
+{
+ *pFileTime = pStream->Base.Http.FileTime;
return true;
+}
+
+static void BaseHttp_Close(TFileStream * pStream)
+{
+#ifdef PLATFORM_WINDOWS
+ if(pStream->Base.Http.hConnect != NULL)
+ InternetCloseHandle(pStream->Base.Http.hConnect);
+ pStream->Base.Http.hConnect = NULL;
+
+ if(pStream->Base.Http.hInternet != NULL)
+ InternetCloseHandle(pStream->Base.Http.hInternet);
+ pStream->Base.Http.hInternet = NULL;
+#else
+ pStream = pStream;
#endif
+}
+
+static bool BaseHttp_Open(
+ TFileStream * pStream,
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
+{
+#ifdef PLATFORM_WINDOWS
+
+ HINTERNET hRequest;
+ DWORD dwTemp = 0;
+ bool bFileAvailable = false;
+ int nError = ERROR_SUCCESS;
-#ifdef PLATFORM_MAC
- SInt64 fileLength = 0;
- OSErr theErr;
+ // Don't connect to the internet
+ if(!InternetGetConnectedState(&dwTemp, 0))
+ nError = GetLastError();
- theErr = FSGetForkSize((short)(long)pStream->hFile, &fileLength);
- if(theErr != noErr)
+ // Initiate the connection to the internet
+ if(nError == ERROR_SUCCESS)
{
- nLastError = theErr;
- return false;
+ pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"),
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL,
+ NULL,
+ 0);
+ if(pStream->Base.Http.hInternet == NULL)
+ nError = GetLastError();
}
- FileSize = (ULONGLONG)fileLength;
- return true;
-#endif
+ // Connect to the server
+ if(nError == ERROR_SUCCESS)
+ {
+ TCHAR szServerName[MAX_PATH];
+ DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE;
+
+ // Initiate connection with the server
+ szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);
+ pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,
+ szServerName,
+ INTERNET_DEFAULT_HTTP_PORT,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ dwFlags,
+ 0);
+ if(pStream->Base.Http.hConnect == NULL)
+ nError = GetLastError();
+ }
-#ifdef PLATFORM_LINUX
- struct stat64 fileinfo;
+ // Now try to query the file size
+ if(nError == ERROR_SUCCESS)
+ {
+ // Open HTTP request to the file
+ hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
+ if(hRequest != NULL)
+ {
+ if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
+ {
+ ULONGLONG FileTime = 0;
+ DWORD dwFileSize = 0;
+ DWORD dwDataSize;
+ DWORD dwIndex = 0;
+
+ // Check if the MPQ has Last Modified field
+ dwDataSize = sizeof(ULONGLONG);
+ if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
+ pStream->Base.Http.FileTime = FileTime;
+
+ // Verify if the server supports random access
+ dwDataSize = sizeof(DWORD);
+ if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
+ {
+ if(dwFileSize != 0)
+ {
+ pStream->Base.Http.FileSize = dwFileSize;
+ pStream->Base.Http.FilePos = 0;
+ bFileAvailable = true;
+ }
+ }
+ }
+ InternetCloseHandle(hRequest);
+ }
+ }
- if(fstat64((intptr_t)pStream->hFile, &fileinfo) == -1)
+ // If the file is not there and is not available for random access,
+ // report error
+ if(bFileAvailable == false)
{
- nLastError = errno;
+ BaseHttp_Close(pStream);
return false;
}
- FileSize = (ULONGLONG)fileinfo.st_size;
+ // Fill-in entry points
+ pStream->BaseRead = BaseHttp_Read;
+ pStream->BaseGetPos = BaseHttp_GetPos;
+ pStream->BaseGetSize = BaseHttp_GetSize;
+ pStream->BaseGetTime = BaseHttp_GetTime;
+ pStream->BaseClose = BaseHttp_Close;
+ pStream->dwFlags = dwStreamFlags;
return true;
+
+#else
+
+ // Not supported
+ pStream = pStream;
+ szFileName = szFileName;
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return false;
+
#endif
}
-/**
- * \a pStream Pointer to an open stream
- * \a NewFileSize New size of the file
- */
-static bool File_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)
+//-----------------------------------------------------------------------------
+// Local functions - linear stream support
+
+static bool LinearStream_Read(
+ TLinearStream * 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
{
-#ifdef PLATFORM_WINDOWS
+ ULONGLONG ByteOffset;
+ ULONGLONG EndOffset;
+ LPBYTE pbBitmap;
+ DWORD BlockIndex;
+ DWORD ByteIndex;
+ DWORD BitMask;
+
+ // At this point, we must have a bitmap set
+ assert(pStream->pBitmap != NULL);
+
+ // If we have data map, we must check if the data block is present in the MPQ
+ if(dwBytesToRead != 0)
{
- LONG FileSizeHi = (LONG)(NewFileSize >> 32);
- LONG FileSizeLo = (LONG)(NewFileSize);
- DWORD dwNewPos;
- bool bResult;
+ DWORD BlockSize = pStream->pBitmap->BlockSize;
- // Set the position at the new file size
- dwNewPos = SetFilePointer(pStream->hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN);
- if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS)
- return false;
+ // Get the offset where we read it from
+ if(pByteOffset == NULL)
+ pStream->BaseGetPos(pStream, ByteOffset);
+ else
+ ByteOffset = *pByteOffset;
+ EndOffset = ByteOffset + dwBytesToRead;
- // Set the current file pointer as the end of the file
- bResult = (bool)SetEndOfFile(pStream->hFile);
+ // If the start of the area is within the region
+ // protected by data map, check each block
+ if(ByteOffset < pStream->pBitmap->EndOffset)
+ {
+ // Cut the end of the stream protected by the data map
+ EndOffset = STORMLIB_MIN(EndOffset, pStream->pBitmap->EndOffset);
- // Restore the file position
- FileSizeHi = (LONG)(pStream->RawFilePos >> 32);
- FileSizeLo = (LONG)(pStream->RawFilePos);
- SetFilePointer(pStream->hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN);
- return bResult;
- }
-#endif
-
-#ifdef PLATFORM_MAC
- {
- OSErr theErr;
+ // Calculate the initial block index
+ BlockIndex = (DWORD)(ByteOffset / BlockSize);
+ pbBitmap = (LPBYTE)(pStream->pBitmap + 1);
- theErr = FSSetForkSize((short)(long)pStream->hFile, fsFromStart, (SInt64)NewFileSize);
- if(theErr != noErr)
- {
- nLastError = theErr;
- return false;
+ // Parse each block
+ while(ByteOffset < EndOffset)
+ {
+ // Prepare byte index and bit mask
+ ByteIndex = BlockIndex / 8;
+ BitMask = 1 << (BlockIndex & 0x07);
+
+ // If that bit is not set, it means that the block is not present
+ if((pbBitmap[ByteIndex] & BitMask) == 0)
+ {
+ SetLastError(ERROR_FILE_CORRUPT);
+ return false;
+ }
+
+ // Move to tne next block
+ ByteOffset += BlockSize;
+ BlockIndex++;
+ }
}
-
- return true;
}
-#endif
-#ifdef PLATFORM_LINUX
+ // Now if all tests passed, we can call the base read function
+ return pStream->BaseRead(pStream, pByteOffset, pvBuffer, dwBytesToRead);
+}
+
+static bool LinearStream_Switch(TLinearStream * pStream, TLinearStream * pNewStream)
+{
+ // Sanity checks
+ assert((pNewStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR);
+ assert((pNewStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE);
+ assert((pStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR);
+ assert((pStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE);
+
+ // Close the new stream
+ pNewStream->BaseClose(pNewStream);
+
+ // Close the source stream
+ pStream->BaseClose(pStream);
+
+ // Rename the new data source file to the existing file
+ if(!BaseFile_Switch(pStream, pNewStream))
+ return false;
+
+ // Now we have to open the "pStream" again
+ if(!BaseFile_Open(pStream, pStream->szFileName, pNewStream->dwFlags))
+ return false;
+
+ // We need to cleanup the new data stream
+ FileStream_Close(pNewStream);
+ return true;
+}
+
+static bool LinearStream_GetBitmap(
+ TLinearStream * pStream,
+ TFileBitmap * pBitmap,
+ DWORD Length,
+ LPDWORD LengthNeeded)
+{
+ DWORD TotalLength;
+ bool bResult = false;
+
+ // Assumed that we have bitmap now
+ assert(pStream->pBitmap != NULL);
+
+ // Give the bitmap length
+ TotalLength = sizeof(TFileBitmap) + pStream->pBitmap->BitmapSize;
+ if(LengthNeeded != NULL)
+ *LengthNeeded = TotalLength;
+
+ // Do we have enough space to fill at least the bitmap structure?
+ if(Length >= sizeof(TFileBitmap))
{
- if(ftruncate((intptr_t)pStream->hFile, (off_t)NewFileSize) == -1)
+ // Enough space for complete bitmap?
+ if(Length >= TotalLength)
{
- nLastError = errno;
- return false;
+ memcpy(pBitmap, pStream->pBitmap, TotalLength);
+ bResult = true;
+ }
+ else
+ {
+ memcpy(pBitmap, pStream->pBitmap, sizeof(TFileBitmap));
+ bResult = true;
}
-
- return true;
}
-#endif
+
+ return bResult;
}
-//-----------------------------------------------------------------------------
-// Stream functions - partial normal file stream
+static void LinearStream_Close(TLinearStream * pStream)
+{
+ // Free the data map, if any
+ if(pStream->pBitmap != NULL)
+ STORM_FREE(pStream->pBitmap);
+ pStream->pBitmap = NULL;
+
+ // Call the base class for closing the stream
+ return pStream->BaseClose(pStream);
+}
-/**
- * \a pStream Pointer to an open stream
- * \a ByteOffset File byte offset
- */
-static bool PartFile_GetPos(TPartFileStream * pStream, ULONGLONG & ByteOffset)
+static bool LinearStream_Open(TLinearStream * pStream)
{
- ByteOffset = pStream->VirtualPos;
+ // No extra work here really; just set entry points
+ pStream->StreamRead = pStream->BaseRead;
+ pStream->StreamWrite = pStream->BaseWrite;
+ pStream->StreamGetPos = pStream->BaseGetPos;
+ pStream->StreamGetSize = pStream->BaseGetSize;
+ pStream->StreamSetSize = pStream->BaseSetSize;
+ pStream->StreamGetTime = pStream->BaseGetTime;
+ pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap;
+ pStream->StreamSwitch = (STREAM_SWITCH)LinearStream_Switch;
+ pStream->StreamClose = (STREAM_CLOSE)LinearStream_Close;
return true;
}
-/**
- * \a pStream Pointer to an open stream
- * \a pByteOffset Pointer to file byte offset. If NULL, reads from the current position
- * \a pvBuffer Pointer to data to be read
- * \a dwBytesToRead Number of bytes to read from the file
- */
-static bool PartFile_Read(TPartFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead)
+//-----------------------------------------------------------------------------
+// Local functions - partial stream support
+
+static bool IsPartHeader(PPART_FILE_HEADER pPartHdr)
+{
+ // Version number must be 2
+ if(pPartHdr->PartialVersion == 2)
+ {
+ // GameBuildNumber must be an ASCII number
+ if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2]))
+ {
+ // Block size must be power of 2
+ if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool PartialStream_Read(
+ TPartialStream * pStream,
+ ULONGLONG * pByteOffset,
+ void * pvBuffer,
+ DWORD dwBytesToRead)
{
ULONGLONG RawByteOffset;
LPBYTE pbBuffer = (LPBYTE)pvBuffer;
@@ -789,7 +1176,7 @@ static bool PartFile_Read(TPartFileStream * pStream, ULONGLONG * pByteOffset, vo
// If the part is not present in the file, we fail the read
if((PartMap->Flags & 3) == 0)
{
- nFailReason = ERROR_CAN_NOT_COMPLETE;
+ nFailReason = ERROR_FILE_CORRUPT;
bResult = false;
break;
}
@@ -805,7 +1192,7 @@ static bool PartFile_Read(TPartFileStream * pStream, ULONGLONG * pByteOffset, vo
RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo);
if(RawByteOffset == 0)
{
- nFailReason = ERROR_CAN_NOT_COMPLETE;
+ nFailReason = ERROR_FILE_CORRUPT;
bResult = false;
break;
}
@@ -816,9 +1203,9 @@ static bool PartFile_Read(TPartFileStream * pStream, ULONGLONG * pByteOffset, vo
// Append the offset within the part
RawByteOffset += dwPartOffset;
- if(!File_Read(pStream, &RawByteOffset, pbBuffer, dwBytesInPart))
+ if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart))
{
- nFailReason = ERROR_CAN_NOT_COMPLETE;
+ nFailReason = ERROR_FILE_CORRUPT;
bResult = false;
break;
}
@@ -840,69 +1227,237 @@ static bool PartFile_Read(TPartFileStream * pStream, ULONGLONG * pByteOffset, vo
return (dwBytesRead == dwBytesToRead);
}
-static bool PartFile_Write(
- TPartFileStream * pStream, // Pointer to an open stream
- ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
- const void * pvBuffer, // Pointer to data to be read
- DWORD dwBytesToRead) // Number of bytes to read from the file
+static bool PartialStream_GetPos(
+ TPartialStream * pStream,
+ ULONGLONG & ByteOffset)
{
- // Keep compiler happy
- dwBytesToRead = dwBytesToRead;
- pByteOffset = pByteOffset;
- pvBuffer = pvBuffer;
- pStream = pStream;
-
- // Not allowed
- return false;
+ ByteOffset = pStream->VirtualPos;
+ return true;
}
-static bool PartFile_GetSize(
- TPartFileStream * pStream, // Pointer to an open stream
+static bool PartialStream_GetSize(
+ TPartialStream * pStream, // Pointer to an open stream
ULONGLONG & FileSize) // Pointer where to store file size
{
FileSize = pStream->VirtualSize;
return true;
}
-static bool PartFile_SetSize(
- TPartFileStream * pStream, // Pointer to an open stream
- ULONGLONG NewSize) // new size of the file
+static bool PartialStream_GetBitmap(
+ TPartialStream * pStream,
+ TFileBitmap * pBitmap,
+ DWORD Length,
+ LPDWORD LengthNeeded)
{
- // Keep compiler happy
- pStream = pStream;
- NewSize = NewSize;
+ LPBYTE pbBitmap;
+ DWORD TotalLength;
+ DWORD BitmapSize = 0;
+ DWORD ByteOffset;
+ DWORD BitMask;
+ bool bResult = false;
- // Not allowed
- return false;
+ // Do we have stream bitmap?
+ BitmapSize = ((pStream->BlockCount - 1) / 8) + 1;
+
+ // Give the bitmap length
+ TotalLength = sizeof(TFileBitmap) + BitmapSize;
+ if(LengthNeeded != NULL)
+ *LengthNeeded = TotalLength;
+
+ // Do we have enough to fill at least the header?
+ if(Length >= sizeof(TFileBitmap))
+ {
+ // Fill the bitmap header
+ pBitmap->StartOffset = 0;
+ pBitmap->EndOffset = pStream->VirtualSize;
+ pBitmap->IsComplete = 1;
+ pBitmap->BitmapSize = BitmapSize;
+ pBitmap->BlockSize = pStream->BlockSize;
+ pBitmap->Reserved = 0;
+
+ // Is there at least one incomplete block?
+ for(DWORD i = 0; i < pStream->BlockCount; i++)
+ {
+ if(pStream->PartMap[i].Flags != 3)
+ {
+ pBitmap->IsComplete = 0;
+ break;
+ }
+ }
+
+ bResult = true;
+ }
+
+ // Do we have enough space for supplying the bitmap?
+ if(Length >= TotalLength)
+ {
+ // Fill the file bitmap
+ pbBitmap = (LPBYTE)(pBitmap + 1);
+ for(DWORD i = 0; i < pStream->BlockCount; i++)
+ {
+ // Is the block there?
+ if(pStream->PartMap[i].Flags == 3)
+ {
+ ByteOffset = i / 8;
+ BitMask = 1 << (i & 7);
+ pbBitmap[ByteOffset] |= BitMask;
+ }
+ }
+ bResult = true;
+ }
+
+ return bResult;
}
-/*
- * Stream functions - encrypted stream
- *
- * Note: In original Starcraft II Installer.exe: Suffix derived from battle.net auth. code
- * Address of decryption routine: 0053A3D0 http://us.battle.net/static/mediakey/sc2-authenticationcode-enUS.txt
- * Pointer to decryptor object: ECX Numbers mean offset of 4-char group of auth code
- * Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10-
- */
-static const char * MpqeKey_Starcraft2_Install_enUS = "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG";
-static const char * MpqeKey_Starcraft2_Install_enGB = "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D";
-static const char * MpqeKey_Starcraft2_Install_deDE = "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V";
-static const char * MpqeKey_Starcraft2_Install_esES = "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P";
-static const char * MpqeKey_Starcraft2_Install_frFR = "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76";
-static const char * MpqeKey_Starcraft2_Install_itIT = "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B";
-static const char * MpqeKey_Starcraft2_Install_plPL = "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3";
-static const char * MpqeKey_Starcraft2_Install_ruRU = "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V";
+static void PartialStream_Close(TPartialStream * pStream)
+{
+ // Free the part map
+ if(pStream->PartMap != NULL)
+ STORM_FREE(pStream->PartMap);
+ pStream->PartMap = NULL;
+
+ // Clear variables
+ pStream->VirtualSize = 0;
+ pStream->VirtualPos = 0;
+
+ // Close the base stream
+ assert(pStream->BaseClose != NULL);
+ pStream->BaseClose(pStream);
+}
+
+static bool PartialStream_Open(TPartialStream * pStream)
+{
+ PART_FILE_HEADER PartHdr;
+ ULONGLONG VirtualSize; // Size of the file stored in part file
+ ULONGLONG ByteOffset = {0};
+ DWORD BlockCount;
+
+ // Sanity check
+ assert(pStream->BaseRead != NULL);
+ // Attempt to read PART file header
+ if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER)))
+ {
+ // We need to swap PART file header on big-endian platforms
+ BSWAP_PART_HEADER(&PartHdr);
+
+ // Verify the PART file header
+ if(IsPartHeader(&PartHdr))
+ {
+ // Calculate the number of parts in the file
+ VirtualSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo);
+ assert(VirtualSize != 0);
+ BlockCount = (DWORD)((VirtualSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize);
+
+ // Allocate the map entry array
+ pStream->PartMap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount);
+ if(pStream->PartMap != NULL)
+ {
+ // Load the block map
+ if(pStream->BaseRead(pStream, NULL, pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY)))
+ {
+ // Swap the array of file map entries
+ BSWAP_ARRAY32_UNSIGNED(pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY));
+
+ // Fill the members of PART file stream
+ pStream->VirtualSize = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo;
+ pStream->VirtualPos = 0;
+ pStream->BlockCount = BlockCount;
+ pStream->BlockSize = PartHdr.BlockSize;
+
+ // Set new function pointers
+ pStream->StreamRead = (STREAM_READ)PartialStream_Read;
+ pStream->StreamGetPos = (STREAM_GETPOS)PartialStream_GetPos;
+ pStream->StreamGetSize = (STREAM_GETSIZE)PartialStream_GetSize;
+ pStream->StreamGetTime = pStream->BaseGetTime;
+ pStream->StreamGetTime = pStream->BaseGetTime;
+ pStream->StreamGetBmp = (STREAM_GETBMP)PartialStream_GetBitmap;
+ pStream->StreamClose = (STREAM_CLOSE)PartialStream_Close;
+ return true;
+ }
+
+ // Free the part map
+ STORM_FREE(pStream->PartMap);
+ pStream->PartMap = NULL;
+ }
+ }
+ }
+
+ SetLastError(ERROR_BAD_FORMAT);
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Local functions - encrypted stream support
+
+// Note: Starcraft II - Installer.exe (4.1.1.4219): Suffix derived from battle.net auth. code
+// Address of decryption routine: 0053A3D0 http://us.battle.net/static/mediakey/sc2-authenticationcode-enUS.txt
+// Pointer to decryptor object: ECX Numbers mean offset of 4-char group of auth code (follows in comment)
+// Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10-
+static const char * MpqeKey_Starcraft2_Install_deDE = "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V"; // Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX
+static const char * MpqeKey_Starcraft2_Install_enGB = "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D"; // G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH
+static const char * MpqeKey_Starcraft2_Install_enSG = "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE"; // W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW
+static const char * MpqeKey_Starcraft2_Install_enUS = "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG"; // 3DH5RE5NVM5GTFD85LXGWT6FK859ETR5
+static const char * MpqeKey_Starcraft2_Install_esES = "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P"; // 8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ
+static const char * MpqeKey_Starcraft2_Install_esMX = "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U"; // A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54
+static const char * MpqeKey_Starcraft2_Install_frFR = "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76"; // ZG7J9K938HJEFWPQUA768MA2PFER6EAJ
+static const char * MpqeKey_Starcraft2_Install_itIT = "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B"; // NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2
+static const char * MpqeKey_Starcraft2_Install_koKR = "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA"; // 3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F
+static const char * MpqeKey_Starcraft2_Install_plPL = "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3"; // 2NSFB8MELULJ83U6YHA3UP6K4MQD48L6
+static const char * MpqeKey_Starcraft2_Install_ptBR = "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX"; // QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E
+static const char * MpqeKey_Starcraft2_Install_ruRU = "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V"; // VHB378W64BAT9SH7D68VV9NLQDK9YEGT
+static const char * MpqeKey_Starcraft2_Install_zhTW = "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ"; // U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE
+
+// Note: Diablo III: Agent.exe (1.0.0.954): Suffix derived from battle.net auth. code
+// Address of decryption routine: 00502b00 http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt
+// Pointer to decryptor object: ECX Numbers mean offset of 4-char group of auth code (follows in comment)
+// Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10-
+static const char * MpqeKey_Diablo3_Install_deDE = "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX"; // UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK
+static const char * MpqeKey_Diablo3_Install_enGB = "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ"; // MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP
+static const char * MpqeKey_Diablo3_Install_enSG = "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS"; // 8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP
+static const char * MpqeKey_Diablo3_Install_enUS = "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG"; // EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ
+static const char * MpqeKey_Diablo3_Install_esES = "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3"; // PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT
+static const char * MpqeKey_Diablo3_Install_esMX = "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS"; // X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2
+static const char * MpqeKey_Diablo3_Install_frFR = "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5"; // 5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2
+static const char * MpqeKey_Diablo3_Install_itIT = "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T"; // 478JD2K56EVNVVY4XX8TDWYT5B8KB254
+static const char * MpqeKey_Diablo3_Install_koKR = "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH"; // 8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A
+static const char * MpqeKey_Diablo3_Install_plPL = "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK"; // LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB
+static const char * MpqeKey_Diablo3_Install_ptBR = "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB"; // K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG
+static const char * MpqeKey_Diablo3_Install_ruRU = "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //
+static const char * MpqeKey_Diablo3_Install_zhTW = "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8"; // 6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H
+static const char * MpqeKey_Diablo3_Install_zhCN = "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //
+
static const char * MpqKeyArray[] =
{
- MpqeKey_Starcraft2_Install_enUS,
- MpqeKey_Starcraft2_Install_enGB,
MpqeKey_Starcraft2_Install_deDE,
+ MpqeKey_Starcraft2_Install_enGB,
+ MpqeKey_Starcraft2_Install_enSG,
+ MpqeKey_Starcraft2_Install_enUS,
MpqeKey_Starcraft2_Install_esES,
+ MpqeKey_Starcraft2_Install_esMX,
MpqeKey_Starcraft2_Install_frFR,
MpqeKey_Starcraft2_Install_itIT,
+ MpqeKey_Starcraft2_Install_koKR,
MpqeKey_Starcraft2_Install_plPL,
+ MpqeKey_Starcraft2_Install_ptBR,
MpqeKey_Starcraft2_Install_ruRU,
+ MpqeKey_Starcraft2_Install_zhTW,
+
+ MpqeKey_Diablo3_Install_deDE,
+ MpqeKey_Diablo3_Install_enGB,
+ MpqeKey_Diablo3_Install_enSG,
+ MpqeKey_Diablo3_Install_enUS,
+ MpqeKey_Diablo3_Install_esES,
+ MpqeKey_Diablo3_Install_esMX,
+ MpqeKey_Diablo3_Install_frFR,
+ MpqeKey_Diablo3_Install_itIT,
+ MpqeKey_Diablo3_Install_koKR,
+ MpqeKey_Diablo3_Install_plPL,
+ MpqeKey_Diablo3_Install_ptBR,
+// MpqeKey_Diablo3_Install_ruRU,
+ MpqeKey_Diablo3_Install_zhTW,
+// MpqeKey_Diablo3_Install_zhCN,
+
NULL
};
@@ -1026,40 +1581,35 @@ static void DecryptFileChunk(
}
}
-
-static bool DetectFileKey(TEncryptedStream * pStream)
+static const char * DetectFileKey(LPBYTE pbEncryptedHeader)
{
ULONGLONG ByteOffset = 0;
- BYTE EncryptedHeader[MPQE_CHUNK_SIZE];
BYTE FileHeader[MPQE_CHUNK_SIZE];
-
- // Load the chunk from the file
- if(!FileStream_Read(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader)))
- return false;
+ BYTE Key[MPQE_CHUNK_SIZE];
// We just try all known keys one by one
for(int i = 0; MpqKeyArray[i] != NULL; i++)
{
// Copy the key there
- memcpy(pStream->Key, MpqKeyArray[i], MPQE_CHUNK_SIZE);
- BSWAP_ARRAY32_UNSIGNED(pStream->Key, MPQE_CHUNK_SIZE);
+ memcpy(Key, MpqKeyArray[i], MPQE_CHUNK_SIZE);
+ BSWAP_ARRAY32_UNSIGNED(Key, MPQE_CHUNK_SIZE);
// Try to decrypt with the given key
- memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE);
- DecryptFileChunk((LPDWORD)FileHeader, pStream->Key, ByteOffset, MPQE_CHUNK_SIZE);
+ memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE);
+ DecryptFileChunk((LPDWORD)FileHeader, Key, ByteOffset, MPQE_CHUNK_SIZE);
- // We check the decrypoted data
+ // We check the decrypted data
// All known encrypted MPQs have header at the begin of the file,
// so we check for MPQ signature there.
if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q')
- return true;
+ return MpqKeyArray[i];
}
// Key not found, sorry
- return false;
+ return NULL;
}
-static bool EncryptedFile_Read(
+static bool EncryptedStream_Read(
TEncryptedStream * 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
@@ -1075,10 +1625,10 @@ static bool EncryptedFile_Read(
bool bResult = false;
// Get the byte offset
- if(pByteOffset != NULL)
- ByteOffset = *pByteOffset;
+ if(pByteOffset == NULL)
+ pStream->BaseGetPos(pStream, ByteOffset);
else
- ByteOffset = pStream->RawFilePos;
+ ByteOffset = *pByteOffset;
// Cut it down to MPQE chunk size
StartOffset = ByteOffset;
@@ -1097,7 +1647,7 @@ static bool EncryptedFile_Read(
dwOffsetInCache = (DWORD)(ByteOffset - StartOffset);
// Read the file from the stream as-is
- if(File_Read(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt))
+ if(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt))
{
// Decrypt the data
DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate);
@@ -1119,31 +1669,42 @@ static bool EncryptedFile_Read(
return bResult;
}
-static bool EncryptedFile_Write(
- TEncryptedStream * pStream, // Pointer to an open stream
- ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
- const void * pvBuffer, // Pointer to data to be read
- DWORD dwBytesToRead) // Number of bytes to read from the file
+static bool EncryptedStream_Open(TEncryptedStream * pStream)
{
- // Keep compiler happy
- dwBytesToRead = dwBytesToRead;
- pByteOffset = pByteOffset;
- pvBuffer = pvBuffer;
- pStream = pStream;
+ ULONGLONG ByteOffset = 0;
+ BYTE EncryptedHeader[MPQE_CHUNK_SIZE];
+ const char * szKey;
- // Not allowed
- return false;
-}
+ // Sanity check
+ assert(pStream->BaseRead != NULL);
-static bool EncryptedFile_SetSize(
- TEncryptedStream * pStream, // Pointer to an open stream
- ULONGLONG NewSize) // new size of the file
-{
- // Keep compiler happy
- pStream = pStream;
- NewSize = NewSize;
+ // Load one MPQE chunk and try to detect the file key
+ if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader)))
+ {
+ // Attempt to decrypt the MPQ header with all known keys
+ szKey = DetectFileKey(EncryptedHeader);
+ if(szKey != NULL)
+ {
+ // Copy the key for the file
+ memcpy(pStream->Key, szKey, MPQE_CHUNK_SIZE);
+ BSWAP_ARRAY32_UNSIGNED(pStream->Key, MPQE_CHUNK_SIZE);
+
+ // Assign functions
+ pStream->StreamRead = (STREAM_READ)EncryptedStream_Read;
+ pStream->StreamGetPos = pStream->BaseGetPos;
+ pStream->StreamGetSize = pStream->BaseGetSize;
+ pStream->StreamGetTime = pStream->BaseGetTime;
+ pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap;
+ pStream->StreamClose = pStream->BaseClose;
+
+ // We need to reset the position back to the begin of the file
+ pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, 0);
+ return true;
+ }
- // Not allowed
+ // An unknown key
+ SetLastError(ERROR_UNKNOWN_FILE_KEY);
+ }
return false;
}
@@ -1151,7 +1712,7 @@ static bool EncryptedFile_SetSize(
// Public functions
/**
- * This function creates a new file for read or read-write access
+ * This function creates a new file for read-write access
*
* - If the current platform supports file sharing,
* the file must be created for read sharing (i.e. another application
@@ -1168,35 +1729,45 @@ static bool EncryptedFile_SetSize(
*/
TFileStream * FileStream_CreateFile(
- const TCHAR * szFileName) // Name of the file to create
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
{
- TFileStream * pStream = NULL;
- HANDLE hFile;
+ TFileStream * pStream;
- // Create the file
- hFile = CreateNewFile(szFileName);
- if(hFile != INVALID_HANDLE_VALUE)
+ // We only support creation of linear, local file
+ if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE))
{
- // Allocate the FileStream structure and fill it
- pStream = STORM_ALLOC(TFileStream, 1);
- if(pStream != NULL)
- {
- // Reset entire structure to zero
- memset(pStream, 0, sizeof(TFileStream));
-
- // Save file name and set function pointers
- _tcscpy(pStream->szFileName, szFileName);
- pStream->StreamGetPos = File_GetPos;
- pStream->StreamRead = File_Read;
- pStream->StreamWrite = File_Write;
- pStream->StreamGetSize = File_GetSize;
- pStream->StreamSetSize = File_SetSize;
- pStream->hFile = hFile;
- }
- else
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return NULL;
+ }
+
+ // Allocate file stream structure for linear stream
+ pStream = STORM_ALLOC(TFileStream, 1);
+ if(pStream != NULL)
+ {
+ // Reset entire structure to zero
+ memset(pStream, 0, sizeof(TFileStream));
+ _tcscpy(pStream->szFileName, szFileName);
+
+ // Attempt to create the disk file
+ if(BaseFile_Create(pStream, szFileName, dwStreamFlags))
{
- CloseTheFile(hFile);
+ // Fill the stream provider functions
+ pStream->StreamRead = pStream->BaseRead;
+ pStream->StreamWrite = pStream->BaseWrite;
+ pStream->StreamGetPos = pStream->BaseGetPos;
+ pStream->StreamGetSize = pStream->BaseGetSize;
+ pStream->StreamSetSize = pStream->BaseSetSize;
+ pStream->StreamGetTime = pStream->BaseGetTime;
+ pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap;;
+ pStream->StreamSwitch = (STREAM_SWITCH)LinearStream_Switch;
+ pStream->StreamClose = pStream->BaseClose;
+ return pStream;
}
+
+ // File create failed, delete the stream
+ STORM_FREE(pStream);
+ pStream = NULL;
}
// Return the stream
@@ -1212,185 +1783,106 @@ TFileStream * FileStream_CreateFile(
* - If the file exists but cannot be open, then function must return NULL
* - The parameters of the function must be validate by the caller
* - The function must check if the file is a PART file,
- * and create TPartFileStream object if so.
+ * and create TPartialStream object if so.
* - The function must initialize all stream function pointers in TFileStream
* - If the function fails from any reason, it must close all handles
* and free all memory that has been allocated in the process of stream creation,
* including the TFileStream structure itself
*
* \a szFileName Name of the file to open
- * \a bWriteAccess false for read only, true for read+write
+ * \a dwStreamFlags specifies the provider and base storage type
*/
-TFileStream * FileStream_OpenRawFile(
- const TCHAR * szFileName, // Name of the file to create
- bool bWriteAccess) // false = read-only, true = read+write
+TFileStream * FileStream_OpenFile(
+ const TCHAR * szFileName,
+ DWORD dwStreamFlags)
{
- TFileStream * pStream;
- HANDLE hFile;
-
- // Create the file
- hFile = OpenExistingFile(szFileName, bWriteAccess);
- if(hFile == INVALID_HANDLE_VALUE)
- return NULL;
+ TFileStream * pStream = NULL;
+ size_t StreamSize = 0;
+ bool bStreamResult = false;
+ bool bBaseResult = false;
- // Initialize the file as normal file stream
- pStream = STORM_ALLOC(TFileStream, 1);
- if(pStream != NULL)
+ // Allocate file stream for each stream provider
+ switch(dwStreamFlags & STREAM_PROVIDER_MASK)
{
- // Reset entire structure to zero
- memset(pStream, 0, sizeof(TFileStream));
-
- // Save file name and set function pointers
- _tcscpy(pStream->szFileName, szFileName);
- pStream->StreamGetPos = File_GetPos;
- pStream->StreamRead = File_Read;
- pStream->StreamWrite = File_Write;
- pStream->StreamGetSize = File_GetSize;
- pStream->StreamSetSize = File_SetSize;
- if(bWriteAccess == false)
- pStream->StreamFlags |= STREAM_FLAG_READ_ONLY;
- pStream->hFile = hFile;
- return pStream;
- }
+ case STREAM_PROVIDER_LINEAR: // Allocate structure for linear stream
+ StreamSize = sizeof(TLinearStream);
+ break;
- CloseTheFile(hFile);
- return NULL;
-}
+ case STREAM_PROVIDER_PARTIAL:
+ dwStreamFlags |= STREAM_FLAG_READ_ONLY;
+ StreamSize = sizeof(TPartialStream);
+ break;
-/**
- * Opens a file
- *
- * \a szFileName Name of the file to open
- * \a bWriteAccess false for read only, true for read+write
- */
+ case STREAM_PROVIDER_ENCRYPTED:
+ dwStreamFlags |= STREAM_FLAG_READ_ONLY;
+ StreamSize = sizeof(TEncryptedStream);
+ break;
-TFileStream * FileStream_OpenFile(const TCHAR * szFileName, bool bWriteAccess)
-{
- PART_FILE_HEADER PartHdr;
- ULONGLONG VirtualSize; // Size of the file stored in part file
- ULONGLONG ByteOffset = {0};
- TFileStream * pStream;
- size_t nStructLength;
- DWORD BlockCount;
+ default:
+ return NULL;
+ }
- // Open the file as normal stream
- pStream = FileStream_OpenRawFile(szFileName, bWriteAccess);
+ // Allocate the stream for each type
+ pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize);
if(pStream == NULL)
return NULL;
- // Attempt to read PART file header
- if(FileStream_Read(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER)))
- {
- // We need to swap PART file header on big-endian platforms
- BSWAP_PART_HEADER(&PartHdr);
-
- // Verify the PART file header
- if(IsPartHeader(&PartHdr))
- {
- TPartFileStream * pPartStream;
+ // Fill the stream structure with zeros
+ memset(pStream, 0, StreamSize);
+ _tcscpy(pStream->szFileName, szFileName);
- // Calculate the number of parts in the file
- VirtualSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo);
- BlockCount = (DWORD)((VirtualSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize);
+ // Now initialize the respective base provider
+ switch(dwStreamFlags & BASE_PROVIDER_MASK)
+ {
+ case BASE_PROVIDER_FILE:
+ bBaseResult = BaseFile_Open(pStream, szFileName, dwStreamFlags);
+ break;
- // Calculate the size of the entire structure
- // Note that we decrement number of parts by one,
- // because there already is one entry in the TPartFileStream structure
- nStructLength = sizeof(TPartFileStream) + (BlockCount - 1) * sizeof(PART_FILE_MAP_ENTRY);
- pPartStream = (TPartFileStream *)STORM_ALLOC(char, nStructLength);
- if(pPartStream != NULL)
- {
- // Initialize the part file stream
- memset(pPartStream, 0, nStructLength);
- memcpy(pPartStream, pStream, sizeof(TFileStream));
+ case BASE_PROVIDER_MAP:
+ dwStreamFlags |= STREAM_FLAG_READ_ONLY;
+ bBaseResult = BaseMap_Open(pStream, szFileName, dwStreamFlags);
+ break;
- // Load the block map
- if(!FileStream_Read(pPartStream, NULL, pPartStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY)))
- {
- FileStream_Close(pStream);
- STORM_FREE(pPartStream);
- return NULL;
- }
+ case BASE_PROVIDER_HTTP:
+ dwStreamFlags |= STREAM_FLAG_READ_ONLY;
+ bBaseResult = BaseHttp_Open(pStream, szFileName, dwStreamFlags);
+ break;
+ }
- // Swap the array of file map entries
- BSWAP_ARRAY32_UNSIGNED(pPartStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY));
+ // If we failed to open the base storage, fail the operation
+ if(bBaseResult == false)
+ {
+ STORM_FREE(pStream);
+ return NULL;
+ }
- // Set new function pointers
- pPartStream->StreamGetPos = (STREAM_GETPOS)PartFile_GetPos;
- pPartStream->StreamRead = (STREAM_READ)PartFile_Read;
- pPartStream->StreamWrite = (STREAM_WRITE)PartFile_Write;
- pPartStream->StreamGetSize = (STREAM_GETSIZE)PartFile_GetSize;
- pPartStream->StreamSetSize = (STREAM_SETSIZE)PartFile_SetSize;
- pPartStream->StreamFlags |= (STREAM_FLAG_READ_ONLY | STREAM_FLAG_PART_FILE);
+ // Now initialize the stream provider
+ switch(dwStreamFlags & STREAM_PROVIDER_MASK)
+ {
+ case STREAM_PROVIDER_LINEAR:
+ bStreamResult = LinearStream_Open((TLinearStream *)pStream);
+ break;
- // Fill the members of PART file stream
- pPartStream->VirtualSize = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo;
- pPartStream->VirtualPos = 0;
- pPartStream->BlockCount = BlockCount;
- pPartStream->BlockSize = PartHdr.BlockSize;
+ case STREAM_PROVIDER_PARTIAL:
+ bStreamResult = PartialStream_Open((TPartialStream *)pStream);
+ break;
- STORM_FREE(pStream);
- }
- return pPartStream;
- }
+ case STREAM_PROVIDER_ENCRYPTED:
+ bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream);
+ break;
}
- // If the file doesn't contain PART file header,
- // reset the file position to begin of the file
- FileStream_Read(pStream, &ByteOffset, NULL, 0);
- return pStream;
-}
-
-TFileStream * FileStream_OpenEncrypted(const TCHAR * szFileName)
-{
- TEncryptedStream * pEncryptedStream;
- TFileStream * pStream;
-
- // Open the file as raw stream
- pStream = FileStream_OpenRawFile(szFileName, false);
- if(pStream)
+ // If the operation failed, free the stream and set it to NULL
+ if(bStreamResult == false)
{
- // Allocate new stream for handling encryption
- pEncryptedStream = STORM_ALLOC(TEncryptedStream, 1);
- if(pEncryptedStream != NULL)
- {
- // Copy the file stream to the encrypted stream
- memset(pEncryptedStream, 0, sizeof(TEncryptedStream));
- memcpy(pEncryptedStream, pStream, sizeof(TFileStream));
-
- // Assign functions
- pEncryptedStream->StreamRead = (STREAM_READ)EncryptedFile_Read;
- pEncryptedStream->StreamWrite = (STREAM_WRITE)EncryptedFile_Write;
- pEncryptedStream->StreamSetSize = (STREAM_SETSIZE)EncryptedFile_SetSize;
- pEncryptedStream->StreamFlags |= (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE);
-
- // Get the file key
- if(DetectFileKey(pEncryptedStream))
- return pEncryptedStream;
-
- // Close the encrypted stream
- STORM_FREE(pEncryptedStream);
- pEncryptedStream = NULL;
- }
-
- FileStream_Close(pStream);
+ // Only close the base stream
+ pStream->BaseClose(pStream);
+ STORM_FREE(pStream);
pStream = NULL;
}
- SetLastError(ERROR_UNKNOWN_FILE_KEY);
- return NULL;
-}
-
-/**
- * This function returns the current file position
- * \a pStream
- * \a ByteOffset
- */
-bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset)
-{
- assert(pStream->StreamGetPos != NULL);
- return pStream->StreamGetPos(pStream, ByteOffset);
+ return pStream;
}
/**
@@ -1432,67 +1924,22 @@ bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBu
*/
bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)
{
- if(pStream->StreamFlags & STREAM_FLAG_READ_ONLY)
+ if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
return false;
- assert(pStream->StreamWrite != NULL);
+ assert(pStream->StreamWrite != NULL);
return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite);
}
-
/**
- * Returns the last write time of a file
- *
- * \a pStream Pointer to an open stream
- * \a pFileType Pointer where to store the file last write time
+ * This function returns the current file position
+ * \a pStream
+ * \a ByteOffset
*/
-bool FileStream_GetLastWriteTime(TFileStream * pStream, ULONGLONG * pFileTime)
+bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset)
{
-#ifdef PLATFORM_WINDOWS
- FILETIME ft;
-
- if(!GetFileTime(pStream->hFile, NULL, NULL, &ft))
- return false;
-
- *pFileTime = MAKE_OFFSET64(ft.dwHighDateTime, ft.dwLowDateTime);
- return true;
-#endif
-
-#ifdef PLATFORM_MAC
- OSErr theErr;
- FSRef theFileRef;
- FSCatalogInfo theCatInfo;
-
- theErr = FSGetForkCBInfo((short)(long)pStream->hFile, 0, NULL, NULL, NULL, &theFileRef, NULL);
- if(theErr != noErr)
- {
- nLastError = theErr;
- return false;
- }
-
- theErr = FSGetCatalogInfo(&theFileRef, kFSCatInfoContentMod, &theCatInfo, NULL, NULL, NULL);
- if(theErr != noErr)
- {
- nLastError = theErr;
- return false;
- }
-
- ConvertUTCDateTimeToFileTime(&theCatInfo.contentModDate, pFileTime);
- return true;
-#endif
-
-#ifdef PLATFORM_LINUX
- struct stat file_stats;
-
- if(fstat((int)(size_t)pStream->hFile, &file_stats) == -1)
- {
- nLastError = errno;
- return false;
- }
-
- ConvertTimeTToFileTime(pFileTime, file_stats.st_mtime);
- return true;
-#endif
+ assert(pStream->StreamGetPos != NULL);
+ return pStream->StreamGetPos(pStream, ByteOffset);
}
/**
@@ -1515,14 +1962,26 @@ bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize)
*/
bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)
{
- if(pStream->StreamFlags & STREAM_FLAG_READ_ONLY)
+ if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
return false;
- assert(pStream->StreamSetSize != NULL);
+ assert(pStream->StreamSetSize != NULL);
return pStream->StreamSetSize(pStream, NewFileSize);
}
/**
+ * Returns the last write time of a file
+ *
+ * \a pStream Pointer to an open stream
+ * \a pFileType Pointer where to store the file last write time
+ */
+bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)
+{
+ assert(pStream->StreamGetTime != NULL);
+ return pStream->StreamGetTime(pStream, pFileTime);
+}
+
+/**
* Switches a stream with another. Used for final phase of archive compacting.
* Performs these steps:
*
@@ -1533,37 +1992,86 @@ bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)
* \a pStream Pointer to an open stream
* \a pTempStream Temporary ("working") stream (created during archive compacting)
*/
-bool FileStream_MoveFile(TFileStream * pStream, TFileStream * pTempStream)
+bool FileStream_Switch(TFileStream * pStream, TFileStream * pNewStream)
+{
+ if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
+ return false;
+
+ assert(pStream->StreamSwitch != NULL);
+ return pStream->StreamSwitch(pStream, pNewStream);
+}
+
+/**
+ * Returns the file name of the stream
+ *
+ * \a pStream Pointer to an open stream
+ */
+TCHAR * FileStream_GetFileName(TFileStream * pStream)
{
- bool bWriteAccess;
+ assert(pStream != NULL);
+ return pStream->szFileName;
+}
- // Close the handle to the temporary file
- CloseTheFile(pTempStream->hFile);
- pTempStream->hFile = INVALID_HANDLE_VALUE;
+/**
+ * Returns true if the stream is read-only
+ *
+ * \a pStream Pointer to an open stream
+ */
+bool FileStream_IsReadOnly(TFileStream * pStream)
+{
+ return (pStream->dwFlags & STREAM_FLAG_READ_ONLY) ? true : false;
+}
+
+/**
+ * This function enabled a linear stream to include data bitmap.
+ * Used by MPQs v 4.0 from WoW. Each file block is represented by
+ * a bit in the bitmap. 1 means the block is present, 0 means it's not.
+ *
+ * \a pStream Pointer to an open stream
+ * \a pBitmap Pointer to file bitmap
+ */
- // Close the handle to the source file
- CloseTheFile(pStream->hFile);
- pStream->hFile = INVALID_HANDLE_VALUE;
+bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap)
+{
+ TLinearStream * pLinearStream;
- // Rename the temp file to the final file
- if(!RenameFile(pTempStream->szFileName, pStream->szFileName))
+ // It must be a linear stream.
+ if((pStream->dwFlags & STREAM_PROVIDER_MASK) != STREAM_PROVIDER_LINEAR)
return false;
+ pLinearStream = (TLinearStream *)pStream;
- // Now open the renamed file again, and store its handle to the old stream
- bWriteAccess = (pStream->StreamFlags & STREAM_FLAG_READ_ONLY) ? false : true;
- pStream->hFile = OpenExistingFile(pStream->szFileName, bWriteAccess);
- if(pStream->hFile == INVALID_HANDLE_VALUE)
+ // Two bitmaps are not allowed
+ if(pLinearStream->pBitmap != NULL)
return false;
- // Delete the temporary file stream
- FileStream_Close(pTempStream);
+ // We need to change some entry points
+ pLinearStream->StreamRead = (STREAM_READ)LinearStream_Read;
+ pLinearStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBitmap;
- // The file position has been reset to zero by reopening the file
- pStream->RawFilePos = 0;
+ // Using data bitmap renders the stream to be read only.
+ pLinearStream->dwFlags |= STREAM_FLAG_READ_ONLY;
+ pLinearStream->pBitmap = pBitmap;
return true;
}
/**
+ * This function retrieves the file bitmap. A file bitmap is an array
+ * of bits, each bit representing one file block. A value of 1 means
+ * that the block is present in the file, a value of 0 means that the
+ * block is not present.
+ *
+ * \a pStream Pointer to an open stream
+ * \a pBitmap Pointer to buffer where to store the file bitmap
+ * \a Length Size of buffer pointed by pBitmap, in bytes
+ * \a LengthNeeded If non-NULL, the function supplies the necessary byte size of the buffer
+ */
+bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
+{
+ assert(pStream->StreamGetBmp != NULL);
+ return pStream->StreamGetBmp(pStream, pBitmap, Length, LengthNeeded);
+}
+
+/**
* This function closes an archive file and frees any data buffers
* that have been allocated for stream management. The function must also
* support partially allocated structure, i.e. one or more buffers
@@ -1576,9 +2084,10 @@ void FileStream_Close(TFileStream * pStream)
// Check if the stream structure is allocated at all
if(pStream != NULL)
{
- // Close the file handle
- if(pStream->hFile != INVALID_HANDLE_VALUE)
- CloseTheFile(pStream->hFile);
+ // Close the stream provider.
+ // This will also close the base stream
+ assert(pStream->StreamClose != NULL);
+ pStream->StreamClose(pStream);
// Free the stream itself
STORM_FREE(pStream);
@@ -1587,275 +2096,199 @@ void FileStream_Close(TFileStream * pStream)
//-----------------------------------------------------------------------------
// main - for testing purposes
-/*
-int main(void)
+
+#ifdef __STORMLIB_TEST__
+int FileStream_Test(const TCHAR * szFileName, DWORD dwStreamFlags)
{
- ULONGLONG FilePos;
- ULONGLONG FileSize;
- TMPQFileTime * pFT;
- TFileStream * pTempStream;
TFileStream * pStream;
+ TMPQHeader MpqHeader;
+ ULONGLONG FilePos;
TMPQBlock * pBlock;
TMPQHash * pHash;
- TMPQHeader2 MpqHeader;
- char szString1[100] = "This is a single line\n\r";
- char szString2[100];
- char Buffer[0x80];
- DWORD dwLength = strlen(szString1);
- //
- // Test 1: Write to a stream
- //
+ InitializeMpqCryptography();
- pStream = FileStream_CreateFile("E:\\Stream.bin");
+ pStream = FileStream_OpenFile(szFileName, dwStreamFlags);
if(pStream == NULL)
+ return GetLastError();
+
+ // Read the MPQ header
+ FileStream_Read(pStream, NULL, &MpqHeader, MPQ_HEADER_SIZE_V2);
+ if(MpqHeader.dwID != ID_MPQ)
+ return ERROR_FILE_CORRUPT;
+
+ // Read the hash table
+ pHash = STORM_ALLOC(TMPQHash, MpqHeader.dwHashTableSize);
+ if(pHash != NULL)
{
- printf("Failed to create new file\n");
- return -1;
+ FilePos = MpqHeader.dwHashTablePos;
+ FileStream_Read(pStream, &FilePos, pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash));
+ DecryptMpqBlock(pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash), MPQ_KEY_HASH_TABLE);
+ STORM_FREE(pHash);
}
- for(int i = 0; i < 10; i++)
+ // Read the block table
+ pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize);
+ if(pBlock != NULL)
{
- if(!FileStream_Write(pStream, NULL, szString1, dwLength))
- {
- printf("Failed to write to the stream\n");
- return -1;
- }
+ FilePos = MpqHeader.dwBlockTablePos;
+ FileStream_Read(pStream, &FilePos, pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock));
+ DecryptMpqBlock(pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock), MPQ_KEY_BLOCK_TABLE);
+ STORM_FREE(pBlock);
}
+
FileStream_Close(pStream);
+ return ERROR_SUCCESS;
+}
+#endif
+
+/*
+int FileStream_Test()
+{
+ TFileStream * pStream;
+
+ InitializeMpqCryptography();
//
- // Test2: Read from the stream
+ // Test 1: Write to a stream
//
- pStream = FileStream_OpenFile("E:\\Stream.bin", false);
- if(pStream == NULL)
+ pStream = FileStream_CreateFile("E:\\Stream.bin", 0);
+ if(pStream != NULL)
{
- printf("Failed to open existing file\n");
- return -1;
- }
+ char szString1[100] = "This is a single line\n\r";
+ DWORD dwLength = strlen(szString1);
- // This call must end with an error
- if(FileStream_Write(pStream, NULL, "aaa", 3))
- {
- printf("Write succeeded while it should fail\n");
- return -1;
+ for(int i = 0; i < 10; i++)
+ {
+ if(!FileStream_Write(pStream, NULL, szString1, dwLength))
+ {
+ printf("Failed to write to the stream\n");
+ return ERROR_CAN_NOT_COMPLETE;
+ }
+ }
+ FileStream_Close(pStream);
}
- for(int i = 0; i < 10; i++)
+ //
+ // Test2: Read from the stream
+ //
+
+ pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pStream != NULL)
{
- if(!FileStream_Read(pStream, NULL, szString2, dwLength))
+ char szString1[100] = "This is a single line\n\r";
+ char szString2[100];
+ DWORD dwLength = strlen(szString1);
+
+ // This call must end with an error
+ if(FileStream_Write(pStream, NULL, "aaa", 3))
{
- printf("Failed to read from the stream\n");
+ printf("Write succeeded while it should fail\n");
return -1;
}
- szString2[dwLength] = 0;
- if(strcmp(szString1, szString2))
+ for(int i = 0; i < 10; i++)
{
- printf("Data read from file are different from data written\n");
- return -1;
+ if(!FileStream_Read(pStream, NULL, szString2, dwLength))
+ {
+ printf("Failed to read from the stream\n");
+ return -1;
+ }
+
+ szString2[dwLength] = 0;
+ if(strcmp(szString1, szString2))
+ {
+ printf("Data read from file are different from data written\n");
+ return -1;
+ }
}
+ FileStream_Close(pStream);
}
- FileStream_Close(pStream);
//
// Test3: Open the temp stream, write some data and switch it to the original stream
//
- pStream = FileStream_OpenFile("E:\\Stream.bin", false);
- if(pStream == NULL)
- {
- printf("Failed to open existing file\n");
- return -1;
- }
-
- pTempStream = FileStream_CreateFile("E:\\TempStream.bin");
- if(pTempStream == NULL)
- {
- printf("Failed to create temp stream\n");
- return -1;
- }
-
- // Copy the original stream to the temp
- if(!FileStream_GetSize(pStream, &FileSize))
- {
- printf("Failed to get the file size\n");
- return -1;
- }
-
- while(FileSize.QuadPart != 0)
+ pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pStream != NULL)
{
- DWORD dwBytesToRead = FileSize.LowPart;
+ TFileStream * pTempStream;
+ ULONGLONG FileSize;
- if(dwBytesToRead > sizeof(Buffer))
- dwBytesToRead = sizeof(Buffer);
-
- if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead))
+ pTempStream = FileStream_CreateFile("E:\\TempStream.bin", 0);
+ if(pTempStream == NULL)
{
- printf("CopyStream: Read source file failed\n");
+ printf("Failed to create temp stream\n");
return -1;
}
- if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead))
+ // Copy the original stream to the temp
+ if(!FileStream_GetSize(pStream, FileSize))
{
- printf("CopyStream: Write target file failed\n");
+ printf("Failed to get the file size\n");
return -1;
}
- FileSize.QuadPart -= dwBytesToRead;
- }
+ while(FileSize != 0)
+ {
+ DWORD dwBytesToRead = (DWORD)FileSize;
+ char Buffer[0x80];
- // Switch the streams
- // Note that the pTempStream is closed by the operation
- FileStream_MoveFile(pStream, pTempStream);
- FileStream_Close(pStream);
+ if(dwBytesToRead > sizeof(Buffer))
+ dwBytesToRead = sizeof(Buffer);
- //
- // Test4: Read from the stream again
- //
+ if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead))
+ {
+ printf("CopyStream: Read source file failed\n");
+ return -1;
+ }
- pStream = FileStream_OpenFile("E:\\Stream.bin", false);
- if(pStream == NULL)
- {
- printf("Failed to open existing file\n");
- return -1;
- }
+ if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead))
+ {
+ printf("CopyStream: Write target file failed\n");
+ return -1;
+ }
- for(int i = 0; i < 10; i++)
- {
- if(!FileStream_Read(pStream, NULL, szString2, dwLength))
- {
- printf("Failed to read from the stream\n");
- return -1;
+ FileSize -= dwBytesToRead;
}
- szString2[dwLength] = 0;
- if(strcmp(szString1, szString2))
- {
- printf("Data read from file are different from data written\n");
- return -1;
- }
+ // Switch the streams
+ // Note that the pTempStream is closed by the operation
+ FileStream_Switch(pStream, pTempStream);
+ FileStream_Close(pStream);
}
- FileStream_Close(pStream);
//
- // Test5: Open partial MPQ stream
+ // Test4: Read from the stream again
//
-// InitializeMpqCryptography();
- pStream = FileStream_OpenFile("e:\\Multimedia\\MPQs\\PartialMPQs\\patch.MPQ.part", false);
+ pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream != NULL)
{
- // Read the MPQ header
- FileStream_Read(pStream, NULL, &MpqHeader, MPQ_HEADER_SIZE_V2);
-
- // Read the hash table
- pHash = STORM_ALLOC(TMPQHash, MpqHeader.dwHashTableSize);
- FilePos.HighPart = 0;
- FilePos.LowPart = MpqHeader.dwHashTablePos;
- FileStream_Read(pStream, &FilePos, pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash));
-
- //
- // At this point, the encrypted hash table should be like this:
- //
- // 416c7175 5ddfb61b 84bb75c8 c0399515
- // a9793eca 9ec773d7 ed8a54d6 74fb2adf
- // 6acd4ae5 b13816b5 ffad2341 2f2b2a54
- // 614339c7 5fd0adf0 62434e91 d62439e3
- // 8f317aa5 f12706d6 bd83d2ca 97d7f108
- // 7586d373 51d85b05 8540beca f37ef3d7
- // d931d4d6 d592aadf 9044e960 c4592e92
- // 47dc03f7 0982dea4 afb31943 7c3c7cec
- // 0c28fd0d bcbfb7df 4d13b6e4 b5b0ef31
- // e1a33b70 ec30e4b9 7aaa5e7a fb6d46ec
- // 61732791 55fe757e 8ba18b5d d5f93246
- // 6d275f38 a89b5781 c34189a9 654c6472
- // 07e1d4e1 814bc8ee c72d2730 815afd43
- // 40bd2a92 640a9391 d868f813 0f61b73d
- // 6d202746 2c5124ca 65db3ad0 5b1c3e39
- // b731013c 73776405 eac0c746 6e50c938
- // a4a7fd00 56db3805 6d6dbab7 44fed28a
- // 2383394b bf617bdd a3edfaa2 e7d3aaaf
- //
-
- // Decrypt the hash table
-// DecryptMpqBlock(pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash), MPQ_KEY_HASH_TABLE);
-
- //
- // At this point, the hash table should be like this:
- //
- // c750beb9 72c2538a 00000000 00000466
- // ffffffff ffffffff ffffffff ffffffff
- // 898fdc7a 18963b5d 00000000 000005e1
- // ffffffff ffffffff ffffffff ffffffff
- // e3c6fc32 d8afff2b 00000000 000001ea
- // ffffffff ffffffff ffffffff ffffffff
- // ffffffff ffffffff ffffffff ffffffff
- // ffffffff ffffffff ffffffff ffffffff
- // ffffffff ffffffff ffffffff ffffffff
- // ffffffff ffffffff ffffffff ffffffff
- // ffffffff ffffffff ffffffff ffffffff
- // 0fa4fd60 3fbe8626 00000000 0000076f
- // 9ee5bccf 031b277b 00000000 0000095c
- // f4e154c5 0aadd1c1 00000000 00000876
- // 9e1ce9e7 e12d575d 00000000 0000071d
- //
-
- // Read the block table
- pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize);
- FilePos.HighPart = 0;
- FilePos.LowPart = MpqHeader.dwBlockTablePos;
- FileStream_Read(pStream, &FilePos, pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock));
+ char szString1[100] = "This is a single line\n\r";
+ char szString2[100];
+ DWORD dwLength = strlen(szString1);
- //
- // At this point, the encrypted block table should be like this:
- //
- // 3d4867a7 ca0f533e f82c54d6 ed3c9dec
- // d8d607dc d9ad13ab f4588b46 8d058704
- // e8084fc8 63bc8064 b058c777 3683e9e3
- // 6c0da998 7703be0d 91ce3607 c14e29b9
- // 481b5c0d 42d902d2 8302acb7 e8f3e715
- // c9cdfc91 7cc38c15 ea3dfd22 ad20c856
- // b6450c7f 08522866 4cedb064 e03e3a86
- // 4509c7cc ddffbfc3 82fc8c66 e82a4424
- // afc4a982 23169037 5af6a3e2 34e1d24e
- // 362c9e34 846cfc3d 4c611fcd d645fe8f
- // f4061640 6d08d196 f330a975 66e30993
- // fd96a033 2b16def6 62ff30af 3e190b0b
- // 664a5b91 b8558235 fd631825 a7807be7
- // ec906b9b 76d8b32e 36f3ea0b 1b0f5391
- //
-
- // Decrypt the block table
-// DecryptMpqBlock(pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock), MPQ_KEY_BLOCK_TABLE);
-
- //
- // At this point, the block table should be like this:
- //
- // 0000002c 00078093 00116824 84000200
- // 000780bf 000002d5 00008044 84000200
- // 00078394 00001516 0000874c 84000200
- // 000798aa 00003797 0000af4e 84000200
- // 0007d041 000001db 00008044 84000200
- // 0007d21c 0000005e 0000005e 84000200
- // 0007d27a 000022fb 00009674 84000200
- // 0007f575 00002389 00009c64 84000200
- // 000818fe 000023cb 00009d58 84000200
- // 00083cc9 000024d9 0000a0d8 84000200
- // 000861a2 00002356 00009c70 84000200
- // 000884f8 000023d3 00009da4 84000200
- // 0008a8cb 000022d6 00009cd4 84000200
- // 0008cba1 00002339 00009714 84000200
- // 0008eeda 000023dc 00009b24 84000200
- // 000912b6 00002481 00009eac 84000200
- // 00093737 00002444 0000a028 84000200
- // 00095b7b 00002440 00009fc4 84000200
- //
+ for(int i = 0; i < 10; i++)
+ {
+ if(!FileStream_Read(pStream, NULL, szString2, dwLength))
+ {
+ printf("Failed to read from the stream\n");
+ return -1;
+ }
+ szString2[dwLength] = 0;
+ if(strcmp(szString1, szString2))
+ {
+ printf("Data read from file are different from data written\n");
+ return -1;
+ }
+ }
FileStream_Close(pStream);
}
return 0;
}
*/
+
diff --git a/dep/StormLib/src/FileStream.h b/dep/StormLib/src/FileStream.h
new file mode 100644
index 00000000000..31fe757514d
--- /dev/null
+++ b/dep/StormLib/src/FileStream.h
@@ -0,0 +1,189 @@
+/*****************************************************************************/
+/* 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__
diff --git a/dep/StormLib/src/SBaseCommon.cpp b/dep/StormLib/src/SBaseCommon.cpp
index 9db708a2fe7..65818784521 100644
--- a/dep/StormLib/src/SBaseCommon.cpp
+++ b/dep/StormLib/src/SBaseCommon.cpp
@@ -15,12 +15,11 @@
#include "StormLib.h"
#include "StormCommon.h"
-char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2011";
+char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2012";
//-----------------------------------------------------------------------------
// The buffer for decryption engine.
-DWORD dwGlobalFlags = 0; // Global flags
LCID lcFileLocale = LANG_NEUTRAL; // File locale
USHORT wPlatform = 0; // File platform
@@ -319,6 +318,20 @@ int ConvertMpqHeaderToFormat4(
}
//-----------------------------------------------------------------------------
+// Default flags for (attributes) and (listfile)
+
+DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize)
+{
+ // Fixed for format 1.0
+ if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
+ return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY;
+
+ // Size-dependent for formats 2.0-4.0
+ return (dwFileSize > 0x4000) ? (MPQ_FILE_COMPRESS | MPQ_FILE_SECTOR_CRC) : (MPQ_FILE_COMPRESS | MPQ_FILE_SINGLE_UNIT);
+}
+
+
+//-----------------------------------------------------------------------------
// Encrypting and decrypting MPQ file data
void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1)
@@ -692,11 +705,9 @@ void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos)
FreeSpacePos = pFileEntry->ByteOffset + pFileEntry->dwCmpSize;
// Add the MD5 chunks, if present
- if(pHeader->dwRawChunkSize != 0)
+ if(pHeader->dwRawChunkSize != 0 && pFileEntry->dwCmpSize != 0)
{
- dwChunkCount = pFileEntry->dwCmpSize / pHeader->dwRawChunkSize;
- if(pFileEntry->dwCmpSize % pHeader->dwRawChunkSize)
- dwChunkCount++;
+ dwChunkCount = ((pFileEntry->dwCmpSize - 1) / pHeader->dwRawChunkSize) + 1;
FreeSpacePos += dwChunkCount * MD5_DIGEST_SIZE;
}
}
@@ -773,7 +784,7 @@ int LoadMpqTable(
int cbOutBuffer = (int)dwRealSize;
int cbInBuffer = (int)dwCompressedSize;
- if(!SCompDecompress((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer))
+ if(!SCompDecompress2((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer))
nError = GetLastError();
// Free the temporary buffer
@@ -828,13 +839,11 @@ unsigned char * AllocateMd5Buffer(
DWORD cbMd5Size;
// Sanity check
+ assert(dwRawDataSize != 0);
assert(dwChunkSize != 0);
// Calculate how many MD5's we will calculate
- cbMd5Size = dwRawDataSize / dwChunkSize;
- if(dwRawDataSize % dwChunkSize)
- cbMd5Size++;
- cbMd5Size *= MD5_DIGEST_SIZE;
+ cbMd5Size = (((dwRawDataSize - 1) / dwChunkSize) + 1) * MD5_DIGEST_SIZE;
// Allocate space for array or MD5s
md5_array = STORM_ALLOC(BYTE, cbMd5Size);
@@ -853,9 +862,12 @@ int AllocateSectorBuffer(TMPQFile * hf)
// Caller of AllocateSectorBuffer must ensure these
assert(hf->pbFileSector == NULL);
assert(hf->pFileEntry != NULL);
- assert(hf->dwDataSize != 0);
assert(hf->ha != NULL);
+ // Don't allocate anything if the file has zero size
+ if(hf->pFileEntry->dwFileSize == 0 || hf->dwDataSize == 0)
+ return ERROR_SUCCESS;
+
// Determine the file sector size and allocate buffer for it
hf->dwSectorSize = (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) ? hf->dwDataSize : ha->dwSectorSize;
hf->pbFileSector = STORM_ALLOC(BYTE, hf->dwSectorSize);
@@ -889,7 +901,7 @@ __AllocateAndLoadPatchInfo:
// Load the patch header
if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength))
{
- // Free the sector offsets
+ // Free the patch info
STORM_FREE(hf->pPatchInfo);
hf->pPatchInfo = NULL;
return GetLastError();
@@ -904,10 +916,14 @@ __AllocateAndLoadPatchInfo:
// If it's not default size, we have to reload them
if(hf->pPatchInfo->dwLength > dwLength)
{
+ // Free the patch info
dwLength = hf->pPatchInfo->dwLength;
STORM_FREE(hf->pPatchInfo);
hf->pPatchInfo = NULL;
+ // If the length is out of all possible ranges, fail the operation
+ if(dwLength > 0x400)
+ return ERROR_FILE_CORRUPT;
goto __AllocateAndLoadPatchInfo;
}
@@ -947,6 +963,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
}
// Calculate the number of data sectors
+ // Note that this doesn't work if the file size is zero
hf->dwSectorCount = ((hf->dwDataSize - 1) / hf->dwSectorSize) + 1;
// Calculate the number of file sectors
@@ -961,6 +978,8 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
// Only allocate and load the table if the file is compressed
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
+ __LoadSectorOffsets:
+
// Allocate the sector offset table
hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen);
if(hf->SectorOffsets == NULL)
@@ -1044,10 +1063,18 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
// There may be various extra DWORDs loaded after the sector offset table.
// They are mostly empty on WoW release MPQs, but on MPQs from PTR,
// they contain random non-zero data. Their meaning is unknown.
- // At this point, we completely ignore them
+ //
+ // These extra values are, however, include in the dwCmpSize in the file
+ // table. We cannot ignore them, because compacting archive would fail
//
-// assert(dwSectorOffsLen == hf->SectorOffsets[0]);
+ if(hf->SectorOffsets[0] > dwSectorOffsLen)
+ {
+ dwSectorOffsLen = hf->SectorOffsets[0];
+ STORM_FREE(hf->SectorOffsets);
+ hf->SectorOffsets = NULL;
+ goto __LoadSectorOffsets;
+ }
}
else
{
@@ -1388,6 +1415,10 @@ void FreeMPQArchive(TMPQArchive *& ha)
if(ha->haPatch != NULL)
FreeMPQArchive(ha->haPatch);
+ // Close the file stream
+ FileStream_Close(ha->pStream);
+ ha->pStream = NULL;
+
// Free the file names from the file table
if(ha->pFileTable != NULL)
{
@@ -1402,11 +1433,12 @@ void FreeMPQArchive(TMPQArchive *& ha)
STORM_FREE(ha->pFileTable);
}
+ if(ha->pBitmap != NULL)
+ STORM_FREE(ha->pBitmap);
if(ha->pHashTable != NULL)
STORM_FREE(ha->pHashTable);
if(ha->pHetTable != NULL)
FreeHetTable(ha->pHetTable);
- FileStream_Close(ha->pStream);
STORM_FREE(ha);
ha = NULL;
}
diff --git a/dep/StormLib/src/SBaseDumpData.cpp b/dep/StormLib/src/SBaseDumpData.cpp
index f2b5fad1e8f..c9dcf0a3366 100644
--- a/dep/StormLib/src/SBaseDumpData.cpp
+++ b/dep/StormLib/src/SBaseDumpData.cpp
@@ -17,18 +17,18 @@
void DumpMpqHeader(TMPQHeader * pHeader)
{
printf("== MPQ Header =================================\n");
- printf("DWORD dwID = %08lX\n", pHeader->dwID);
- printf("DWORD dwHeaderSize = %08lX\n", pHeader->dwHeaderSize);
- printf("DWORD dwArchiveSize = %08lX\n", pHeader->dwArchiveSize);
- printf("USHORT wFormatVersion = %04lX\n", pHeader->wFormatVersion);
- printf("USHORT wSectorSize = %04lX\n", pHeader->wSectorSize);
- printf("DWORD dwHashTablePos = %08lX\n", pHeader->dwHashTablePos);
- printf("DWORD dwBlockTablePos = %08lX\n", pHeader->dwBlockTablePos);
- printf("DWORD dwHashTableSize = %08lX\n", pHeader->dwHashTableSize);
- printf("DWORD dwBlockTableSize = %08lX\n", pHeader->dwBlockTableSize);
+ 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 = %04lX\n", pHeader->wHashTablePosHi);
- printf("USHORT wBlockTablePosHi = %04lX\n", pHeader->wBlockTablePosHi);
+ 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);
@@ -37,7 +37,7 @@ void DumpMpqHeader(TMPQHeader * pHeader)
printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64);
printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64);
printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64);
- printf("DWORD dwRawChunkSize = %08lX\n", pHeader->dwRawChunkSize);
+ printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize);
printf("-----------------------------------------------\n\n");
}
@@ -51,31 +51,31 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
printf("== HET Header =================================\n");
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
- printf("DWORD dwIndexSizeTotal = %08lX\n", pHetTable->dwIndexSizeTotal);
- printf("DWORD dwIndexSizeExtra = %08lX\n", pHetTable->dwIndexSizeExtra);
- printf("DWORD dwIndexSize = %08lX\n", pHetTable->dwIndexSize);
- printf("DWORD dwMaxFileCount = %08lX\n", pHetTable->dwMaxFileCount);
- printf("DWORD dwHashTableSize = %08lX\n", pHetTable->dwHashTableSize);
- printf("DWORD dwHashBitSize = %08lX\n", pHetTable->dwHashBitSize);
+ 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 = %08lX\n", pBetTable->dwTableEntrySize);
- printf("DWORD dwBitIndex_FilePos = %08lX\n", pBetTable->dwBitIndex_FilePos);
- printf("DWORD dwBitIndex_FileSize = %08lX\n", pBetTable->dwBitIndex_FileSize);
- printf("DWORD dwBitIndex_CmpSize = %08lX\n", pBetTable->dwBitIndex_CmpSize);
- printf("DWORD dwBitIndex_FlagIndex = %08lX\n", pBetTable->dwBitIndex_FlagIndex);
- printf("DWORD dwBitIndex_Unknown = %08lX\n", pBetTable->dwBitIndex_Unknown);
- printf("DWORD dwBitCount_FilePos = %08lX\n", pBetTable->dwBitCount_FilePos);
- printf("DWORD dwBitCount_FileSize = %08lX\n", pBetTable->dwBitCount_FileSize);
- printf("DWORD dwBitCount_CmpSize = %08lX\n", pBetTable->dwBitCount_CmpSize);
- printf("DWORD dwBitCount_FlagIndex = %08lX\n", pBetTable->dwBitCount_FlagIndex);
- printf("DWORD dwBitCount_Unknown = %08lX\n", pBetTable->dwBitCount_Unknown);
- printf("DWORD dwBetHashSizeTotal = %08lX\n", pBetTable->dwBetHashSizeTotal);
- printf("DWORD dwBetHashSizeExtra = %08lX\n", pBetTable->dwBetHashSizeExtra);
- printf("DWORD dwBetHashSize = %08lX\n", pBetTable->dwBetHashSize);
- printf("DWORD dwMaxFileCount = %08lX\n", pBetTable->dwMaxFileCount);
- printf("DWORD dwFlagCount = %08lX\n", pBetTable->dwFlagCount);
+ 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");
@@ -128,7 +128,7 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
}
- printf(" %04lX %02lX %04lX %016llX %016llX %08lX %08lX %04lX %08lX\n", i,
+ printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
pHetTable->pHetHashes[i],
dwBetIndex,
BetHash,
diff --git a/dep/StormLib/src/SBaseFileTable.cpp b/dep/StormLib/src/SBaseFileTable.cpp
index 83d31bbac94..09379ca5da7 100644
--- a/dep/StormLib/src/SBaseFileTable.cpp
+++ b/dep/StormLib/src/SBaseFileTable.cpp
@@ -15,7 +15,8 @@
//-----------------------------------------------------------------------------
// Local defines
-#define MAX_FLAG_INDEX 256
+#define INVALID_FLAG_VALUE 0xCCCCCCCC
+#define MAX_FLAG_INDEX 512
//-----------------------------------------------------------------------------
// Local structures
@@ -62,63 +63,10 @@ typedef struct _BET_TABLE_HEADER
//-----------------------------------------------------------------------------
// Support for calculating bit sizes
-static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
-{
- DWORD dwBitCount = 0;
-
- while(MaxValue > 0)
- {
- MaxValue >>= 1;
- dwBitCount++;
- }
-
- return dwBitCount;
-}
-
-static BYTE GetFlagsComboIndex(DWORD dwFlags)
+static void InitFileFlagArray(LPDWORD FlagArray)
{
- BYTE FlagComboIndex = 0;
-
- //
- // We only use 8 different bits. This allows us to
- // construct 256 unique values for every possible combination of MPQ file flags.
- //
-
- if(dwFlags & MPQ_FILE_IMPLODE)
- FlagComboIndex |= 0x01;
- dwFlags &= ~MPQ_FILE_IMPLODE;
-
- if(dwFlags & MPQ_FILE_COMPRESS)
- FlagComboIndex |= 0x02;
- dwFlags &= ~MPQ_FILE_COMPRESS;
-
- if(dwFlags & MPQ_FILE_ENCRYPTED)
- FlagComboIndex |= 0x04;
- dwFlags &= ~MPQ_FILE_ENCRYPTED;
-
- if(dwFlags & MPQ_FILE_FIX_KEY)
- FlagComboIndex |= 0x08;
- dwFlags &= ~MPQ_FILE_FIX_KEY;
-
- if(dwFlags & MPQ_FILE_PATCH_FILE)
- FlagComboIndex |= 0x10;
- dwFlags &= ~MPQ_FILE_PATCH_FILE;
-
- if(dwFlags & MPQ_FILE_SINGLE_UNIT)
- FlagComboIndex |= 0x20;
- dwFlags &= ~MPQ_FILE_SINGLE_UNIT;
-
- if(dwFlags & MPQ_FILE_DELETE_MARKER)
- FlagComboIndex |= 0x40;
- dwFlags &= ~MPQ_FILE_DELETE_MARKER;
-
- if(dwFlags & MPQ_FILE_SECTOR_CRC)
- FlagComboIndex |= 0x80;
- dwFlags &= ~MPQ_FILE_SECTOR_CRC;
-
- // Sanity check - the flags must now be zero
- assert((dwFlags & 0x7FFFFFFF) == 0);
- return FlagComboIndex;
+ for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
+ FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE;
}
static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
@@ -126,7 +74,7 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
// Find free or equal entry in the flag array
for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
{
- if(FlagArray[dwFlagIndex] == 0 || FlagArray[dwFlagIndex] == dwFlags)
+ if(FlagArray[dwFlagIndex] == INVALID_FLAG_VALUE || FlagArray[dwFlagIndex] == dwFlags)
{
FlagArray[dwFlagIndex] = dwFlags;
return dwFlagIndex;
@@ -138,6 +86,19 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
return 0xFFFFFFFF;
}
+static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
+{
+ DWORD dwBitCount = 0;
+
+ while(MaxValue > 0)
+ {
+ MaxValue >>= 1;
+ dwBitCount++;
+ }
+
+ return dwBitCount;
+}
+
//-----------------------------------------------------------------------------
// Support functions for BIT_ARRAY
@@ -420,8 +381,7 @@ static TMPQBlock * TranslateBlockTable(
BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize;
for(DWORD i = 0; i < ha->dwFileTableSize; i++)
{
- if(pFileEntry->ByteOffset >> 32)
- bNeedHiBlockTable = true;
+ bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false;
pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset;
pBlock->dwFSize = pFileEntry->dwFileSize;
pBlock->dwCSize = pFileEntry->dwCmpSize;
@@ -519,11 +479,15 @@ TMPQExtTable * LoadExtTable(
int cbOutBuffer = (int)pCompressed->dwDataSize;
int cbInBuffer = (int)Size;
- // Decompress the XXX block
+ // Decompress the extended table
pExtTable->dwSignature = pCompressed->dwSignature;
pExtTable->dwVersion = pCompressed->dwVersion;
pExtTable->dwDataSize = pCompressed->dwDataSize;
- SCompDecompress((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer);
+ if(!SCompDecompress2((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer))
+ {
+ STORM_FREE(pExtTable);
+ pExtTable = NULL;
+ }
}
// Free the compressed block
@@ -537,9 +501,9 @@ TMPQExtTable * LoadExtTable(
}
// Used in MPQ Editor
-void FreeExtTable(TMPQExtTable * pExtTable)
+void FreeMpqBuffer(void * pvBuffer)
{
- STORM_FREE(pExtTable);
+ STORM_FREE(pvBuffer);
}
static int SaveMpqTable(
@@ -1018,26 +982,18 @@ static void CreateBetHeader(
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
ULONGLONG MaxByteOffset = 0;
+ DWORD FlagArray[MAX_FLAG_INDEX];
+ DWORD dwMaxFlagIndex = 0;
DWORD dwMaxFileSize = 0;
DWORD dwMaxCmpSize = 0;
- DWORD dwFlagCount = 0;
- BYTE FlagComboArray[MAX_FLAG_INDEX];
- BYTE FlagComboIndex;
+ DWORD dwFlagIndex;
// Initialize array of flag combinations
- memset(FlagComboArray, 0, sizeof(FlagComboArray));
+ InitFileFlagArray(FlagArray);
// Get the maximum values for the BET table
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
- // We don't allow the file table to have free entries in the middle
- // This must be a bug in the library somewhere.
- if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
- {
- pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
- assert(false);
- }
-
// Highest file position in the MPQ
if(pFileEntry->ByteOffset > MaxByteOffset)
MaxByteOffset = pFileEntry->ByteOffset;
@@ -1051,10 +1007,9 @@ static void CreateBetHeader(
dwMaxCmpSize = pFileEntry->dwCmpSize;
// Check if this flag was there before
- FlagComboIndex = GetFlagsComboIndex(pFileEntry->dwFlags);
- if(FlagComboArray[FlagComboIndex] == 0)
- dwFlagCount++;
- FlagComboArray[FlagComboIndex] = 1;
+ dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
+ if(dwFlagIndex > dwMaxFlagIndex)
+ dwMaxFlagIndex = dwFlagIndex;
}
// Now save bit count for every piece of file information
@@ -1068,7 +1023,7 @@ static void CreateBetHeader(
pBetHeader->dwBitCount_CmpSize = GetNecessaryBitCount(dwMaxCmpSize);
pBetHeader->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_CmpSize + pBetHeader->dwBitCount_CmpSize;
- pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwFlagCount);
+ pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwMaxFlagIndex + 1);
pBetHeader->dwBitIndex_Unknown = pBetHeader->dwBitIndex_FlagIndex + pBetHeader->dwBitCount_FlagIndex;
pBetHeader->dwBitCount_Unknown = 0;
@@ -1082,7 +1037,7 @@ static void CreateBetHeader(
// Save the file count and flag count
pBetHeader->dwFileCount = ha->dwFileTableSize;
- pBetHeader->dwFlagCount = dwFlagCount;
+ pBetHeader->dwFlagCount = dwMaxFlagIndex + 1;
pBetHeader->dwUnknown08 = 0x10;
// Save the total size of the BET hash
@@ -1224,8 +1179,8 @@ TMPQExtTable * TranslateBetTable(
DWORD i;
// Calculate the bit sizes of various entries
+ InitFileFlagArray(FlagArray);
CreateBetHeader(ha, &BetHeader);
- memset(FlagArray, 0, sizeof(FlagArray));
// Calculate the size of the BET table
BetTableSize = sizeof(BET_TABLE_HEADER) +
@@ -1260,30 +1215,32 @@ TMPQExtTable * TranslateBetTable(
// Construct the array of flag values and bit-based file table
for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++)
{
- // Only count files that exist
- if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
- {
- // Save the byte offset
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
- BetHeader.dwBitCount_FilePos,
- &pFileEntry->ByteOffset,
- 8);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
- BetHeader.dwBitCount_FileSize,
- &pFileEntry->dwFileSize,
- 4);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
- BetHeader.dwBitCount_CmpSize,
- &pFileEntry->dwCmpSize,
- 4);
-
- // Get the flag array for the file
- dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
- BetHeader.dwBitCount_FlagIndex,
- &dwFlagIndex,
- 4);
- }
+ //
+ // Note: Blizzard MPQs contain valid values even for non-existant files
+ // (FilePos, FileSize, CmpSize and FlagIndex)
+ // Note: If flags is zero, it must be in the flag table too !!!
+ //
+
+ // Save the byte offset
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
+ BetHeader.dwBitCount_FilePos,
+ &pFileEntry->ByteOffset,
+ 8);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
+ BetHeader.dwBitCount_FileSize,
+ &pFileEntry->dwFileSize,
+ 4);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
+ BetHeader.dwBitCount_CmpSize,
+ &pFileEntry->dwCmpSize,
+ 4);
+
+ // Save the flag index
+ dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
+ BetHeader.dwBitCount_FlagIndex,
+ &dwFlagIndex,
+ 4);
// Move the bit offset
nBitOffset += BetHeader.dwTableEntrySize;
@@ -1480,7 +1437,7 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName)
TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
- TFileEntry * pDeletedEntry = NULL;
+ TFileEntry * pFreeEntry = NULL;
TFileEntry * pFileEntry;
// Try to find a free entry
@@ -1488,18 +1445,22 @@ TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
// If that entry is free, we reuse it
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
- return pFileEntry;
+ {
+ pFreeEntry = pFileEntry;
+ break;
+ }
- // If that entry is deleted, remember it
- if(pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER)
- pDeletedEntry = pFileEntry;
+ //
+ // Note: Files with "delete marker" are not deleted.
+ // Don't consider them free entries
+ //
}
// Do we have a deleted entry?
- if(pDeletedEntry != NULL)
+ if(pFreeEntry != NULL)
{
- ClearFileEntry(ha, pDeletedEntry);
- return pDeletedEntry;
+ ClearFileEntry(ha, pFreeEntry);
+ return pFreeEntry;
}
// If no file entry within the existing file table is free,
@@ -1702,9 +1663,11 @@ void ClearFileEntry(
4);
}
- // Free the file name, and zero the entire entry
+ // Free the file name, and set the file entry as deleted
if(pFileEntry->szFileName != NULL)
STORM_FREE(pFileEntry->szFileName);
+
+ // Invalidate the file entry
memset(pFileEntry, 0, sizeof(TFileEntry));
}
@@ -1718,7 +1681,7 @@ int FreeFileEntry(
//
// If we have HET table, we cannot just get rid of the file
- // Doing so would lead to empty gaps in the HET and BET tables
+ // Doing so would lead to empty gaps in the HET table
// We have to keep BET hash, hash index, HET index, locale, platform and file name
//
@@ -1750,14 +1713,10 @@ int FreeFileEntry(
}
else
{
- memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE);
- pFileEntry->ByteOffset = 0;
- pFileEntry->FileTime = 0;
- pFileEntry->dwFileSize = 0;
- pFileEntry->dwCmpSize = 0;
- pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
- pFileEntry->dwCrc32 = 0;
- nError = ERROR_MARKED_FOR_DELETE;
+ // Note: Deleted entries in Blizzard MPQs version 4.0
+ // normally contain valid byte offset and length
+ pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS;
+ nError = ERROR_SUCCESS;
}
return nError;
@@ -1785,49 +1744,104 @@ void InvalidateInternalFiles(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES;
}
- // Remember that the MPQ has been changed and it will ne necessary
+ // Remember that the MPQ has been changed and it will be necessary
// to update the tables
ha->dwFlags |= MPQ_FLAG_CHANGED;
}
//-----------------------------------------------------------------------------
-// Support for file tables - hash table, block table, hi-block table,
-// (attributes) and (listfile)
+// Functions that loads and verify MPQ data bitmap
-static void FixBlockTableSize(
- TMPQArchive * ha,
- TMPQBlock * pBlockTable,
- DWORD dwClaimedSize)
+int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete)
{
- TMPQHeader * pHeader = ha->pHeader;
- ULONGLONG BlockTableStart;
- ULONGLONG BlockTableEnd;
- ULONGLONG FileDataStart;
+ TMPQBitmap * pBitmap = NULL;
+ TMPQBitmap DataBitmap;
+ ULONGLONG BitmapOffset;
+ ULONGLONG EndOfMpq;
+ DWORD DataBlockCount = 0;
+ DWORD BitmapByteSize;
+ DWORD WholeByteCount;
+ DWORD ExtraBitsCount;
+
+ // Is there enough space for a MPQ bitmap?
+ EndOfMpq = ha->MpqPos + ha->pHeader->ArchiveSize64;
+ FileSize = FileSize - sizeof(TMPQBitmap);
+ if(FileSize > EndOfMpq)
+ {
+ // Try to load the data bitmap from the end of the file
+ if(FileStream_Read(ha->pStream, &FileSize, &DataBitmap, sizeof(TMPQBitmap)))
+ {
+ // Is it a valid data bitmap?
+ BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap));
+ if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE)
+ {
+ // We assume that MPQs with data bitmap begin at position 0
+ assert(ha->MpqPos == 0);
- // Only perform this check on MPQs version 1.0
- if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1)
+ // Calculate the number of extra bytes for data bitmap
+ DataBlockCount = (DWORD)(((ha->pHeader->ArchiveSize64 - 1) / DataBitmap.dwBlockSize) + 1);
+ BitmapByteSize = ((DataBlockCount - 1) / 8) + 1;
+
+ // Verify the data block size
+ BitmapOffset = ((ULONGLONG)DataBitmap.dwMapOffsetHi << 32) | DataBitmap.dwMapOffsetLo;
+ assert((DWORD)(FileSize - BitmapOffset) == BitmapByteSize);
+
+ // Allocate space for the data bitmap
+ pBitmap = (TMPQBitmap *)STORM_ALLOC(BYTE, sizeof(TMPQBitmap) + BitmapByteSize);
+ if(pBitmap != NULL)
+ {
+ // Copy the bitmap header
+ memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap));
+
+ // Read the remaining part
+ if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize))
+ {
+ STORM_FREE(pBitmap);
+ pBitmap = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ // If the caller asks for file completeness, check it
+ if(pBitmap != NULL && pbFileIsComplete != NULL)
{
- // Calculate claimed block table begin and end
- BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
- BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
+ LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1);
+ DWORD i;
+ bool bFileIsComplete = true;
- for(DWORD i = 0; i < dwClaimedSize; i++)
+ // Calculate the number of whole bytes and extra bits of the bitmap
+ WholeByteCount = (DataBlockCount / 8);
+ ExtraBitsCount = (DataBlockCount & 7);
+
+ // Verify the whole bytes - their value must be 0xFF
+ for(i = 0; i < WholeByteCount; i++)
{
- // If the block table end goes into that file, fix the block table end
- FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos;
- if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart)
- {
- dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock));
- BlockTableEnd = FileDataStart;
- }
+ if(pbBitmap[i] != 0xFF)
+ bFileIsComplete = false;
+ }
+
+ // If there are extra bits, calculate the mask
+ if(ExtraBitsCount != 0)
+ {
+ BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1);
+
+ if(pbBitmap[i] != ExpectedValue)
+ bFileIsComplete = false;
}
+
+ // Give the result to the caller
+ *pbFileIsComplete = bFileIsComplete;
}
- // Fix the block table size
- pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock);
- pHeader->dwBlockTableSize = dwClaimedSize;
+ ha->pBitmap = pBitmap;
+ return ERROR_SUCCESS;
}
+//-----------------------------------------------------------------------------
+// Support for file tables - hash table, block table, hi-block table
+
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
{
TMPQHash * pHashTable;
@@ -1851,7 +1865,7 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
return ERROR_SUCCESS;
}
-int LoadHashTable(TMPQArchive * ha)
+TMPQHash * LoadHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG ByteOffset;
@@ -1862,17 +1876,17 @@ int LoadHashTable(TMPQArchive * ha)
// If the MPQ has no hash table, do nothing
if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
- return ERROR_SUCCESS;
+ return NULL;
// If the hash table size is zero, do nothing
if(pHeader->dwHashTableSize == 0)
- return ERROR_SUCCESS;
+ return NULL;
// Allocate buffer for the hash table
dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
if(pHashTable == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ return NULL;
// Compressed size of the hash table
dwCmpSize = (DWORD)pHeader->HashTableSize64;
@@ -1891,17 +1905,108 @@ int LoadHashTable(TMPQArchive * ha)
{
STORM_FREE(pHashTable);
pHashTable = NULL;
- return nError;
}
- // Set the maximum file count to the size of the hash table
- // In case there is HET table, we have to keep the file limit
- if(ha->pHetTable == NULL)
- ha->dwMaxFileCount = pHeader->dwHashTableSize;
+ // Return the hash table
+ return pHashTable;
+}
- // Store the hash table to the MPQ
- ha->pHashTable = pHashTable;
- return ERROR_SUCCESS;
+static void FixBlockTableSize(
+ TMPQArchive * ha,
+ TMPQBlock * pBlockTable,
+ DWORD dwClaimedSize)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ ULONGLONG BlockTableStart;
+ ULONGLONG BlockTableEnd;
+ ULONGLONG FileDataStart;
+
+ // Only perform this check on MPQs version 1.0
+ if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1)
+ {
+ // Calculate claimed block table begin and end
+ BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
+ BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
+
+ for(DWORD i = 0; i < dwClaimedSize; i++)
+ {
+ // If the block table end goes into that file, fix the block table end
+ FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos;
+ if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart)
+ {
+ dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock));
+ BlockTableEnd = FileDataStart;
+ }
+ }
+ }
+
+ // Fix the block table size
+ pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock);
+ pHeader->dwBlockTableSize = dwClaimedSize;
+}
+
+TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ TMPQBlock * pBlockTable;
+ ULONGLONG ByteOffset;
+ DWORD dwTableSize;
+ DWORD dwCmpSize;
+ int nError;
+
+ // Do nothing if the block table position is zero
+ if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0)
+ return NULL;
+
+ // Do nothing if the block table size is zero
+ if(pHeader->dwBlockTableSize == 0)
+ return NULL;
+
+ // Sanity check, enforced by LoadAnyHashTable
+ assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
+
+ // Calculate sizes of both tables
+ ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
+ dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ dwCmpSize = (DWORD)pHeader->BlockTableSize64;
+
+ // Allocate space for the block table
+ // Note: pHeader->dwBlockTableSize can be zero !!!
+ pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
+ if(pBlockTable == NULL)
+ return NULL;
+
+ // Fill the block table with zeros
+ memset(pBlockTable, 0, dwTableSize);
+
+ // I found a MPQ which claimed 0x200 entries in the block table,
+ // but the file was cut and there was only 0x1A0 entries.
+ // We will handle this case properly.
+ if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
+ {
+ pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
+ pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ }
+
+ //
+ // One of the first cracked versions of Diablo I had block table unencrypted
+ // StormLib does NOT support such MPQs anymore, as they are incompatible
+ // with compressed block table feature
+ //
+
+ // Load the block table
+ nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
+ if(nError != ERROR_SUCCESS)
+ {
+ // Failed, sorry
+ STORM_FREE(pBlockTable);
+ return NULL;
+ }
+
+ // Defense against MPQs that claim block table to be bigger than it really is
+ FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
+ return pBlockTable;
}
int LoadHetTable(TMPQArchive * ha)
@@ -1963,19 +2068,25 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha)
int LoadAnyHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
- bool bHashTableLoaded = false;
// If the MPQ archive is empty, don't bother trying to load anything
if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0)
return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT);
- // Try to load HET table
- if(LoadHetTable(ha) == ERROR_SUCCESS)
- bHashTableLoaded = true;
+ // Try to load HET and/or classic hash table
+ LoadHetTable(ha);
- // Try to load the classic hash table
- if(LoadHashTable(ha) == ERROR_SUCCESS)
- bHashTableLoaded = true;
+ // Load the HASH table
+ ha->pHashTable = LoadHashTable(ha);
+
+ // Set the maximum file count to the size of the hash table
+ // In case there is HET table, we have to keep the file limit
+ if(ha->pHetTable == NULL)
+ ha->dwMaxFileCount = pHeader->dwHashTableSize;
+
+ // Did at least one succeed?
+ if(ha->pHetTable == NULL && ha->pHashTable == NULL)
+ return ERROR_FILE_CORRUPT;
// In theory, a MPQ could have bigger block table than hash table
if(ha->pHeader->dwBlockTableSize > ha->dwMaxFileCount)
@@ -1984,7 +2095,7 @@ int LoadAnyHashTable(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
}
- return bHashTableLoaded ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
+ return ERROR_SUCCESS;
}
int BuildFileTable_Classic(
@@ -2001,118 +2112,80 @@ int BuildFileTable_Classic(
// Sanity checks
assert(ha->pHashTable != NULL);
- // Do nothing if the size of the block table is zero
- if(pHeader->dwBlockTablePos != 0 && pHeader->dwBlockTableSize != 0)
+ // Load the block table
+ pBlockTable = LoadBlockTable(ha, FileSize);
+ if(pBlockTable != NULL)
{
- // Sanity check, enforced by LoadAnyHashTable
- assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
+ TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
+ TMPQHash * pHash;
- // Allocate space for the block table
- // Note: pHeader->dwBlockTableSize can be zero !!!
- pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
- if(pBlockTable != NULL)
+ // If we don't have HET table, we build the file entries from the hash&block tables
+ if(ha->pHetTable == NULL)
{
- ULONGLONG ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
- TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
- TMPQHash * pHash;
- DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- DWORD dwCmpSize = (DWORD)pHeader->BlockTableSize64;
-
- // Fill the block table with zeros
- memset(pBlockTable, 0, dwTableSize);
-
- // I have found a MPQ which claimed 0x200 entries in the block table,
- // but the file was cut and there was only 0x1A0 entries.
- // We will handle this case properly.
- if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
+ for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
- pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
- pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- }
-
- //
- // One of the first cracked versions of Diablo I had block table unencrypted
- // StormLib does NOT support such MPQs anymore, as they are incompatible
- // with compressed block table feature
- //
-
- // Load the block table
- nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
- if(nError == ERROR_SUCCESS)
- {
- // Defense against MPQs that claim block table to be bigger than it really is
- FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
-
- // If we don't have HET table, we build the file entries from the hash&block tables
- if(ha->pHetTable == NULL)
+ if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
{
- for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ pFileEntry = pFileTable + pHash->dwBlockIndex;
+ pBlock = pBlockTable + pHash->dwBlockIndex;
+
+ //
+ // Yet another silly map protector: For each valid file,
+ // there are 4 items in the hash table, that appears to be valid:
+ //
+ // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid
+ // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid
+ // a6d79af0 e61a0932 00000000 0000002f <== Real file entry
+ // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid
+ //
+
+ if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS))
{
- if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
- {
- pFileEntry = pFileTable + pHash->dwBlockIndex;
- pBlock = pBlockTable + pHash->dwBlockIndex;
-
- //
- // Yet another silly map protector: For each valid file,
- // there are 4 items in the hash table, that appears to be valid:
- //
- // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid
- // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid
- // a6d79af0 e61a0932 00000000 0000002f <== Real file entry
- // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid
- //
-
- if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS))
- {
- // Fill the entry
- pFileEntry->ByteOffset = pBlock->dwFilePos;
- pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
- pFileEntry->dwFileSize = pBlock->dwFSize;
- pFileEntry->dwCmpSize = pBlock->dwCSize;
- pFileEntry->dwFlags = pBlock->dwFlags;
- pFileEntry->lcLocale = pHash->lcLocale;
- pFileEntry->wPlatform = pHash->wPlatform;
- }
- else
- {
- // If the hash table entry doesn't point to the valid file item,
- // we invalidate the entire hash table entry
- pHash->dwName1 = 0xFFFFFFFF;
- pHash->dwName2 = 0xFFFFFFFF;
- pHash->lcLocale = 0xFFFF;
- pHash->wPlatform = 0xFFFF;
- pHash->dwBlockIndex = HASH_ENTRY_DELETED;
- }
- }
+ // Fill the entry
+ pFileEntry->ByteOffset = pBlock->dwFilePos;
+ pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
+ pFileEntry->dwFileSize = pBlock->dwFSize;
+ pFileEntry->dwCmpSize = pBlock->dwCSize;
+ pFileEntry->dwFlags = pBlock->dwFlags;
+ pFileEntry->lcLocale = pHash->lcLocale;
+ pFileEntry->wPlatform = pHash->wPlatform;
}
- }
- else
- {
- for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ else
{
- if(pHash->dwBlockIndex < ha->dwFileTableSize)
- {
- pFileEntry = pFileTable + pHash->dwBlockIndex;
- if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
- {
- pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
- pFileEntry->lcLocale = pHash->lcLocale;
- pFileEntry->wPlatform = pHash->wPlatform;
- }
- }
+ // If the hash table entry doesn't point to the valid file item,
+ // we invalidate the entire hash table entry
+ pHash->dwName1 = 0xFFFFFFFF;
+ pHash->dwName2 = 0xFFFFFFFF;
+ pHash->lcLocale = 0xFFFF;
+ pHash->wPlatform = 0xFFFF;
+ pHash->dwBlockIndex = HASH_ENTRY_DELETED;
}
}
}
-
- // Free the block table
- STORM_FREE(pBlockTable);
}
else
{
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ {
+ if(pHash->dwBlockIndex < ha->dwFileTableSize)
+ {
+ pFileEntry = pFileTable + pHash->dwBlockIndex;
+ if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
+ {
+ pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
+ pFileEntry->lcLocale = pHash->lcLocale;
+ pFileEntry->wPlatform = pHash->wPlatform;
+ }
+ }
+ }
}
+
+ // Free the block table
+ STORM_FREE(pBlockTable);
+ }
+ else
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Load the hi-block table
diff --git a/dep/StormLib/src/SCompression.cpp b/dep/StormLib/src/SCompression.cpp
index 88351499ef7..5c7432248dc 100644
--- a/dep/StormLib/src/SCompression.cpp
+++ b/dep/StormLib/src/SCompression.cpp
@@ -175,7 +175,7 @@ void Compress_ZLIB(
// Storm.dll uses zlib version 1.1.3
// Wow.exe uses zlib version 1.2.3
nResult = deflateInit2(&z,
- Z_DEFAULT_COMPRESSION,
+ 6, // Compression level used by WoW MPQs
Z_DEFLATED,
windowBits,
8,
@@ -292,7 +292,13 @@ static void Compress_PKLIB(
Info.pbOutBuff = pbOutBuffer;
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
- // Set the compression type and dictionary size
+ //
+ // Set the dictionary size
+ //
+ // Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
+ // Starcraft uses the variable dictionary size based on algorithm below
+ //
+
if (cbInBuffer < 0x600)
dict_size = CMP_IMPLODE_DICT_SIZE1;
else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
@@ -455,7 +461,7 @@ static void LZMA_Callback_Free(void *p, void *address)
// the data compressed by StormLib.
//
-static void Compress_LZMA(
+/*static */ void Compress_LZMA(
char * pbOutBuffer,
int * pcbOutBuffer,
char * pbInBuffer,
@@ -752,13 +758,13 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe
static TCompressTable cmp_table[] =
{
- {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
- {MPQ_COMPRESSION_WAVE_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
- {MPQ_COMPRESSION_WAVE_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
- {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
- {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
- {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
- {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
+ {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
+ {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
+ {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
+ {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
+ {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
+ {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
+ {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
};
int WINAPI SCompCompress(
@@ -904,13 +910,13 @@ int WINAPI SCompCompress(
// of compressed data
static TDecompressTable dcmp_table[] =
{
- {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library
- {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
- {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library
- {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
- {MPQ_COMPRESSION_WAVE_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression
- {MPQ_COMPRESSION_WAVE_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression
- {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
+ {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library
+ {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
+ {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library
+ {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
+ {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression
+ {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression
+ {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
};
int WINAPI SCompDecompress(
@@ -919,109 +925,86 @@ int WINAPI SCompDecompress(
char * pbInBuffer,
int cbInBuffer)
{
- DECOMPRESS DecompressFuncArray[0x10]; // Array of compression functions, applied sequentially
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
char * pbOutput = pbOutBuffer; // Where to store decompressed data
char * pbInput; // Where to store decompressed data
unsigned uCompressionMask; // Decompressions applied to the data
+ unsigned uCompressionCopy; // Decompressions applied to the data
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
int cbInLength; // Current size of the input buffer
int nCompressCount = 0; // Number of compressions to be applied
int nCompressIndex = 0;
int nResult = 1;
- // Zero input data bring zero output data
- if(cbInBuffer == 0)
- {
- *pcbOutBuffer = 0;
- return 1;
- }
+ // Verify buffer sizes
+ if(cbOutBuffer < cbInBuffer || cbInBuffer < 1)
+ return 0;
-/*
// If the input length is the same as output length, do nothing.
- // Unfortunately, some data in WoW-Cataclysm BETA MPQs are like that ....
- if(cbInBuffer == cbOutBuffer)
+ if(cbOutBuffer == cbInBuffer)
{
// If the buffers are equal, don't copy anything.
- if(pbInBuffer == pbOutBuffer)
- return 1;
-
- memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
+ if(pbInBuffer != pbOutBuffer)
+ memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
return 1;
}
-*/
// Get applied compression types and decrement data length
- uCompressionMask = (unsigned char)*pbInBuffer++;
+ uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++;
cbInBuffer--;
// Get current compressed data and length of it
pbInput = pbInBuffer;
cbInLength = cbInBuffer;
- //
- // Beginning with Starcraft II, the decompression byte can no longer contain
- // any arbitrary combination types. Instead, it can be one of the few
- // pre-set values (0x02, 0x08, 0x10, 0x12, 0x20, 0x22, 0x30)
- // Note that Starcraft II no longer uses WAVE files, so compressions 0x41, 0x48, 0x81, 0x88
- // are no longer used.
- //
+ // This compression function doesn't support LZMA
+ assert(uCompressionMask != MPQ_COMPRESSION_LZMA);
- // Special case: LZMA decompression (added in Starcraft II)
- if(uCompressionMask == MPQ_COMPRESSION_LZMA)
+ // Parse the compression mask
+ for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
{
- DecompressFuncArray[0] = Decompress_LZMA;
- nCompressCount = 1;
- }
- else
- {
- // Fill the compressions array
- for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
+ // If the mask agrees, insert the compression function to the array
+ if(uCompressionMask & dcmp_table[i].uMask)
{
- // If the mask agrees, insert the compression function to the array
- if(uCompressionMask & dcmp_table[i].uMask)
- {
- DecompressFuncArray[nCompressCount] = dcmp_table[i].Decompress;
- uCompressionMask &= ~dcmp_table[i].uMask;
- nCompressCount++;
- }
+ uCompressionCopy &= ~dcmp_table[i].uMask;
+ nCompressCount++;
}
+ }
- // If at least one of the compressions remaing unknown, return an error
- if(uCompressionMask != 0)
- {
- SetLastError(ERROR_NOT_SUPPORTED);
- return 0;
- }
+ // If at least one of the compressions remaing unknown, return an error
+ if(nCompressCount == 0 || uCompressionCopy != 0)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
}
- // If there is at least one decompression, do it
- if(nCompressCount > 0)
+ // If there is more than one compression, we have to allocate extra buffer
+ if(nCompressCount > 1)
{
- // If there is more than one compression, we have to allocate extra buffer
- if(nCompressCount > 1)
+ pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer);
+ if(pbWorkBuffer == NULL)
{
- pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
- if(pbWorkBuffer == NULL)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- return 0;
- }
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
}
+ }
- // Get the current compression index
- nCompressIndex = nCompressCount - 1;
+ // Get the current compression index
+ nCompressIndex = nCompressCount - 1;
- // Apply all decompressions
- for(int i = 0; i < nCompressCount; i++)
+ // Apply all decompressions
+ for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
+ {
+ // Perform the (next) decompression
+ if(uCompressionMask & dcmp_table[i].uMask)
{
// Get the correct output buffer
pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
nCompressIndex--;
-
- // Perform the (next) decompression
+
+ // Perform the decompression
cbOutBuffer = *pcbOutBuffer;
- nResult = DecompressFuncArray[i](pbOutput, &cbOutBuffer, pbInput, cbInLength);
+ nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength);
if(nResult == 0 || cbOutBuffer == 0)
{
SetLastError(ERROR_FILE_CORRUPT);
@@ -1030,21 +1013,123 @@ int WINAPI SCompDecompress(
}
// Switch buffers
- pbInput = pbOutput;
cbInLength = cbOutBuffer;
+ pbInput = pbOutput;
}
+ }
+
+ // Put the length of the decompressed data to the output buffer
+ *pcbOutBuffer = cbOutBuffer;
+
+ // Cleanup and return
+ if(pbWorkBuffer != NULL)
+ STORM_FREE(pbWorkBuffer);
+ return nResult;
+}
+
+int WINAPI SCompDecompress2(
+ char * pbOutBuffer,
+ int * pcbOutBuffer,
+ char * pbInBuffer,
+ int cbInBuffer)
+{
+ DECOMPRESS pfnDecompress1 = NULL;
+ DECOMPRESS pfnDecompress2 = NULL;
+ char * pbWorkBuffer = pbOutBuffer;
+ int cbWorkBuffer = *pcbOutBuffer;
+ int nResult;
+ char CompressionMethod;
+
+ // Verify buffer sizes
+ if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1)
+ return 0;
- // Put the length of the decompressed data to the output buffer
- *pcbOutBuffer = cbOutBuffer;
+ // If the outputbuffer is as big as input buffer, just copy the block
+ if(*pcbOutBuffer == cbInBuffer)
+ {
+ if(pbOutBuffer != pbInBuffer)
+ memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
+ return 1;
}
- else
+
+ // Get the compression methods
+ CompressionMethod = *pbInBuffer++;
+ cbInBuffer--;
+
+ // We only recognize a fixed set of compression methods
+ switch((unsigned char)CompressionMethod)
{
- memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
- *pcbOutBuffer = cbInBuffer;
+ case MPQ_COMPRESSION_ZLIB:
+ pfnDecompress1 = Decompress_ZLIB;
+ break;
+
+ case MPQ_COMPRESSION_PKWARE:
+ pfnDecompress1 = Decompress_PKLIB;
+ break;
+
+ case MPQ_COMPRESSION_BZIP2:
+ pfnDecompress1 = Decompress_BZIP2;
+ break;
+
+ case MPQ_COMPRESSION_LZMA:
+ pfnDecompress1 = Decompress_LZMA;
+ break;
+
+ case MPQ_COMPRESSION_SPARSE:
+ pfnDecompress1 = Decompress_SPARSE;
+ break;
+
+ case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
+ pfnDecompress1 = Decompress_ZLIB;
+ pfnDecompress2 = Decompress_SPARSE;
+ break;
+
+ case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
+ pfnDecompress1 = Decompress_BZIP2;
+ pfnDecompress2 = Decompress_SPARSE;
+ break;
+
+ //
+ // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
+ // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN
+ // is not supported by newer MPQs.
+ //
+
+ default:
+ SetLastError(ERROR_FILE_CORRUPT);
+ return 0;
}
- // Cleanup and return
- if(pbWorkBuffer != NULL)
+ // If we have to use two decompressions, allocate temporary buffer
+ if(pfnDecompress2 != NULL)
+ {
+ pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
+ if(pbWorkBuffer == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+ }
+
+ // Apply the first decompression method
+ nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer);
+
+ // Apply the second decompression method, if any
+ if(pfnDecompress2 != NULL && nResult != 0)
+ {
+ cbInBuffer = cbWorkBuffer;
+ cbWorkBuffer = *pcbOutBuffer;
+ nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
+ }
+
+ // Supply the output buffer size
+ *pcbOutBuffer = cbWorkBuffer;
+
+ // Free temporary buffer
+ if(pbWorkBuffer != pbOutBuffer)
STORM_FREE(pbWorkBuffer);
+
+ if(nResult == 0)
+ SetLastError(ERROR_FILE_CORRUPT);
return nResult;
}
diff --git a/dep/StormLib/src/SFileAddFile.cpp b/dep/StormLib/src/SFileAddFile.cpp
index e908d644990..dda47370bd1 100644
--- a/dep/StormLib/src/SFileAddFile.cpp
+++ b/dep/StormLib/src/SFileAddFile.cpp
@@ -13,6 +13,33 @@
#include "StormCommon.h"
//-----------------------------------------------------------------------------
+// Local structures
+
+#define FILE_SIGNATURE_RIFF 0x46464952
+#define FILE_SIGNATURE_WAVE 0x45564157
+#define FILE_SIGNATURE_FMT 0x20746D66
+#define AUDIO_FORMAT_PCM 1
+
+typedef struct _WAVE_FILE_HEADER
+{
+ DWORD dwChunkId; // 0x52494646 ("RIFF")
+ DWORD dwChunkSize; // Size of that chunk, in bytes
+ DWORD dwFormat; // Must be 0x57415645 ("WAVE")
+
+ // Format sub-chunk
+ DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ")
+ DWORD dwSubChunk1Size; // 0x16 for PCM
+ USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression
+ USHORT wChannels; // Number of channels
+ DWORD dwSampleRate; // 8000, 44100, etc.
+ DWORD dwBytesRate; // SampleRate * NumChannels * BitsPerSample/8
+ USHORT wBlockAlign; // NumChannels * BitsPerSample/8
+ USHORT wBitsPerSample; // 8 bits = 8, 16 bits = 16, etc.
+
+ // Followed by "data" sub-chunk (we don't care)
+} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
+
+//-----------------------------------------------------------------------------
// Local variables
// Data compression for SFileAddFile
@@ -27,6 +54,29 @@ static void * pvUserData = NULL;
#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN)
+static int IsWaveFile(
+ LPBYTE pbFileData,
+ DWORD cbFileData,
+ LPDWORD pdwChannels)
+{
+ PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData;
+
+ if(cbFileData > sizeof(WAVE_FILE_HEADER))
+ {
+ if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE)
+ {
+ if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM)
+ {
+ *pdwChannels = pWaveHdr->wChannels;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
static int WriteDataToMpqFile(
TMPQArchive * ha,
TMPQFile * hf,
@@ -86,16 +136,6 @@ static int WriteDataToMpqFile(
// Set the position in the file
ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize;
- // If the file is compressed, allocate buffer for the compressed data.
- // Note that we allocate buffer that is a bit longer than sector size,
- // for case if the compression method performs a buffer overrun
- if((pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) && pbCompressed == NULL)
- {
- pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100);
- if(pbCompressed == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
-
// Update CRC32 and MD5 of the file
md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector);
hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector);
@@ -106,7 +146,18 @@ static int WriteDataToMpqFile(
int nOutBuffer = (int)dwBytesInSector;
int nInBuffer = (int)dwBytesInSector;
- assert(pbCompressed != NULL);
+ // If the file is compressed, allocate buffer for the compressed data.
+ // Note that we allocate buffer that is a bit longer than sector size,
+ // for case if the compression method performs a buffer overrun
+ if(pbCompressed == NULL)
+ {
+ pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100);
+ if(pbCompressed == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ }
//
// Note that both SCompImplode and SCompCompress give original buffer,
@@ -319,11 +370,17 @@ int SFileAddFile_Init(
// flags get to this point
//
- // Adjust file flags for too-small files
- if(dwFileSize < 0x04)
- dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY);
- if(dwFileSize < 0x20)
- dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC);
+ // Sestor CRC is not allowed with single unit files
+ if(dwFlags & MPQ_FILE_SINGLE_UNIT)
+ dwFlags &= ~MPQ_FILE_SECTOR_CRC;
+
+ // Sector CRC is not allowed if the file is not compressed
+ if(!(dwFlags & MPQ_FILE_COMPRESSED))
+ dwFlags &= ~MPQ_FILE_SECTOR_CRC;
+
+ // Fix Key is not allowed if the file is not enrypted
+ if(!(dwFlags & MPQ_FILE_ENCRYPTED))
+ dwFlags &= ~MPQ_FILE_FIX_KEY;
// If the MPQ is of version 3.0 or higher, we ignore file locale.
// This is because HET and BET tables have no known support for it
@@ -371,12 +428,9 @@ int SFileAddFile_Init(
}
else
{
- // If the file is marked as deleted, it's OK
- if(!(pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER))
- {
- if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
- nError = ERROR_ALREADY_EXISTS;
- }
+ // If the file exists and "replace existing" is not set, fail it
+ if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
+ nError = ERROR_ALREADY_EXISTS;
// If the file entry already contains a file
// and it is a pseudo-name, replace it
@@ -459,7 +513,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
}
// Allocate patch info, if the data is patch
- if(hf->pPatchInfo == NULL && IsPatchData(pvData, dwSize, &hf->dwPatchedFileSize))
+ if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize))
{
// Set the MPQ_FILE_PATCH_FILE flag
hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE;
@@ -782,6 +836,9 @@ bool WINAPI SFileAddFileEx(
DWORD dwBytesRemaining = 0;
DWORD dwBytesToRead;
DWORD dwSectorSize = 0x1000;
+ DWORD dwChannels = 0;
+ bool bIsAdpcmCompression = false;
+ bool bIsFirstSector = true;
int nError = ERROR_SUCCESS;
// Check parameters
@@ -791,7 +848,7 @@ bool WINAPI SFileAddFileEx(
// Open added file
if(nError == ERROR_SUCCESS)
{
- pStream = FileStream_OpenFile(szFileName, false);
+ pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream == NULL)
nError = GetLastError();
}
@@ -799,7 +856,7 @@ bool WINAPI SFileAddFileEx(
// Get the file size and file time
if(nError == ERROR_SUCCESS)
{
- FileStream_GetLastWriteTime(pStream, &FileTime);
+ FileStream_GetTime(pStream, &FileTime);
FileStream_GetSize(pStream, FileSize);
// Files bigger than 4GB cannot be added to MPQ
@@ -821,16 +878,19 @@ bool WINAPI SFileAddFileEx(
{
// When the compression for next blocks is set to default,
// we will copy the compression for the first sector
- if(dwCompressionNext == 0xFFFFFFFF)
+ if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME)
dwCompressionNext = dwCompression;
- // If the caller wants ADPCM compression, we make sure that the first sector is not
- // compressed with lossy compression
- if(dwCompressionNext & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO))
+ // If the caller wants ADPCM compression, we make sure
+ // that the first sector is not compressed with lossy compression
+ if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
{
// The first compression must not be WAVE
- if(dwCompression & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO))
+ if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
dwCompression = MPQ_COMPRESSION_PKWARE;
+
+ dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO);
+ bIsAdpcmCompression = true;
}
// Initiate adding file to the MPQ
@@ -853,6 +913,21 @@ bool WINAPI SFileAddFileEx(
break;
}
+ // If the file being added is a WAVE file, we check number of channels
+ if(bIsFirstSector && bIsAdpcmCompression)
+ {
+ // The file must really be a wave file, otherwise it's data corruption
+ if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels))
+ {
+ nError = ERROR_BAD_FORMAT;
+ break;
+ }
+
+ // Setup the compression according to number of channels
+ dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO;
+ bIsFirstSector = false;
+ }
+
// Add the file sectors to the MPQ
if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression))
{
@@ -920,12 +995,12 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA
case MPQ_WAVE_QUALITY_MEDIUM:
// WaveCompressionLevel = 4;
- dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN;
+ dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
break;
case MPQ_WAVE_QUALITY_LOW:
// WaveCompressionLevel = 2;
- dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN;
+ dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
break;
}
diff --git a/dep/StormLib/src/SFileAttributes.cpp b/dep/StormLib/src/SFileAttributes.cpp
index 091950885c8..0f056088fa3 100644
--- a/dep/StormLib/src/SFileAttributes.cpp
+++ b/dep/StormLib/src/SFileAttributes.cpp
@@ -23,6 +23,7 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
// 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;
//-----------------------------------------------------------------------------
@@ -147,6 +148,40 @@ int SAttrLoadAttributes(TMPQArchive * ha)
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
@@ -174,6 +209,19 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
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);
@@ -210,15 +258,20 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
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
- assert(ha->dwFileFlags2 != 0);
nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
0,
dwFileSize,
LANG_NEUTRAL,
- ha->dwFileFlags2,
+ ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
&hf);
// Write all parts of the (attributes) file
@@ -226,8 +279,9 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
{
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);
+ AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER);
nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB);
}
@@ -283,6 +337,30 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
}
}
+ // 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)
{
diff --git a/dep/StormLib/src/SFileCompactArchive.cpp b/dep/StormLib/src/SFileCompactArchive.cpp
index 09cb179bd74..319108b08f4 100644
--- a/dep/StormLib/src/SFileCompactArchive.cpp
+++ b/dep/StormLib/src/SFileCompactArchive.cpp
@@ -194,6 +194,7 @@ static int CopyMpqFileSectors(
if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen))
nError = GetLastError();
+ dwBytesToCopy -= dwSectorOffsLen;
dwCmpSize += dwSectorOffsLen;
}
@@ -216,10 +217,6 @@ static int CopyMpqFileSectors(
DWORD dwRawDataInSector = hf->dwSectorSize;
DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;
- // Last sector: If there is not enough bytes remaining in the file, cut the raw size
- if(dwRawDataInSector > dwBytesToCopy)
- dwRawDataInSector = dwBytesToCopy;
-
// Fix the raw data length if the file is compressed
if(hf->SectorOffsets != NULL)
{
@@ -227,6 +224,10 @@ static int CopyMpqFileSectors(
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);
@@ -263,7 +264,7 @@ static int CopyMpqFileSectors(
}
// Adjust byte counts
- dwBytesToCopy -= hf->dwSectorSize;
+ dwBytesToCopy -= dwRawDataInSector;
dwCmpSize += dwRawDataInSector;
}
}
@@ -291,10 +292,39 @@ static int CopyMpqFileSectors(
}
// 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)
{
@@ -469,13 +499,13 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
// Get the temporary file name and create it
if(nError == ERROR_SUCCESS)
{
- _tcscpy(szTempFile, ha->pStream->szFileName);
+ _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
_tcscpy(szTemp + 1, _T("mp_"));
else
_tcscat(szTempFile, _T("_"));
- pTempStream = FileStream_CreateFile(szTempFile);
+ pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pTempStream == NULL)
nError = GetLastError();
}
@@ -531,7 +561,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
// If succeeded, switch the streams
if(nError == ERROR_SUCCESS)
{
- if(FileStream_MoveFile(ha->pStream, pTempStream))
+ if(FileStream_Switch(ha->pStream, pTempStream))
pTempStream = NULL;
else
nError = ERROR_CAN_NOT_COMPLETE;
diff --git a/dep/StormLib/src/SFileCreateArchive.cpp b/dep/StormLib/src/SFileCreateArchive.cpp
index 6e8be7c46ec..59f3d3fa9e8 100644
--- a/dep/StormLib/src/SFileCreateArchive.cpp
+++ b/dep/StormLib/src/SFileCreateArchive.cpp
@@ -68,17 +68,46 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
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;
- USHORT wFormatVersion = MPQ_FORMAT_VERSION_1;
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 || phMpq == NULL)
+ 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;
@@ -89,7 +118,7 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// 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, dwFlags, &hMpq))
+ 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);
@@ -102,25 +131,18 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// - If the file doesn't exist, create new empty file
//
- pStream = FileStream_OpenFile(szMpqName, true);
+ pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
{
- pStream = FileStream_CreateFile(szMpqName);
+ pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
return false;
}
- // Decide what format to use
- wFormatVersion = (USHORT)((dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> 16);
- if(wFormatVersion > MPQ_FORMAT_VERSION_4)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
- }
-
// Increment the maximum amount of files to have space
// for listfile and attributes file
- if(dwFlags & MPQ_CREATE_ATTRIBUTES)
+ dwMaxFileCount = pCreateInfo->dwMaxFileCount;
+ if(pCreateInfo->dwAttrFlags != 0)
dwMaxFileCount++;
dwMaxFileCount++;
@@ -150,19 +172,18 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
{
memset(ha, 0, sizeof(TMPQArchive));
ha->pStream = pStream;
- ha->dwSectorSize = (wFormatVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
+ ha->dwSectorSize = pCreateInfo->dwSectorSize;
ha->UserDataPos = MpqPos;
ha->MpqPos = MpqPos;
ha->pHeader = (TMPQHeader *)ha->HeaderData;
ha->dwMaxFileCount = dwMaxFileCount;
ha->dwFileTableSize = 0;
- ha->dwFileFlags1 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
- ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
+ ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
+ ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
ha->dwFlags = 0;
// Setup the attributes
- if(dwFlags & MPQ_CREATE_ATTRIBUTES)
- ha->dwAttrFlags = MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5;
+ ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
pStream = NULL;
}
@@ -174,9 +195,9 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// Fill the MPQ header
memset(pHeader, 0, sizeof(ha->HeaderData));
pHeader->dwID = ID_MPQ;
- pHeader->dwHeaderSize = MpqHeaderSizes[wFormatVersion];
+ pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
- pHeader->wFormatVersion = wFormatVersion;
+ pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
pHeader->dwHashTablePos = pHeader->dwHeaderSize;
pHeader->dwHashTableSize = dwHashTableSize;
@@ -185,8 +206,8 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// For MPQs version 4 and higher, we set the size of raw data block
// for calculating MD5
- if(wFormatVersion >= MPQ_FORMAT_VERSION_4)
- pHeader->dwRawChunkSize = 0x4000;
+ if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
+ pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
// Write the naked MPQ header
nError = WriteNakedMPQHeader(ha);
@@ -196,7 +217,7 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
}
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
- if(nError == ERROR_SUCCESS && wFormatVersion >= MPQ_FORMAT_VERSION_3)
+ if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3)
{
ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
if(ha->pHetTable == NULL)
diff --git a/dep/StormLib/src/SFileExtractFile.cpp b/dep/StormLib/src/SFileExtractFile.cpp
index f46ef178fbc..c8053ed4e62 100644
--- a/dep/StormLib/src/SFileExtractFile.cpp
+++ b/dep/StormLib/src/SFileExtractFile.cpp
@@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR
// Create the local file
if(nError == ERROR_SUCCESS)
{
- pLocalFile = FileStream_CreateFile(szExtracted);
+ pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pLocalFile == NULL)
nError = GetLastError();
}
diff --git a/dep/StormLib/src/SFileFindFile.cpp b/dep/StormLib/src/SFileFindFile.cpp
index 542637b3668..337be7d2c54 100644
--- a/dep/StormLib/src/SFileFindFile.cpp
+++ b/dep/StormLib/src/SFileFindFile.cpp
@@ -31,7 +31,7 @@ struct TMPQSearch
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; // Search mask (variable length)
+ char szSearchMask[1]; // Search mask (variable length)
};
//-----------------------------------------------------------------------------
@@ -69,7 +69,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
szString++;
}
- // If there is '*', means zero or more chars. We have to
+ // If there is '*', means zero or more chars. We have to
// find the sequence after '*'
if(*szWildCard == '*')
{
@@ -337,8 +337,6 @@ static void FreeMPQSearch(TMPQSearch *& hs)
{
if(hs->pSearchTable != NULL)
STORM_FREE(hs->pSearchTable);
- if(hs->szSearchMask != NULL)
- free(hs->szSearchMask); // allocated with strdup
STORM_FREE(hs);
hs = NULL;
}
@@ -378,7 +376,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
if(nError == ERROR_SUCCESS)
{
memset(hs, 0, sizeof(TMPQSearch));
- hs->szSearchMask = strdup(szMask);
+ strcpy(&hs->szSearchMask[0], szMask);
hs->dwFlagMask = MPQ_FILE_EXISTS;
hs->ha = ha;
@@ -408,7 +406,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
FreeMPQSearch(hs);
SetLastError(nError);
}
-
+
// Return the result value
return (HANDLE)hs;
}
diff --git a/dep/StormLib/src/SFileListFile.cpp b/dep/StormLib/src/SFileListFile.cpp
index 9f476d1e4a4..4604206ed0a 100644
--- a/dep/StormLib/src/SFileListFile.cpp
+++ b/dep/StormLib/src/SFileListFile.cpp
@@ -343,13 +343,16 @@ int SListFileSaveToMpq(TMPQArchive * ha)
}
}
+ // Determine the flags for (listfile)
+ if(ha->dwFileFlags1 == 0)
+ ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize);
+
// Create the listfile in the MPQ
- assert(ha->dwFileFlags1 != 0);
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
0,
dwFileSize,
LANG_NEUTRAL,
- ha->dwFileFlags1,
+ ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
&hf);
// Add all file names
if(nError == ERROR_SUCCESS)
diff --git a/dep/StormLib/src/SFileOpenArchive.cpp b/dep/StormLib/src/SFileOpenArchive.cpp
index d7b04aba70e..b36b7d35ea7 100644
--- a/dep/StormLib/src/SFileOpenArchive.cpp
+++ b/dep/StormLib/src/SFileOpenArchive.cpp
@@ -32,6 +32,34 @@ static bool IsAviFile(void * pvFileBegin)
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)
{
@@ -91,19 +119,6 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
// SFileGetLocale and SFileSetLocale
// Set the locale for all newly opened files
-DWORD WINAPI SFileGetGlobalFlags()
-{
- return dwGlobalFlags;
-}
-
-DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags)
-{
- DWORD dwOldFlags = dwGlobalFlags;
-
- dwGlobalFlags = dwNewFlags;
- return dwOldFlags;
-}
-
LCID WINAPI SFileGetLocale()
{
return lcFileLocale;
@@ -145,18 +160,10 @@ bool WINAPI SFileOpenArchive(
// Open the MPQ archive file
if(nError == ERROR_SUCCESS)
{
- if(!(dwFlags & MPQ_OPEN_ENCRYPTED))
- {
- pStream = FileStream_OpenFile(szMpqName, (dwFlags & MPQ_OPEN_READ_ONLY) ? false : true);
- if(pStream == NULL)
- nError = GetLastError();
- }
- else
- {
- pStream = FileStream_OpenEncrypted(szMpqName);
- if(pStream == NULL)
- nError = GetLastError();
- }
+ // Initialize the stream
+ pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
+ if(pStream == NULL)
+ nError = GetLastError();
}
// Allocate the MPQhandle
@@ -175,7 +182,7 @@ bool WINAPI SFileOpenArchive(
pStream = NULL;
// Remember if the archive is open for write
- if(ha->pStream->StreamFlags & (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE))
+ if(FileStream_IsReadOnly(ha->pStream))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// Also remember if we shall check sector CRCs when reading file
@@ -276,10 +283,6 @@ bool WINAPI SFileOpenArchive(
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
- // Set the default file flags for (listfile) and (attributes)
- ha->dwFileFlags1 =
- ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
-
// Set the size of file sector
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
@@ -287,6 +290,24 @@ bool WINAPI SFileOpenArchive(
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)
@@ -369,6 +390,16 @@ bool WINAPI SFileOpenArchive(
}
//-----------------------------------------------------------------------------
+// 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.
diff --git a/dep/StormLib/src/SFileOpenFileEx.cpp b/dep/StormLib/src/SFileOpenFileEx.cpp
index c35ac9f969c..e03a8b17998 100644
--- a/dep/StormLib/src/SFileOpenFileEx.cpp
+++ b/dep/StormLib/src/SFileOpenFileEx.cpp
@@ -29,10 +29,10 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
for(i = 0; szFileName[i] != 0; i++)
szFileNameT[i] = szFileName[i];
szFileNameT[i] = 0;
- pStream = FileStream_OpenFile(szFileNameT, false);
+ pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
#else
- pStream = FileStream_OpenFile(szFileName, false);
+ pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
#endif
if(pStream != NULL)
@@ -73,7 +73,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
{
// Construct the name of the patch file
strcpy(szPatchFileName, ha->szPatchPrefix);
- strcat(szPatchFileName, szFileName);
+ 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
@@ -102,7 +102,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
{
// Construct patch file name
strcpy(szPatchFileName, ha->szPatchPrefix);
- strcat(szPatchFileName, szFileName);
+ strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile))
{
// Remember the new version
@@ -229,7 +229,11 @@ int WINAPI SFileEnumLocales(
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))
@@ -240,26 +244,39 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
// Prepare the file opening
if(nError == ERROR_SUCCESS)
{
- if(!IsPseudoFileName(szFileName, &dwFileIndex))
- {
- if(GetFileEntryLocale(ha, szFileName, lcFileLocale) == NULL)
- {
- nError = ERROR_FILE_NOT_FOUND;
- }
- }
- else
+ // Different processing for pseudo-names
+ bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex);
+
+ // Walk through the MPQ and all patches
+ while(ha != NULL)
{
- if(GetFileEntryByIndex(ha, dwFileIndex) == 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)
{
- nError = ERROR_FILE_NOT_FOUND;
+ strcpy(szPatchFileName, ha->szPatchPrefix);
+ strcat(szPatchFileName, szFileName);
+ szFileName = szPatchFileName;
}
}
+
+ // Not found, sorry
+ nError = ERROR_FILE_NOT_FOUND;
}
// Cleanup
- if(nError != ERROR_SUCCESS)
- SetLastError(nError);
- return (nError == ERROR_SUCCESS);
+ SetLastError(nError);
+ return false;
}
diff --git a/dep/StormLib/src/SFilePatchArchives.cpp b/dep/StormLib/src/SFilePatchArchives.cpp
index 02f149be968..24ae2c52c37 100644
--- a/dep/StormLib/src/SFilePatchArchives.cpp
+++ b/dep/StormLib/src/SFilePatchArchives.cpp
@@ -400,7 +400,7 @@ static int ApplyMpqPatch(
//-----------------------------------------------------------------------------
// Public functions (StormLib internals)
-bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
+bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
{
TPatchHeader * pPatchHeader = (TPatchHeader *)pvData;
BLIZZARD_BSDIFF40_FILE DiffFile;
@@ -520,7 +520,7 @@ bool WINAPI SFileOpenPatchArchive(
if(nError == ERROR_SUCCESS)
{
- if((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) == 0)
+ if(!FileStream_IsReadOnly(ha->pStream))
nError = ERROR_ACCESS_DENIED;
}
@@ -539,7 +539,7 @@ bool WINAPI SFileOpenPatchArchive(
{
if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME))
{
- GetDefaultPatchPrefix(ha->pStream->szFileName, szPatchPrefixBuff);
+ GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff);
szPatchPathPrefix = szPatchPrefixBuff;
}
}
diff --git a/dep/StormLib/src/SFileReadFile.cpp b/dep/StormLib/src/SFileReadFile.cpp
index 8c6482c13d2..5570fd466c5 100644
--- a/dep/StormLib/src/SFileReadFile.cpp
+++ b/dep/StormLib/src/SFileReadFile.cpp
@@ -75,13 +75,14 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
if(hf->pStream != NULL)
{
// Calculate the length needed
- cchCharsNeeded += _tcslen(hf->pStream->szFileName) + 1;
+ szFileName = FileStream_GetFileName(hf->pStream);
+ cchCharsNeeded += _tcslen(szFileName) + 1;
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
// If we have enough space, copy the file name
if(cbFileInfo >= cbLengthNeeded)
{
- nLength = _tcslen(szFileName = hf->pStream->szFileName) + 1;
+ nLength = _tcslen(szFileName) + 1;
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
szPatchChain += nLength;
@@ -93,7 +94,7 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
{
// Calculate number of characters needed
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
- cchCharsNeeded += _tcslen(hfTemp->ha->pStream->szFileName) + 1;
+ cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1;
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
// If we have enough space, the copy the patch chain
@@ -101,7 +102,8 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
{
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
{
- nLength = _tcslen(szFileName = hfTemp->ha->pStream->szFileName) + 1;
+ szFileName = FileStream_GetFileName(hfTemp->ha->pStream);
+ nLength = _tcslen(szFileName) + 1;
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
szPatchChain += nLength;
}
@@ -265,15 +267,19 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW
int cbInSector = dwRawBytesInThisSector;
int nResult = 0;
- // Is the file compressed by PKWARE Data Compression Library ?
- if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
- nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
-
// Is the file compressed by Blizzard's multiple compression ?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
{
- hf->PreviousCompression = pbInSector[0];
- nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
+ if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
+ nResult = SCompDecompress2((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
+ else
+ nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
+ }
+
+ // Is the file compressed by PKWARE Data Compression Library ?
+ else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
+ {
+ nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
}
// Did the decompression fail ?
@@ -314,7 +320,6 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
TFileEntry * pFileEntry = hf->pFileEntry;
LPBYTE pbCompressed = NULL;
LPBYTE pbRawData = NULL;
- bool bIsReallyCompressed = false;
int nError = ERROR_SUCCESS;
// If the file buffer is not allocated yet, do it.
@@ -334,13 +339,12 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
if(hf->dwSectorOffs != 0)
{
// Is the file compressed?
- if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
+ if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
// Allocate space for compressed data
pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
if(pbCompressed == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
- bIsReallyCompressed = true;
pbRawData = pbCompressed;
}
@@ -359,47 +363,43 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
}
- //
- // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
- //
- // File CmpSize FileSize Data
- // -------------------------------------- ------- -------- ---------------
- // esES\DBFilesClient\LightSkyBox.dbc 0xBE 0xBC Is compressed
- // deDE\DBFilesClient\MountCapability.dbc 0x93 0x77 Is uncompressed
- //
- // Now tell me how to deal with this mess.
- //
-
- if(hf->pPatchInfo != NULL)
- {
- if(pbRawData[0] == 'P' && pbRawData[1] == 'T' && pbRawData[2] == 'C' && pbRawData[3] == 'H')
- {
- assert(pFileEntry->dwCmpSize >= hf->dwDataSize);
- bIsReallyCompressed = false;
- }
- }
- else
- {
- if(pFileEntry->dwCmpSize >= hf->dwDataSize)
- bIsReallyCompressed = false;
- }
-
// If the file is compressed, we have to decompress it now
- if(bIsReallyCompressed)
+ if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
int cbOutBuffer = (int)hf->dwDataSize;
+ int cbInBuffer = (int)pFileEntry->dwCmpSize;
+ int nResult = 0;
- // Note: Single unit files compressed with IMPLODE are not supported by Blizzard
- if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
- {
- if(!SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
- nError = ERROR_FILE_CORRUPT;
- }
+ //
+ // If the file is an incremental patch, the size of compressed data
+ // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo)
+ //
+ // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
+ //
+ // File CmprSize DcmpSize DataSize Compressed?
+ // -------------------------------------- ---------- -------- -------- ---------------
+ // esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes
+ // deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No
+ //
+
+ if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
+ cbInBuffer = cbInBuffer - sizeof(TPatchInfo);
+
+ // Is the file compressed by Blizzard's multiple compression ?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
{
- if(!SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
- nError = ERROR_FILE_CORRUPT;
+ if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
+ nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
+ else
+ nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
}
+
+ // Is the file compressed by PKWARE Data Compression Library ?
+ // Note: Single unit files compressed with IMPLODE are not supported by Blizzard
+ else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
+ nResult = SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
+
+ nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
}
else
{
@@ -938,7 +938,7 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName)
if(pFileEntry != NULL && pFileEntry->szFileName != NULL)
strcpy(szFileName, pFileEntry->szFileName);
else if(hf->pStream != NULL)
- CopyFileName(szFileName, hf->pStream->szFileName);
+ CopyFileName(szFileName, FileStream_GetFileName(hf->pStream));
}
return (nError == ERROR_SUCCESS);
}
@@ -988,8 +988,8 @@ bool WINAPI SFileGetFileInfo(
VERIFY_MPQ_HANDLE(ha);
// pvFileInfo receives the name of the archive, terminated by 0
- cbLengthNeeded = (DWORD)(_tcslen(ha->pStream->szFileName) + 1) * sizeof(TCHAR);
- pvSrcFileInfo = ha->pStream->szFileName;
+ pvSrcFileInfo = FileStream_GetFileName(ha->pStream);
+ cbLengthNeeded = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR);
break;
case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive
@@ -1056,15 +1056,13 @@ bool WINAPI SFileGetFileInfo(
pvSrcFileInfo = &dwFileCount;
break;
- case SFILE_INFO_STREAM_FLAGS: // Stream flags for the MPQ. See STREAM_FLAG_XXX
- VERIFY_MPQ_HANDLE(ha);
- cbLengthNeeded = sizeof(DWORD);
- pvSrcFileInfo = &ha->pStream->StreamFlags;
+ case SFILE_INFO_STREAM_FLAGS: // Deprecated
+ nError = ERROR_INVALID_PARAMETER;
break;
case SFILE_INFO_IS_READ_ONLY:
VERIFY_MPQ_HANDLE(ha);
- dwIsReadOnly = ((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) || (ha->dwFlags & MPQ_FLAG_READ_ONLY));
+ dwIsReadOnly = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY));
cbLengthNeeded = sizeof(DWORD);
pvSrcFileInfo = &dwIsReadOnly;
break;
diff --git a/dep/StormLib/src/SFileVerify.cpp b/dep/StormLib/src/SFileVerify.cpp
index 2190caa6994..7457171d88f 100644
--- a/dep/StormLib/src/SFileVerify.cpp
+++ b/dep/StormLib/src/SFileVerify.cpp
@@ -169,9 +169,7 @@ static void CalculateArchiveRange(
TMPQArchive * ha,
PMPQ_SIGNATURE_INFO pSI)
{
- TMPQHeader * pHeader = ha->pHeader;
ULONGLONG TempPos = 0;
- ULONGLONG MaxPos;
char szMapHeader[0x200];
// Get the MPQ begin
@@ -188,31 +186,9 @@ static void CalculateArchiveRange(
}
}
- // Get the MPQ data end. The end is calculated as the biggest
- // value of (end of the last file), (end of block table),
- // (end of ext block table), (end of hash table)
- FindFreeMpqSpace(ha, &MaxPos);
-
- // Check if hash table is beyond
- TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + pHeader->HashTableSize64;
- if(TempPos > MaxPos)
- MaxPos = TempPos;
-
- // Check if block table is beyond
- TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + pHeader->BlockTableSize64;
- if(TempPos > MaxPos)
- MaxPos = TempPos;
-
- // Check if ext block table is beyond
- if(pHeader->HiBlockTablePos64 != 0)
- {
- TempPos = ha->MpqPos + pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64;
- if(TempPos > MaxPos)
- MaxPos = TempPos;
- }
-
- // Give the end
- pSI->EndMpqData = MaxPos;
+ // 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);
@@ -421,7 +397,7 @@ static bool CalculateMpqHashSha1(
sha1_done(&sha1_state_temp, sha1_tail0);
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
- GetPlainAnsiFileName(ha->pStream->szFileName, szPlainName);
+ GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName);
AddTailToSha1(&sha1_state_temp, szPlainName);
sha1_done(&sha1_state_temp, sha1_tail1);
@@ -449,11 +425,13 @@ static int VerifyRawMpqData(
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 / dwChunkSize;
- if(dwDataSize % dwChunkSize)
- dwChunkCount++;
+ dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1;
dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE;
// Allocate space for data chunk and for the MD5 array
@@ -659,6 +637,35 @@ static DWORD VerifyFile(
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))
{
@@ -671,18 +678,6 @@ static DWORD VerifyFile(
md5_init(&md5_state);
dwCrc32 = crc32(0, Z_NULL, 0);
- // If we have to verify raw data MD5, do it
- if(dwFlags & SFILE_VERIFY_RAW_MD5)
- {
- if(hf->ha->pHeader->dwRawChunkSize != 0)
- {
- // Note: we have to open the file from the MPQ where it was open from
- if(VerifyRawMpqData(hf->ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS)
- dwVerifyResult |= VERIFY_FILE_RAW_MD5_ERROR;
- dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5;
- }
- }
-
// Also turn on sector checksum verification
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
hf->bCheckSectorCRCs = true;
diff --git a/dep/StormLib/src/StormCommon.h b/dep/StormLib/src/StormCommon.h
index 278c44ed810..9a85c4bd5ac 100644
--- a/dep/StormLib/src/StormCommon.h
+++ b/dep/StormLib/src/StormCommon.h
@@ -109,7 +109,6 @@ __inline void DebugFree(void * ptr)
//-----------------------------------------------------------------------------
// StormLib internal global variables
-extern DWORD dwGlobalFlags; // Global StormLib flags
extern LCID lcFileLocale; // Preferred file locale
//-----------------------------------------------------------------------------
@@ -122,7 +121,7 @@ extern LCID lcFileLocale; // Preferred file locale
DWORD HashString(const char * szFileName, DWORD dwHashType);
-void InitializeMpqCryptography();
+void InitializeMpqCryptography();
DWORD GetHashTableSizeForFileCount(DWORD dwFileCount);
@@ -131,6 +130,8 @@ 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);
@@ -158,7 +159,10 @@ DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos);
-// Functions that load the HET abd BET tables
+// 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);
@@ -206,7 +210,7 @@ int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawD
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
void FreeMPQFile(TMPQFile *& hf);
-bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
+bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
int PatchFileData(TMPQFile * hf);
void FreeMPQArchive(TMPQArchive *& ha);
diff --git a/dep/StormLib/src/StormLib.h b/dep/StormLib/src/StormLib.h
index 1f75ffd26c1..a07ae46ca48 100644
--- a/dep/StormLib/src/StormLib.h
+++ b/dep/StormLib/src/StormLib.h
@@ -64,15 +64,16 @@
/* 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: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
+#endif
#include "StormPort.h"
@@ -85,118 +86,111 @@ extern "C" {
//
// 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
-#if 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
+#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
+ #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
+ #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
+ #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
+ #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
-#endif
//-----------------------------------------------------------------------------
// Defines
-#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
-#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
+#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
+#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_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 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 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 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 TFileStream::Flags
-#define STREAM_FLAG_READ_ONLY 0x01 // The stream is read only
-#define STREAM_FLAG_PART_FILE 0x02 // The stream is a PART file.
-#define STREAM_FLAG_ENCRYPTED_FILE 0x04 // The stream is an encrypted MPQ (MPQE).
+#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
+#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
+#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 0x0000008 // 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 SFilGetFileSize and SFileSetFilePointer
-#define SFILE_INVALID_SIZE 0xFFFFFFFF
-#define SFILE_INVALID_POS 0xFFFFFFFF
-#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF
+#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, 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_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 | \
@@ -209,211 +203,180 @@ extern "C" {
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)
-
-// For backward compatibility
-#define MPQ_COMPRESSION_WAVE_MONO 0x40 // IMA ADPCM compression (mono)
-#define MPQ_COMPRESSION_WAVE_STEREO 0x80 // IMA ADPCM compression (stereo)
-
-// LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags.
-#define MPQ_COMPRESSION_LZMA 0x12
+#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
+#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'
+#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)
+#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 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 0x0804 // Current version of StormLib (8.04)
-#define STORMLIB_VERSION_STRING "8.04"
+#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
+#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_ALL 0x00000007 // Summary mask
+#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
+#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00
// Flags for SFileOpenArchive
-#define MPQ_OPEN_NO_LISTFILE 0x0010 // Don't load the internal listfile
-#define MPQ_OPEN_NO_ATTRIBUTES 0x0020 // Don't open the attributes
-#define MPQ_OPEN_FORCE_MPQ_V1 0x0040 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
-#define MPQ_OPEN_CHECK_SECTOR_CRC 0x0080 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
-#define MPQ_OPEN_READ_ONLY 0x0100 // Open the archive for read-only access
-#define MPQ_OPEN_ENCRYPTED 0x0200 // Opens an encrypted MPQ archive (Example: Starcraft II installation)
+#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 0x00000001 // 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 0x00010000 // Creates archive of version 2 (larger than 4 GB)
-#define MPQ_CREATE_ARCHIVE_V3 0x00020000 // Creates archive of version 3
-#define MPQ_CREATE_ARCHIVE_V4 0x00030000 // Creates archive of version 4
-#define MPQ_CREATE_ARCHIVE_VMASK 0x000F0000 // Mask for archive version
+#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 0x0001 // Verify sector checksum for the file, if available
-#define SFILE_VERIFY_FILE_CRC 0x0002 // Verify file CRC, if available
-#define SFILE_VERIFY_FILE_MD5 0x0004 // Verify file MD5, if available
-#define SFILE_VERIFY_RAW_MD5 0x0008 // Verify raw file MD5, if available
-#define SFILE_VERIFY_ALL 0x000F // Verify every checksum possible
+#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_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
+#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
-
+#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
+#define MD5_DIGEST_SIZE 0x10
#endif
#ifndef SHA1_DIGEST_SIZE
-#define SHA1_DIGEST_SIZE 0x14 // 160 bits
+#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#endif
#ifndef LANG_NEUTRAL
-#define LANG_NEUTRAL 0x00 // Neutral locale
+#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
-
+#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);
-//-----------------------------------------------------------------------------
-// Stream support - structures
-
struct TFileStream;
-typedef bool (*STREAM_GETPOS)(
- struct TFileStream * pStream, // Pointer to an open stream
- ULONGLONG & ByteOffset // Pointer to store current file position
- );
-
-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_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
- );
-
-// Common stream structure. Can be variable length
-struct TFileStream
-{
- ULONGLONG RawFilePos; // Current position in raw file
- HANDLE hFile; // File handle. Do not use directly.
- TCHAR szFileName[MAX_PATH];// Name of the file
- BYTE StreamFlags; // See STREAM_FLAG_XXXX
-
- STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
- 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_GETSIZE StreamGetSize; // Pointer to function returning file size
- STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size
-
- // Extra members may follow
-};
-
//-----------------------------------------------------------------------------
// Structure for bit arrays used for HET and BET tables
@@ -422,8 +385,21 @@ 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)
+ 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)
};
//-----------------------------------------------------------------------------
@@ -465,10 +441,10 @@ struct TMPQUserData
struct TMPQHeader
{
// The ID_MPQ ('MPQ\x1A') signature
- DWORD dwID;
+ DWORD dwID;
// Size of the archive header
- DWORD dwHeaderSize;
+ DWORD dwHeaderSize;
// 32-bit size of MPQ archive
// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
@@ -488,14 +464,14 @@ struct TMPQHeader
// 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;
@@ -540,7 +516,7 @@ struct TMPQHeader
// Size of raw data chunk to calculate MD5.
// MD5 of each data chunk follows the raw file data.
- DWORD dwRawChunkSize;
+ DWORD dwRawChunkSize;
// MD5 of MPQ tables
unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption
@@ -558,7 +534,7 @@ struct TMPQHash
{
// The hash of the file path, using method A.
DWORD dwName1;
-
+
// The hash of the file path, using method B.
DWORD dwName2;
@@ -595,30 +571,30 @@ 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;
-
+ DWORD dwFSize;
+
// Flags for the file. See MPQ_FILE_XXXX constants
- DWORD dwFlags;
+ 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
+ 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
+// Header for PTCH files
struct TPatchHeader
{
//-- PATCH header -----------------------------------
@@ -626,7 +602,7 @@ struct TPatchHeader
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
@@ -666,54 +642,70 @@ struct TFileEntry
// 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
+ 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
+ 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
+ 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
@@ -731,10 +723,11 @@ struct TMPQArchive
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
@@ -747,7 +740,7 @@ struct TMPQArchive
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
@@ -767,7 +760,7 @@ struct TMPQFile
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 * 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
@@ -779,7 +772,6 @@ struct TMPQFile
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
- BYTE PreviousCompression; // Compression of previous sector
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
@@ -803,19 +795,39 @@ typedef struct _SFILE_FIND_DATA
} 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);
-TFileStream * FileStream_OpenFile(const TCHAR * szFileName, bool bWriteAccess);
-TFileStream * FileStream_OpenEncrypted(const TCHAR * szFileName);
-bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset);
+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_GetLastWriteTime(TFileStream * pStream, ULONGLONG * pFT);
+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_MoveFile(TFileStream * pStream, TFileStream * pTempStream);
+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);
//-----------------------------------------------------------------------------
@@ -834,12 +846,6 @@ typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAP
//-----------------------------------------------------------------------------
// Functions for manipulation with StormLib global flags
-#define SFILE_FLAG_ALLOW_WRITE_SHARE 0x00000001 // When a MPQ is open for write by StormLib,
- // it is allowed to open it for write with another application.
-
-DWORD WINAPI SFileGetGlobalFlags();
-DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags);
-
LCID WINAPI SFileGetLocale();
LCID WINAPI SFileSetLocale(LCID lcNewLocale);
@@ -848,7 +854,9 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale);
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);
@@ -931,9 +939,9 @@ bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLON
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 = 0xFFFFFFFF);
-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 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);
@@ -948,6 +956,7 @@ int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pb
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
diff --git a/dep/StormLib/src/StormPort.h b/dep/StormLib/src/StormPort.h
index a8f5835bdfb..0914654b9af 100644
--- a/dep/StormLib/src/StormPort.h
+++ b/dep/StormLib/src/StormPort.h
@@ -46,6 +46,7 @@
#include <ctype.h>
#include <stdio.h>
#include <windows.h>
+ #include <wininet.h>
#define PLATFORM_LITTLE_ENDIAN
#ifdef WIN64
@@ -59,20 +60,26 @@
#endif
-// Defines for Mac Carbon
-#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac Carbon API
+// Defines for Mac
+#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
- // Macintosh using Carbon
- #include <Carbon/Carbon.h> // Mac OS X
+ // Macintosh
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/mman.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <stdlib.h>
+ #include <errno.h>
#define PKEXPORT
#define __SYS_ZLIB
#define __SYS_BZLIB
#ifndef __BIG_ENDIAN__
- #define PLATFORM_LITTLE_ENDIAN // Apple is now making Macs with Intel CPUs
+ #define PLATFORM_LITTLE_ENDIAN
#endif
-
+
#define PLATFORM_MAC
#define PLATFORM_DEFINED // The platform is known now
@@ -83,6 +90,7 @@
#include <sys/types.h>
#include <sys/stat.h>
+ #include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
@@ -152,29 +160,21 @@
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
+ #define _tcsnicmp strncasecmp
#endif // !WIN32
-// Platform-specific error codes
-#ifdef PLATFORM_MAC
- #define ERROR_SUCCESS noErr
- #define ERROR_FILE_NOT_FOUND fnfErr
- #define ERROR_ACCESS_DENIED permErr
- #define ERROR_INVALID_HANDLE rfNumErr
- #define ERROR_NOT_ENOUGH_MEMORY mFulErr
- #define ERROR_BAD_FORMAT 200 // Returned when the opened file is in format that is not recognized by StormLib
- #define ERROR_NO_MORE_FILES errFSNoMoreItems
- #define ERROR_HANDLE_EOF eofErr
- #define ERROR_NOT_SUPPORTED 201
- #define ERROR_INVALID_PARAMETER paramErr
- #define ERROR_DISK_FULL dskFulErr
- #define ERROR_ALREADY_EXISTS dupFNErr
- #define ERROR_CAN_NOT_COMPLETE 202 // A generic error, when any operation fails from an unknown reason
- #define ERROR_FILE_CORRUPT 203 // At any point when there is bad data format in the file
- #define ERROR_INSUFFICIENT_BUFFER errFSBadBuffer
+// 64-bit calls are supplied by "normal" calls on Mac
+#if defined(PLATFORM_MAC)
+ #define stat64 stat
+ #define fstat64 fstat
+ #define lseek64 lseek
+ #define off64_t off_t
+ #define O_LARGEFILE 0
#endif
-#ifdef PLATFORM_LINUX
+// Platform-specific error codes for UNIX-based platforms
+#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
#define ERROR_SUCCESS 0
#define ERROR_FILE_NOT_FOUND ENOENT
#define ERROR_ACCESS_DENIED EPERM
diff --git a/dep/StormLib/src/adpcm/adpcm.cpp b/dep/StormLib/src/adpcm/adpcm.cpp
index c43d2234c18..916fa3811a8 100644
--- a/dep/StormLib/src/adpcm/adpcm.cpp
+++ b/dep/StormLib/src/adpcm/adpcm.cpp
@@ -17,17 +17,17 @@
//------------------------------------------------------------------------------
// Structures
-union TByteAndWordPtr
+typedef union _BYTE_AND_WORD_PTR
{
short * pw;
unsigned char * pb;
-};
+} BYTE_AND_WORD_PTR;
-union TWordAndByteArray
+typedef union _WORD_AND_BYTE_ARRAY
{
short w;
unsigned char b[2];
-};
+} WORD_AND_BYTE_ARRAY;
//-----------------------------------------------------------------------------
// Tables necessary dor decompression
@@ -63,8 +63,8 @@ static long step_table[] =
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
// ECX EDX
{
- TWordAndByteArray Wcmp;
- TByteAndWordPtr out; // Pointer to the output buffer
+ WORD_AND_BYTE_ARRAY Wcmp;
+ BYTE_AND_WORD_PTR out; // Pointer to the output buffer
long SInt32Array1[2];
long SInt32Array2[2];
long SInt32Array3[2];
@@ -83,6 +83,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
int nLength;
int nIndex;
int nValue;
+ int i, chnl;
// If less than 2 bytes remain, don't decompress anything
// pbSaveOutBuffer = pbOutBuffer;
@@ -99,7 +100,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
- for(int i = 0; i < nChannels; i++)
+ for(i = 0; i < nChannels; i++)
{
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
@@ -119,7 +120,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
// ebx - nChannels
// ecx - pwOutPos
- for(int chnl = nChannels; chnl < nWordsRemains; chnl++)
+ for(chnl = nChannels; chnl < nWordsRemains; chnl++)
{
// 1500F030
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
@@ -223,14 +224,14 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
// 1500F230
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
{
- TByteAndWordPtr out; // Output buffer
- TByteAndWordPtr in;
+ 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 dwOutLengthCopy = dwOutLength;
int nIndex;
+ int i;
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
out.pb = pbOutBuffer;
@@ -238,15 +239,15 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char
in.pw++;
// Fill the Uint32Array2 array by channel values.
- for(int i = 0; i < nChannels; i++)
+ for(i = 0; i < nChannels; i++)
{
nOneWord = BSWAP_INT16_SIGNED(*in.pw++);
SInt32Array2[i] = nOneWord;
- if(dwOutLengthCopy < 2)
+ if(dwOutLength < 2)
return (int)(out.pb - pbOutBuffer);
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
- dwOutLengthCopy -= sizeof(short);
+ dwOutLength -= sizeof(short);
}
// Get the initial index
@@ -270,7 +271,7 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char
if(SInt32Array1[nIndex] != 0)
SInt32Array1[nIndex]--;
- if(dwOutLengthCopy < 2)
+ if(dwOutLength < 2)
return (int)(out.pb - pbOutBuffer);
*out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]);
diff --git a/dep/StormLib/src/huffman/huff.cpp b/dep/StormLib/src/huffman/huff.cpp
index 34203a8108f..66a46b3fa55 100644
--- a/dep/StormLib/src/huffman/huff.cpp
+++ b/dep/StormLib/src/huffman/huff.cpp
@@ -263,10 +263,15 @@ static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long n
if(PTR_INVALID(prev2))
{
- prev2 = PTR_NOT(prev);
-
- prev2->next = item;
- item2->prev = item; // Next after last item
+ if(prev != NULL)
+ {
+ prev2 = PTR_NOT(prev);
+ if(prev2 != NULL)
+ {
+ prev2->next = item;
+ item2->prev = item; // Next after last item
+ }
+ }
return;
}
@@ -1009,8 +1014,8 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne
for(;;)
{
// Security check: If we are at the end of the input buffer,
- // it means that the data are corrupt.
- if(is->pbInBuffer > is->pbInBufferEnd)
+ // it means that the data is corrupt
+ if(is->BitCount == 0 && is->pbInBuffer >= is->pbInBufferEnd)
return 0;
// Get 7 bits from input stream
@@ -1046,6 +1051,9 @@ _1500E549:
do
{
+ if(pItem1 == NULL)
+ return 0;
+
pItem1 = pItem1->child; // Move down by one level
if(is->GetBit()) // If current bit is set, move to previous
pItem1 = pItem1->prev;
diff --git a/dep/StormLib/src/huffman/huff.h b/dep/StormLib/src/huffman/huff.h
index 6af4ac638bb..83e9b2cdad0 100644
--- a/dep/StormLib/src/huffman/huff.h
+++ b/dep/StormLib/src/huffman/huff.h
@@ -65,8 +65,6 @@ class TOutputStream
// Huffmann tree item (?)
struct THTreeItem
{
- public:
-
THTreeItem * Call1501DB70(THTreeItem * pLast);
THTreeItem * GetPrevItem(LONG_PTR value);
void ClearItemLinks();
diff --git a/dep/StormLib/src/lzma/info.txt b/dep/StormLib/src/lzma/info.txt
new file mode 100644
index 00000000000..4cee86e3542
--- /dev/null
+++ b/dep/StormLib/src/lzma/info.txt
@@ -0,0 +1 @@
+Taken from LZMA SDK v 9.11 \ No newline at end of file
diff --git a/dep/StormLib/src/sparse/sparse.cpp b/dep/StormLib/src/sparse/sparse.cpp
index 5b37f6d0f8e..6df7021fc8a 100644
--- a/dep/StormLib/src/sparse/sparse.cpp
+++ b/dep/StormLib/src/sparse/sparse.cpp
@@ -255,6 +255,10 @@ int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned c
OneByte = *pbInBuffer++;
cbOutBuffer |= (OneByte << 0x00);
+ // Verify the size of the stream against the output buffer size
+ if(cbOutBuffer > *pcbOutBuffer)
+ return 0;
+
// Put the output size to the buffer
*pcbOutBuffer = cbOutBuffer;