diff options
author | Shauren <shauren.trinity@gmail.com> | 2014-10-10 20:17:30 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2014-10-10 20:17:30 +0200 |
commit | 88ae3da6373dee1f04d03b823ee63d6f1db1502e (patch) | |
tree | f9ac27f0a743a57b70e90b37f5971e024992eb00 /dep/StormLib/src/FileStream.cpp | |
parent | bc97908822c4afa23740ce70151c2486c340e2c2 (diff) |
Tools/Extractors: Updated map extractor
Diffstat (limited to 'dep/StormLib/src/FileStream.cpp')
-rw-r--r-- | dep/StormLib/src/FileStream.cpp | 2294 |
1 files changed, 0 insertions, 2294 deletions
diff --git a/dep/StormLib/src/FileStream.cpp b/dep/StormLib/src/FileStream.cpp deleted file mode 100644 index 413f2acb3a2..00000000000 --- a/dep/StormLib/src/FileStream.cpp +++ /dev/null @@ -1,2294 +0,0 @@ -/*****************************************************************************/ -/* FileStream.cpp Copyright (c) Ladislav Zezula 2010 */ -/*---------------------------------------------------------------------------*/ -/* File stream support for StormLib */ -/* */ -/* Windows support: Written by Ladislav Zezula */ -/* Mac support: Written by Sam Wilkins */ -/* Linux support: Written by Sam Wilkins and Ivan Komissarov */ -/* Big-endian: Written & debugged by Sam Wilkins */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 11.06.10 1.00 Lad Derived from StormPortMac.cpp and StormPortLinux.cpp */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" -#include "FileStream.h" - -#ifdef _MSC_VER -#pragma comment(lib, "wininet.lib") -#endif - -//----------------------------------------------------------------------------- -// Local defines - -#ifndef INVALID_HANDLE_VALUE -#define INVALID_HANDLE_VALUE ((HANDLE)-1) -#endif - -#ifdef _MSC_VER -#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) -#endif - -//----------------------------------------------------------------------------- -// Local functions - platform-specific functions - -#ifndef PLATFORM_WINDOWS -static int nLastError = ERROR_SUCCESS; - -int GetLastError() -{ - return nLastError; -} - -void SetLastError(int nError) -{ - nLastError = nError; -} -#endif - -#ifndef PLATFORM_LITTLE_ENDIAN -void ConvertPartHeader(void * partHeader) -{ - PPART_FILE_HEADER theHeader = (PPART_FILE_HEADER)partHeader; - - theHeader->PartialVersion = SwapUInt32(theHeader->PartialVersion); - theHeader->Flags = SwapUInt32(theHeader->Flags); - theHeader->FileSizeLo = SwapUInt32(theHeader->FileSizeLo); - theHeader->FileSizeHi = SwapUInt32(theHeader->FileSizeHi); - theHeader->BlockSize = SwapUInt32(theHeader->BlockSize); -} -#endif - -//----------------------------------------------------------------------------- -// Preparing file bitmap for a complete file of a given size - -#define DEFAULT_BLOCK_SIZE 0x4000 - -static bool Dummy_GetBitmap( - TFileStream * pStream, - TFileBitmap * pBitmap, - DWORD Length, - LPDWORD LengthNeeded) -{ - 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)) - { - memset(pBitmap, 0, sizeof(TFileBitmap)); - pBitmap->EndOffset = FileSize; - pBitmap->IsComplete = 1; - pBitmap->BitmapSize = BitmapSize; - pBitmap->BlockSize = DEFAULT_BLOCK_SIZE; - bResult = true; - } - - // 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; -} - -//----------------------------------------------------------------------------- -// 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 -{ - ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; - DWORD dwBytesRead = 0; // Must be set by platform-specific code - -#ifdef PLATFORM_WINDOWS - { - // 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 - - // Update the byte offset - pStream->Base.File.FilePos = ByteOffset; - - // Read the data - if(dwBytesToRead != 0) - { - 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; - } -/* - // If the byte offset is different from the current file position, - // we have to update the file position - if(ByteOffset != pStream->Base.File.FilePos) - { - LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); - - SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); - pStream->Base.File.FilePos = ByteOffset; - } - - // Read the data - if(dwBytesToRead != 0) - { - if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL)) - return false; - } -*/ - } -#endif - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - { - ssize_t bytes_read; - - // If the byte offset is different from the current file position, - // we have to update the file position - if(ByteOffset != pStream->Base.File.FilePos) - { - lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); - pStream->Base.File.FilePos = ByteOffset; - } - - // 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 - - // 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); -} - -/** - * \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) -{ - ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; - DWORD dwBytesWritten = 0; // Must be set by platform-specific code - -#ifdef PLATFORM_WINDOWS - { - // 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 - - // Update the byte offset - pStream->Base.File.FilePos = ByteOffset; - - // Read the data - if(dwBytesToWrite != 0) - { - OVERLAPPED Overlapped; - - 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) - { - LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); - - SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); - pStream->Base.File.FilePos = ByteOffset; - } - - // Read the data - if(dwBytesToWrite != 0) - { - if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) - return false; - } -*/ - } -#endif - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - { - ssize_t bytes_written; - - // If the byte offset is different from the current file position, - // we have to update the file position - if(ByteOffset != pStream->Base.File.FilePos) - { - lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); - pStream->Base.File.FilePos = ByteOffset; - } - - // 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 - - // 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 bool BaseFile_GetPos( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset -{ - ByteOffset = pStream->Base.File.FilePos; - return true; -} - -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; -} - -/** - * \a pStream Pointer to an open stream - * \a NewFileSize New size of the file - */ -static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) -{ -#ifdef PLATFORM_WINDOWS - { - LONG FileSizeHi = (LONG)(NewFileSize >> 32); - LONG FileSizeLo; - DWORD dwNewPos; - bool bResult; - - // 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; - - // 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 - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - { - if(ftruncate((intptr_t)pStream->Base.File.hFile, (off_t)NewFileSize) == -1) - { - nLastError = errno; - return false; - } - - return true; - } -#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 - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - // "rename" on Linux also works if the target file exists - if(rename(pNewStream->szFileName, pStream->szFileName) == -1) - { - nLastError = errno; - return false; - } - - return true; -#endif -} - -static void BaseFile_Close(TFileStream * pStream) -{ - 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 BaseFile_Create( - TFileStream * pStream, - const TCHAR * szFileName, - DWORD dwStreamFlags) -{ -#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 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 - { - 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; - - // Query the file size - FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); - pStream->Base.File.FileSize = FileSize.QuadPart; - - // Query last write time - GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime); - } -#endif - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - { - struct stat fileinfo; - int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; - intptr_t handle; - - // Open the file - handle = open(szFileName, oflag); - if(handle == -1) - { - nLastError = errno; - return false; - } - - // Get the file size - if(fstat(handle, &fileinfo) == -1) - { - 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 - - // 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; -} - -//----------------------------------------------------------------------------- -// Local functions - base memory-mapped file support - -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; - - // 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); - } - - // Move the current file position - pStream->Base.Map.FilePos += dwBytesToRead; - return true; -} - -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 BaseMap_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = pStream->Base.Map.FileSize; - return true; -} - -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) - { - // Retrieve file size. Don't allow mapping file of a zero size. - FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); - if(FileSize.QuadPart != 0) - { - // 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; - } - - // Close the map handle - CloseHandle(hMap); - } - } - - // 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 - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) - struct stat fileinfo; - intptr_t handle; - bool bResult = false; - - // Open the file - handle = open(szFileName, O_RDONLY); - if(handle != -1) - { - // Get the file size - if(fstat(handle, &fileinfo) != -1) - { - 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); - } - - // Did the mapping fail? - if(bResult == false) - { - nLastError = errno; - return false; - } -#endif - - // 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) - { - while(szFileName[0] != 0 && szFileName[0] != _T('/')) - *szServerName++ = *szFileName++; - *szServerName = 0; - } - else - { - while(szFileName[0] != 0 && szFileName[0] != _T('/')) - *szFileName++; - } - - // Return the remainder - return szFileName; -} - -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) - { - // 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); - - // 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); - } - } - - // Increment the current file position by number of bytes read - 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 BaseHttp_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = pStream->Base.Http.FileSize; - return true; -} - -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; - - // Don't connect to the internet - if(!InternetGetConnectedState(&dwTemp, 0)) - nError = GetLastError(); - - // Initiate the connection to the internet - if(nError == ERROR_SUCCESS) - { - pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"), - INTERNET_OPEN_TYPE_PRECONFIG, - NULL, - NULL, - 0); - if(pStream->Base.Http.hInternet == NULL) - nError = GetLastError(); - } - - // 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(); - } - - // 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 the file is not there and is not available for random access, - // report error - if(bFileAvailable == false) - { - BaseHttp_Close(pStream); - return false; - } - - // 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 -} - -//----------------------------------------------------------------------------- -// 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 -{ - 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) - { - DWORD BlockSize = pStream->pBitmap->BlockSize; - - // Get the offset where we read it from - if(pByteOffset == NULL) - pStream->BaseGetPos(pStream, ByteOffset); - else - ByteOffset = *pByteOffset; - EndOffset = ByteOffset + dwBytesToRead; - - // 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); - - // Calculate the initial block index - BlockIndex = (DWORD)(ByteOffset / BlockSize); - pbBitmap = (LPBYTE)(pStream->pBitmap + 1); - - // 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++; - } - } - } - - // 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)) - { - // Enough space for complete bitmap? - if(Length >= TotalLength) - { - memcpy(pBitmap, pStream->pBitmap, TotalLength); - bResult = true; - } - else - { - memcpy(pBitmap, pStream->pBitmap, sizeof(TFileBitmap)); - bResult = true; - } - } - - return bResult; -} - -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); -} - -static bool LinearStream_Open(TLinearStream * pStream) -{ - // 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; -} - -//----------------------------------------------------------------------------- -// 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; - DWORD dwBytesRemaining = dwBytesToRead; - DWORD dwPartOffset; - DWORD dwPartIndex; - DWORD dwBytesRead = 0; - DWORD dwBlockSize = pStream->BlockSize; - bool bResult = false; - int nFailReason = ERROR_HANDLE_EOF; // Why it failed if not enough bytes was read - - // If the byte offset is not entered, use the current position - if(pByteOffset == NULL) - pByteOffset = &pStream->VirtualPos; - - // Check if the file position is not at or beyond end of the file - if(*pByteOffset >= pStream->VirtualSize) - { - SetLastError(ERROR_HANDLE_EOF); - return false; - } - - // Get the part index where the read offset is - // Note that the part index should now be within the range, - // as read requests beyond-EOF are handled by the previous test - dwPartIndex = (DWORD)(*pByteOffset / pStream->BlockSize); - assert(dwPartIndex < pStream->BlockCount); - - // If the number of bytes remaining goes past - // the end of the file, cut them - if((*pByteOffset + dwBytesRemaining) > pStream->VirtualSize) - dwBytesRemaining = (DWORD)(pStream->VirtualSize - *pByteOffset); - - // Calculate the offset in the current part - dwPartOffset = (DWORD)(*pByteOffset) & (pStream->BlockSize - 1); - - // Read all data, one part at a time - while(dwBytesRemaining != 0) - { - PPART_FILE_MAP_ENTRY PartMap = pStream->PartMap + dwPartIndex; - DWORD dwBytesInPart; - - // If the part is not present in the file, we fail the read - if((PartMap->Flags & 3) == 0) - { - nFailReason = ERROR_FILE_CORRUPT; - bResult = false; - break; - } - - // If we are in the last part, we have to cut the number of bytes in the last part - if(dwPartIndex == pStream->BlockCount - 1) - dwBlockSize = (DWORD)pStream->VirtualSize & (pStream->BlockSize - 1); - - // Get the number of bytes reamining in the current part - dwBytesInPart = dwBlockSize - dwPartOffset; - - // Compute the raw file offset of the file part - RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo); - if(RawByteOffset == 0) - { - nFailReason = ERROR_FILE_CORRUPT; - bResult = false; - break; - } - - // If the number of bytes in part is too big, cut it - if(dwBytesInPart > dwBytesRemaining) - dwBytesInPart = dwBytesRemaining; - - // Append the offset within the part - RawByteOffset += dwPartOffset; - if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) - { - nFailReason = ERROR_FILE_CORRUPT; - bResult = false; - break; - } - - // Increment the file position - dwBytesRemaining -= dwBytesInPart; - dwBytesRead += dwBytesInPart; - pbBuffer += dwBytesInPart; - - // Move to the next file part - dwPartOffset = 0; - dwPartIndex++; - } - - // Move the file position by the number of bytes read - pStream->VirtualPos = *pByteOffset + dwBytesRead; - if(dwBytesRead != dwBytesToRead) - SetLastError(nFailReason); - return (dwBytesRead == dwBytesToRead); -} - -static bool PartialStream_GetPos( - TPartialStream * pStream, - ULONGLONG & ByteOffset) -{ - ByteOffset = pStream->VirtualPos; - return true; -} - -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 PartialStream_GetBitmap( - TPartialStream * pStream, - TFileBitmap * pBitmap, - DWORD Length, - LPDWORD LengthNeeded) -{ - LPBYTE pbBitmap; - DWORD TotalLength; - DWORD BitmapSize = 0; - DWORD ByteOffset; - DWORD BitMask; - bool bResult = 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; -} - -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_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 -}; - -static DWORD Rol32(DWORD dwValue, DWORD dwRolCount) -{ - DWORD dwShiftRight = 32 - dwRolCount; - - return (dwValue << dwRolCount) | (dwValue >> dwShiftRight); -} - -static void DecryptFileChunk( - DWORD * MpqData, - LPBYTE pbKey, - ULONGLONG ByteOffset, - DWORD dwLength) -{ - ULONGLONG ChunkOffset; - DWORD KeyShuffled[0x10]; - DWORD KeyMirror[0x10]; - DWORD RoundCount = 0x14; - - // Prepare the key - ChunkOffset = ByteOffset / MPQE_CHUNK_SIZE; - memcpy(KeyMirror, pbKey, MPQE_CHUNK_SIZE); - BSWAP_ARRAY32_UNSIGNED(KeyMirror, MPQE_CHUNK_SIZE); - KeyMirror[0x05] = (DWORD)(ChunkOffset >> 32); - KeyMirror[0x08] = (DWORD)(ChunkOffset); - - while(dwLength >= MPQE_CHUNK_SIZE) - { - // Shuffle the key - part 1 - KeyShuffled[0x0E] = KeyMirror[0x00]; - KeyShuffled[0x0C] = KeyMirror[0x01]; - KeyShuffled[0x05] = KeyMirror[0x02]; - KeyShuffled[0x0F] = KeyMirror[0x03]; - KeyShuffled[0x0A] = KeyMirror[0x04]; - KeyShuffled[0x07] = KeyMirror[0x05]; - KeyShuffled[0x0B] = KeyMirror[0x06]; - KeyShuffled[0x09] = KeyMirror[0x07]; - KeyShuffled[0x03] = KeyMirror[0x08]; - KeyShuffled[0x06] = KeyMirror[0x09]; - KeyShuffled[0x08] = KeyMirror[0x0A]; - KeyShuffled[0x0D] = KeyMirror[0x0B]; - KeyShuffled[0x02] = KeyMirror[0x0C]; - KeyShuffled[0x04] = KeyMirror[0x0D]; - KeyShuffled[0x01] = KeyMirror[0x0E]; - KeyShuffled[0x00] = KeyMirror[0x0F]; - - // Shuffle the key - part 2 - for(DWORD i = 0; i < RoundCount; i += 2) - { - KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x02]), 0x07); - KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0E]), 0x09); - KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x0A]), 0x0D); - KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x03]), 0x12); - - KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x04]), 0x07); - KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x0C]), 0x09); - KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x07]), 0x0D); - KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x06]), 0x12); - - KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x01]), 0x07); - KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x05]), 0x09); - KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x0B]), 0x0D); - KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x08]), 0x12); - - KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x00]), 0x07); - KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x0F]), 0x09); - KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x09]), 0x0D); - KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x0D]), 0x12); - - KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x09]), 0x07); - KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x0E]), 0x09); - KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x04]), 0x0D); - KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x08]), 0x12); - - KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x0A]), 0x07); - KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x0C]), 0x09); - KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x01]), 0x0D); - KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0D]), 0x12); - - KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x07]), 0x07); - KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x05]), 0x09); - KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x00]), 0x0D); - KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x03]), 0x12); - - KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x0B]), 0x07); - KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x0F]), 0x09); - KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x02]), 0x0D); - KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x06]), 0x12); - } - - // Decrypt one data chunk - BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE); - MpqData[0x00] = MpqData[0x00] ^ (KeyShuffled[0x0E] + KeyMirror[0x00]); - MpqData[0x01] = MpqData[0x01] ^ (KeyShuffled[0x04] + KeyMirror[0x0D]); - MpqData[0x02] = MpqData[0x02] ^ (KeyShuffled[0x08] + KeyMirror[0x0A]); - MpqData[0x03] = MpqData[0x03] ^ (KeyShuffled[0x09] + KeyMirror[0x07]); - MpqData[0x04] = MpqData[0x04] ^ (KeyShuffled[0x0A] + KeyMirror[0x04]); - MpqData[0x05] = MpqData[0x05] ^ (KeyShuffled[0x0C] + KeyMirror[0x01]); - MpqData[0x06] = MpqData[0x06] ^ (KeyShuffled[0x01] + KeyMirror[0x0E]); - MpqData[0x07] = MpqData[0x07] ^ (KeyShuffled[0x0D] + KeyMirror[0x0B]); - MpqData[0x08] = MpqData[0x08] ^ (KeyShuffled[0x03] + KeyMirror[0x08]); - MpqData[0x09] = MpqData[0x09] ^ (KeyShuffled[0x07] + KeyMirror[0x05]); - MpqData[0x0A] = MpqData[0x0A] ^ (KeyShuffled[0x05] + KeyMirror[0x02]); - MpqData[0x0B] = MpqData[0x0B] ^ (KeyShuffled[0x00] + KeyMirror[0x0F]); - MpqData[0x0C] = MpqData[0x0C] ^ (KeyShuffled[0x02] + KeyMirror[0x0C]); - MpqData[0x0D] = MpqData[0x0D] ^ (KeyShuffled[0x06] + KeyMirror[0x09]); - MpqData[0x0E] = MpqData[0x0E] ^ (KeyShuffled[0x0B] + KeyMirror[0x06]); - MpqData[0x0F] = MpqData[0x0F] ^ (KeyShuffled[0x0F] + KeyMirror[0x03]); - BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE); - - // Update byte offset in the key - KeyMirror[0x08]++; - if(KeyMirror[0x08] == 0) - KeyMirror[0x05]++; - - // Move pointers and decrease number of bytes to decrypt - MpqData += (MPQE_CHUNK_SIZE / sizeof(DWORD)); - dwLength -= MPQE_CHUNK_SIZE; - } -} - -static const char * DetectFileKey(LPBYTE pbEncryptedHeader) -{ - ULONGLONG ByteOffset = 0; - BYTE FileHeader[MPQE_CHUNK_SIZE]; - 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(Key, MpqKeyArray[i], MPQE_CHUNK_SIZE); - BSWAP_ARRAY32_UNSIGNED(Key, MPQE_CHUNK_SIZE); - - // Try to decrypt with the given key - memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE); - DecryptFileChunk((LPDWORD)FileHeader, Key, ByteOffset, MPQE_CHUNK_SIZE); - - // 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 MpqKeyArray[i]; - } - - // Key not found, sorry - return NULL; -} - -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 - DWORD dwBytesToRead) // Number of bytes to read from the file -{ - ULONGLONG StartOffset; // Offset of the first byte to be read from the file - ULONGLONG ByteOffset; // Offset that the caller wants - ULONGLONG EndOffset; // End offset that is to be read from the file - DWORD dwBytesToAllocate; - DWORD dwBytesToDecrypt; - DWORD dwOffsetInCache; - LPBYTE pbMpqData = NULL; - bool bResult = false; - - // Get the byte offset - if(pByteOffset == NULL) - pStream->BaseGetPos(pStream, ByteOffset); - else - ByteOffset = *pByteOffset; - - // Cut it down to MPQE chunk size - StartOffset = ByteOffset; - StartOffset = StartOffset & ~(MPQE_CHUNK_SIZE - 1); - EndOffset = ByteOffset + dwBytesToRead; - - // Calculate number of bytes to decrypt - dwBytesToDecrypt = (DWORD)(EndOffset - StartOffset); - dwBytesToAllocate = (dwBytesToDecrypt + (MPQE_CHUNK_SIZE - 1)) & ~(MPQE_CHUNK_SIZE - 1); - - // Allocate buffers for encrypted and decrypted data - pbMpqData = STORM_ALLOC(BYTE, dwBytesToAllocate); - if(pbMpqData) - { - // Get the offset of the desired data in the cache - dwOffsetInCache = (DWORD)(ByteOffset - StartOffset); - - // Read the file from the stream as-is - if(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) - { - // Decrypt the data - DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate); - - // Copy the decrypted data - memcpy(pvBuffer, pbMpqData + dwOffsetInCache, dwBytesToRead); - bResult = true; - } - else - { - assert(false); - } - - // Free decryption buffer - STORM_FREE(pbMpqData); - } - - // Free buffers and exit - return bResult; -} - -static bool EncryptedStream_Open(TEncryptedStream * pStream) -{ - ULONGLONG ByteOffset = 0; - BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; - const char * szKey; - - // Sanity check - assert(pStream->BaseRead != NULL); - - // 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; - } - - // An unknown key - SetLastError(ERROR_UNKNOWN_FILE_KEY); - } - return false; -} - -//----------------------------------------------------------------------------- -// Public functions - -/** - * 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 - * can open the file for read, but not for write) - * - If the file does not exist, the function must create new one - * - If the file exists, the function must rewrite it and set to zero size - * - The parameters of the function must be validate by the caller - * - 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 create - */ - -TFileStream * FileStream_CreateFile( - const TCHAR * szFileName, - DWORD dwStreamFlags) -{ - TFileStream * pStream; - - // We only support creation of linear, local file - if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) - { - 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)) - { - // 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 - return pStream; -} - -/** - * This function opens an existing file for read or read-write access - * - If the current platform supports file sharing, - * the file must be open for read sharing (i.e. another application - * can open the file for read, but not for write) - * - If the file does not exist, the function must return NULL - * - 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 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 dwStreamFlags specifies the provider and base storage type - */ - -TFileStream * FileStream_OpenFile( - const TCHAR * szFileName, - DWORD dwStreamFlags) -{ - TFileStream * pStream = NULL; - size_t StreamSize = 0; - bool bStreamResult = false; - bool bBaseResult = false; - - // Allocate file stream for each stream provider - switch(dwStreamFlags & STREAM_PROVIDER_MASK) - { - case STREAM_PROVIDER_LINEAR: // Allocate structure for linear stream - StreamSize = sizeof(TLinearStream); - break; - - case STREAM_PROVIDER_PARTIAL: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - StreamSize = sizeof(TPartialStream); - break; - - case STREAM_PROVIDER_ENCRYPTED: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - StreamSize = sizeof(TEncryptedStream); - break; - - default: - return NULL; - } - - // Allocate the stream for each type - pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize); - if(pStream == NULL) - return NULL; - - // Fill the stream structure with zeros - memset(pStream, 0, StreamSize); - _tcscpy(pStream->szFileName, szFileName); - - // Now initialize the respective base provider - switch(dwStreamFlags & BASE_PROVIDER_MASK) - { - case BASE_PROVIDER_FILE: - bBaseResult = BaseFile_Open(pStream, szFileName, dwStreamFlags); - break; - - case BASE_PROVIDER_MAP: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - bBaseResult = BaseMap_Open(pStream, szFileName, dwStreamFlags); - break; - - case BASE_PROVIDER_HTTP: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - bBaseResult = BaseHttp_Open(pStream, szFileName, dwStreamFlags); - break; - } - - // If we failed to open the base storage, fail the operation - if(bBaseResult == false) - { - STORM_FREE(pStream); - return NULL; - } - - // Now initialize the stream provider - switch(dwStreamFlags & STREAM_PROVIDER_MASK) - { - case STREAM_PROVIDER_LINEAR: - bStreamResult = LinearStream_Open((TLinearStream *)pStream); - break; - - case STREAM_PROVIDER_PARTIAL: - bStreamResult = PartialStream_Open((TPartialStream *)pStream); - break; - - case STREAM_PROVIDER_ENCRYPTED: - bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream); - break; - } - - // If the operation failed, free the stream and set it to NULL - if(bStreamResult == false) - { - // Only close the base stream - pStream->BaseClose(pStream); - STORM_FREE(pStream); - pStream = NULL; - } - - return pStream; -} - -/** - * Reads data from the stream - * - * - Returns true if the read operation succeeded and all bytes have been read - * - Returns false if either read failed or not all bytes have been read - * - If the pByteOffset is NULL, the function must read the data from the current file position - * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored - * and the function just adjusts file pointer. - * - * \a pStream Pointer to an open stream - * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position - * \a pvBuffer Pointer to data to be read - * \a dwBytesToRead Number of bytes to read from the file - * - * \returns - * - If the function reads the required amount of bytes, it returns true. - * - If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF - * - If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF - */ -bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) -{ - assert(pStream->StreamRead != NULL); - return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); -} - -/** - * This function writes data to the stream - * - * - Returns true if the write operation succeeded and all bytes have been written - * - Returns false if either write failed or not all bytes have been written - * - If the pByteOffset is NULL, the function must write the data to the current file position - * - * \a pStream Pointer to an open stream - * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position - * \a pvBuffer Pointer to data to be written - * \a dwBytesToWrite Number of bytes to write to the file - */ -bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) -{ - if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) - return false; - - assert(pStream->StreamWrite != NULL); - return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); -} - -/** - * 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); -} - -/** - * Returns the size of a file - * - * \a pStream Pointer to an open stream - * \a FileSize Pointer where to store the file size - */ -bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize) -{ - assert(pStream->StreamGetSize != NULL); - return pStream->StreamGetSize(pStream, FileSize); -} - -/** - * Sets the size of a file - * - * \a pStream Pointer to an open stream - * \a NewFileSize File size to set - */ -bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) -{ - if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) - return false; - - 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: - * - * 1) Closes the handle to the existing MPQ - * 2) Renames the temporary MPQ to the original MPQ, overwrites existing one - * 3) Opens the MPQ stores the handle and stream position to the new stream structure - * - * \a pStream Pointer to an open stream - * \a pTempStream Temporary ("working") stream (created during archive compacting) - */ -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) -{ - assert(pStream != NULL); - return pStream->szFileName; -} - -/** - * 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 - */ - -bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap) -{ - TLinearStream * pLinearStream; - - // It must be a linear stream. - if((pStream->dwFlags & STREAM_PROVIDER_MASK) != STREAM_PROVIDER_LINEAR) - return false; - pLinearStream = (TLinearStream *)pStream; - - // Two bitmaps are not allowed - if(pLinearStream->pBitmap != NULL) - return false; - - // We need to change some entry points - pLinearStream->StreamRead = (STREAM_READ)LinearStream_Read; - pLinearStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBitmap; - - // 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 - * can be NULL, if there was an allocation failure during the process - * - * \a pStream Pointer to an open stream - */ -void FileStream_Close(TFileStream * pStream) -{ - // Check if the stream structure is allocated at all - if(pStream != NULL) - { - // 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); - } -} - -//----------------------------------------------------------------------------- -// main - for testing purposes - -#ifdef __STORMLIB_TEST__ -int FileStream_Test(const TCHAR * szFileName, DWORD dwStreamFlags) -{ - TFileStream * pStream; - TMPQHeader MpqHeader; - ULONGLONG FilePos; - TMPQBlock * pBlock; - TMPQHash * pHash; - - InitializeMpqCryptography(); - - 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) - { - 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); - } - - // Read the block table - pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize); - if(pBlock != NULL) - { - 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(); - - // - // Test 1: Write to a stream - // - - pStream = FileStream_CreateFile("E:\\Stream.bin", 0); - if(pStream != NULL) - { - char szString1[100] = "This is a single line\n\r"; - DWORD dwLength = strlen(szString1); - - 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); - } - - // - // Test2: Read from the stream - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - 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("Write succeeded while it should fail\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; - } - - szString2[dwLength] = 0; - if(strcmp(szString1, szString2)) - { - printf("Data read from file are different from data written\n"); - return -1; - } - } - FileStream_Close(pStream); - } - - // - // Test3: Open the temp stream, write some data and switch it to the original stream - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - TFileStream * pTempStream; - ULONGLONG FileSize; - - pTempStream = FileStream_CreateFile("E:\\TempStream.bin", 0); - 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 != 0) - { - DWORD dwBytesToRead = (DWORD)FileSize; - char Buffer[0x80]; - - if(dwBytesToRead > sizeof(Buffer)) - dwBytesToRead = sizeof(Buffer); - - if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead)) - { - printf("CopyStream: Read source file failed\n"); - return -1; - } - - if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead)) - { - printf("CopyStream: Write target file failed\n"); - return -1; - } - - FileSize -= dwBytesToRead; - } - - // Switch the streams - // Note that the pTempStream is closed by the operation - FileStream_Switch(pStream, pTempStream); - FileStream_Close(pStream); - } - - // - // Test4: Read from the stream again - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - char szString1[100] = "This is a single line\n\r"; - char szString2[100]; - DWORD dwLength = strlen(szString1); - - 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; -} -*/ - |