diff options
| author | Ladislav <Zezula> | 2013-12-22 12:47:26 +0100 | 
|---|---|---|
| committer | Ladislav <Zezula> | 2013-12-22 12:47:26 +0100 | 
| commit | 3dd8d1198c46c42a47cd6089e9dd99b9dfdaa798 (patch) | |
| tree | 75f88998ddb4e09187c163a50680a212a4403643 /src | |
| parent | ebd502e0c220f7c2d3b9ce65bd83ed569d65f314 (diff) | |
+ MPQ Bitmap processign was moved to TFileStream
+ Preparing for implementation of master-mirror streaming (like Blizzard games do)
Diffstat (limited to 'src')
| -rw-r--r-- | src/FileStream.cpp | 2163 | ||||
| -rw-r--r-- | src/FileStream.h | 130 | ||||
| -rw-r--r-- | src/SFileCompactArchive.cpp | 2 | ||||
| -rw-r--r-- | src/SFileGetFileInfo.cpp | 14 | ||||
| -rw-r--r-- | src/SFileOpenArchive.cpp | 7 | ||||
| -rw-r--r-- | src/StormLib.h | 30 | 
6 files changed, 1085 insertions, 1261 deletions
diff --git a/src/FileStream.cpp b/src/FileStream.cpp index 403d1d4..58543e3 100644 --- a/src/FileStream.cpp +++ b/src/FileStream.cpp @@ -48,100 +48,125 @@ void SetLastError(int nError)  #endif  //----------------------------------------------------------------------------- -// Preparing file bitmap for a complete file of a given size +// Dummy init function -static bool FileBitmap_CheckRange( -    TFileBitmap * pBitmap, -    ULONGLONG ByteOffset, -    ULONGLONG EndOffset) +static void BaseNone_Init(TFileStream *)  { -    LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); -    DWORD BlockIndex = (DWORD)(ByteOffset / pBitmap->BlockSize); -    DWORD ByteIndex = (BlockIndex / 0x08); -    BYTE BitMask = (BYTE)(0x01 << (BlockIndex & 0x07)); +    // Nothing here +} -    // Mask the bye offset down to the begin of the block -    ByteOffset = ByteOffset & ~((ULONGLONG)pBitmap->BlockSize - 1); +//----------------------------------------------------------------------------- +// Local functions - base file support -    // Check each block -    while(ByteOffset < EndOffset) +static bool BaseFile_Create(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS      { -        // Check availability of that block -        if((pbBitmap[ByteIndex] & BitMask) == 0) +        DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + +        pStream->Base.File.hFile = CreateFile(pStream->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 -        // Move to the next block -        ByteOffset += pBitmap->BlockSize; -        ByteIndex += (BitMask >> 0x07); -        BitMask = (BitMask >> 0x07) | (BitMask << 0x01); +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        intptr_t handle; +         +        handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +        if(handle == -1) +        { +            nLastError = errno; +            return false; +        } +         +        pStream->Base.File.hFile = (HANDLE)handle;      } +#endif -    // All blocks are present +    // Reset the file size and position +    pStream->Base.File.FileSize = 0; +    pStream->Base.File.FilePos = 0;      return true;  } -static void FileBitmap_SetRange( -    TFileBitmap * pBitmap, -    ULONGLONG ByteOffset, -    ULONGLONG EndOffset) +static bool BaseFile_Open(TFileStream * pStream, DWORD dwStreamFlags)  { -    LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); -    DWORD BlockIndex = (DWORD)(ByteOffset / pBitmap->BlockSize); -    DWORD ByteIndex = (BlockIndex / 0x08); -    BYTE BitMask = (BYTE)(0x01 << (BlockIndex & 0x07)); - -    // Mask the bye offset down to the begin of the block -    ByteOffset = ByteOffset & ~((ULONGLONG)pBitmap->BlockSize - 1); - -    // Check each block -    while(ByteOffset < EndOffset) +#ifdef PLATFORM_WINDOWS      { -        // Set that bit -        pbBitmap[ByteIndex] |= BitMask; +        ULARGE_INTEGER FileSize; +        DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; +        DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; -        // Move to the next block -        ByteOffset += pBitmap->BlockSize; -        ByteIndex += (BitMask >> 0x07); -        BitMask = (BitMask >> 0x07) | (BitMask << 0x01); -    } -} +        // Open the file +        pStream->Base.File.hFile = CreateFile(pStream->szFileName, +                                              FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess, +                                              FILE_SHARE_READ | dwWriteShare, +                                              NULL, +                                              OPEN_EXISTING, +                                              0, +                                              NULL); +        if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) +            return false; -static DWORD FileBitmap_CheckFile(TFileBitmap * pBitmap) -{ -    LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); -    DWORD WholeByteCount = (pBitmap->BlockCount / 8); -    DWORD ExtraBitsCount = (pBitmap->BlockCount & 7); -    BYTE ExpectedValue; +        // Query the file size +        FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); +        pStream->Base.File.FileSize = FileSize.QuadPart; -    // Verify the whole bytes - their value must be 0xFF -    for(DWORD i = 0; i < WholeByteCount; i++) -    { -        if(pbBitmap[i] != 0xFF) -            return 0; +        // Query last write time +        GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime);      } +#endif -    // If there are extra bits, calculate the mask -    if(ExtraBitsCount != 0) +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)      { -        ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); -        if(pbBitmap[WholeByteCount] != ExpectedValue) -            return 0; +        struct stat64 fileinfo; +        int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; +        intptr_t handle; + +        // Open the file +        handle = open(szFileName, oflag | O_LARGEFILE); +        if(handle == -1) +        { +            nLastError = errno; +            return false; +        } + +        // Get the file size +        if(fstat64(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 -    // Yes, the file is complete -    return 1; +    // Reset the file position +    pStream->Base.File.FilePos = 0; +    return true;  } -//----------------------------------------------------------------------------- -// 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->FilePos; +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;      DWORD dwBytesRead = 0;                  // Must be set by platform-specific code  #ifdef PLATFORM_WINDOWS @@ -152,7 +177,7 @@ static bool BaseFile_Read(          // one system call to SetFilePointer          // Update the byte offset -        pStream->FilePos = ByteOffset; +        pStream->Base.File.FilePos = ByteOffset;          // Read the data          if(dwBytesToRead != 0) @@ -197,7 +222,7 @@ static bool BaseFile_Read(      // 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->FilePos = ByteOffset + dwBytesRead; +    pStream->Base.File.FilePos = ByteOffset + dwBytesRead;      if(dwBytesRead != dwBytesToRead)          SetLastError(ERROR_HANDLE_EOF);      return (dwBytesRead == dwBytesToRead); @@ -212,7 +237,7 @@ static bool BaseFile_Read(  static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)  { -    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->FilePos; +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;      DWORD dwBytesWritten = 0;               // Must be set by platform-specific code  #ifdef PLATFORM_WINDOWS @@ -223,7 +248,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const          // one system call to SetFilePointer          // Update the byte offset -        pStream->FilePos = ByteOffset; +        pStream->Base.File.FilePos = ByteOffset;          // Read the data          if(dwBytesToWrite != 0) @@ -264,11 +289,11 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const  #endif      // Increment the current file position by number of bytes read -    pStream->FilePos = ByteOffset + dwBytesWritten; +    pStream->Base.File.FilePos = ByteOffset + dwBytesWritten;      // Also modify the file size, if needed -    if(pStream->FilePos > pStream->FileSize) -        pStream->FileSize = pStream->FilePos; +    if(pStream->Base.File.FilePos > pStream->Base.File.FileSize) +        pStream->Base.File.FileSize = pStream->Base.File.FilePos;      if(dwBytesWritten != dwBytesToWrite)          SetLastError(ERROR_DISK_FULL); @@ -279,7 +304,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const   * \a pStream Pointer to an open stream   * \a NewFileSize New size of the file   */ -static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize)  {  #ifdef PLATFORM_WINDOWS      { @@ -297,8 +322,8 @@ static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)          bResult = (bool)SetEndOfFile(pStream->Base.File.hFile);          // Restore the file position -        FileSizeHi = (LONG)(pStream->FilePos >> 32); -        FileSizeLo = (LONG)(pStream->FilePos); +        FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); +        FileSizeLo = (LONG)(pStream->Base.File.FilePos);          SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN);          return bResult;      } @@ -317,8 +342,26 @@ static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)  #endif  } +// Gives the current file size +static bool BaseFile_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ +    // Note: Used by all thre base providers. +    // Requires the TBaseData union to have the same layout for all three base providers +    *pFileSize = pStream->Base.File.FileSize; +    return true; +} + +// Gives the current file position +static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ +    // Note: Used by all thre base providers. +    // Requires the TBaseData union to have the same layout for all three base providers +    *pByteOffset = pStream->Base.File.FilePos; +    return true; +} +  // Renames the file pointed by pStream so that it contains data from pNewStream -static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream) +static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream)  {  #ifdef PLATFORM_WINDOWS      // Delete the original stream file. Don't check the result value, @@ -358,163 +401,23 @@ static void BaseFile_Close(TFileStream * pStream)      pStream->Base.File.hFile = INVALID_HANDLE_VALUE;  } -static bool BaseFile_Create(TFileStream * pStream) +// Initializes base functions for the disk file +static void BaseFile_Init(TFileStream * pStream)  { -#ifdef PLATFORM_WINDOWS -    { -        DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; - -        pStream->Base.File.hFile = CreateFile(pStream->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(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 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->BaseSetSize = BaseFile_SetSize; -    pStream->BaseClose   = BaseFile_Close; - -    // Reset the file position -    pStream->FileSize = 0; -    pStream->FilePos = 0; -    return true; -} - -static bool BaseFile_Open(TFileStream * pStream, DWORD dwStreamFlags) -{ -#ifdef PLATFORM_WINDOWS -    { -        ULARGE_INTEGER FileSize; -        DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; -        DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; - -        // Open the file -        pStream->Base.File.hFile = CreateFile(pStream->szFileName, -                                              FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess, -                                              FILE_SHARE_READ | dwWriteShare, -                                              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->FileSize = FileSize.QuadPart; - -        // Query last write time -        GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->FileTime); -    } -#endif - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) -    { -        struct stat64 fileinfo; -        int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; -        intptr_t handle; - -        // Open the file -        handle = open(szFileName, oflag | O_LARGEFILE); -        if(handle == -1) -        { -            nLastError = errno; -            return false; -        } - -        // Get the file size -        if(fstat64(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->BaseCreate  = BaseFile_Create; +    pStream->BaseOpen    = BaseFile_Open;      pStream->BaseRead    = BaseFile_Read;      pStream->BaseWrite   = BaseFile_Write; -    pStream->BaseSetSize = BaseFile_SetSize; +    pStream->BaseResize  = BaseFile_Resize; +    pStream->BaseGetSize = BaseFile_GetSize; +    pStream->BaseGetPos  = BaseFile_GetPos;      pStream->BaseClose   = BaseFile_Close; - -    // Reset the file position -    pStream->FilePos = 0; -    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->FilePos; - -    // Do we have to read anything at all? -    if(dwBytesToRead != 0) -    { -        // Don't allow reading past file size -        if((ByteOffset + dwBytesToRead) > pStream->FileSize) -            return false; - -        // Copy the required data -        memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead); -    } - -    // Move the current file position -    pStream->FilePos += dwBytesToRead; -    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->FileSize); -#endif - -    pStream->Base.Map.pbFile = NULL; -} - -static bool BaseMap_Open(TFileStream * pStream) +static bool BaseMap_Open(TFileStream * pStream, DWORD dwStreamFlags)  {  #ifdef PLATFORM_WINDOWS @@ -523,6 +426,9 @@ static bool BaseMap_Open(TFileStream * pStream)      HANDLE hMap;      bool bResult = false; +    // Keep compiler happy +    dwStreamFlags = dwStreamFlags; +      // Open the file for read access      hFile = CreateFile(pStream->szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);      if(hFile != NULL) @@ -531,9 +437,6 @@ static bool BaseMap_Open(TFileStream * pStream)          FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);          if(FileSize.QuadPart != 0)          { -            // Retrieve file time -            GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->FileTime); -              // Now create mapping object              hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);              if(hMap != NULL) @@ -544,8 +447,12 @@ static bool BaseMap_Open(TFileStream * pStream)                  pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);                  if(pStream->Base.Map.pbFile != NULL)                  { -                    pStream->FileSize = FileSize.QuadPart; -                    pStream->FilePos = 0; +                    // Retrieve file time +                    GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + +                    // Retrieve file size and position +                    pStream->Base.Map.FileSize = FileSize.QuadPart; +                    pStream->Base.Map.FilePos = 0;                      bResult = true;                  } @@ -599,10 +506,60 @@ static bool BaseMap_Open(TFileStream * pStream)      }  #endif -    // Fill-in entry points +    return true; +} + +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 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->FileSize); +#endif + +    pStream->Base.Map.pbFile = NULL; +} + +// Initializes base functions for the mapped file +static void BaseMap_Init(TFileStream * pStream) +{ +    // Supply the file stream functions +    pStream->BaseOpen    = BaseMap_Open;      pStream->BaseRead    = BaseMap_Read; +    pStream->BaseGetSize = BaseFile_GetSize;    // Reuse BaseFile function +    pStream->BaseGetPos  = BaseFile_GetPos;     // Reuse BaseFile function      pStream->BaseClose   = BaseMap_Close; -    return true; + +    // Mapped files are read-only +    pStream->dwFlags |= STREAM_FLAG_READ_ONLY;  }  //----------------------------------------------------------------------------- @@ -631,6 +588,110 @@ static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR      return szFileName;  } +static bool BaseHttp_Open(TFileStream * pStream, DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + +    const TCHAR * szFileName; +    HINTERNET hRequest; +    DWORD dwTemp = 0; +    bool bFileAvailable = false; +    int nError = ERROR_SUCCESS; + +    // Keep compiler happy +    dwStreamFlags = dwStreamFlags; + +    // 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(pStream->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) +    { +        pStream->BaseClose(pStream); +        return false; +    } + +    return true; + +#else + +    // Not supported +    SetLastError(ERROR_NOT_SUPPORTED); +    pStream = pStream; +    return false; + +#endif +} +  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 @@ -638,7 +699,7 @@ static bool BaseHttp_Read(      DWORD dwBytesToRead)                    // Number of bytes to read from the file  {  #ifdef PLATFORM_WINDOWS -    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->FilePos; +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos;      DWORD dwTotalBytesRead = 0;      // Do we have to read anything at all? @@ -688,7 +749,7 @@ static bool BaseHttp_Read(      }      // Increment the current file position by number of bytes read -    pStream->FilePos = ByteOffset + dwTotalBytesRead; +    pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead;      // If the number of bytes read doesn't match the required amount, return false      if(dwTotalBytesRead != dwBytesToRead) @@ -723,242 +784,318 @@ static void BaseHttp_Close(TFileStream * pStream)  #endif  } -static bool BaseHttp_Open(TFileStream * pStream) +// Initializes base functions for the mapped file +static void BaseHttp_Init(TFileStream * pStream)  { -#ifdef PLATFORM_WINDOWS +    // Supply the stream functions +    pStream->BaseOpen    = BaseHttp_Open; +    pStream->BaseRead    = BaseHttp_Read; +    pStream->BaseGetSize = BaseFile_GetSize;    // Reuse BaseFile function +    pStream->BaseGetPos  = BaseFile_GetPos;     // Reuse BaseFile function +    pStream->BaseClose   = BaseHttp_Close; -    const TCHAR * szFileName; -    HINTERNET hRequest; -    DWORD dwTemp = 0; -    bool bFileAvailable = false; -    int nError = ERROR_SUCCESS; +    // HTTP files are read-only +    pStream->dwFlags |= STREAM_FLAG_READ_ONLY; +} -    // Don't connect to the internet -    if(!InternetGetConnectedState(&dwTemp, 0)) -        nError = GetLastError(); +//----------------------------------------------------------------------------- +// Local functions - base block-based support -    // Initiate the connection to the internet -    if(nError == ERROR_SUCCESS) +// Generic function that loads blocks from the file +// The function groups the block with the same availability, +// so the called BlockRead can finish the request in a single system call +static bool BlockStream_Read( +    TBlockStream * 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 BlockOffset0; +    ULONGLONG BlockOffset; +    ULONGLONG ByteOffset; +    ULONGLONG EndOffset; +    LPBYTE TransferBuffer; +    LPBYTE BlockBuffer; +    DWORD BlockBufferOffset;                // Offset of the desired data in the block buffer +    DWORD BytesNeeded;                      // Number of bytes that really need to be read +    DWORD BlockSize = pStream->BlockSize; +    DWORD BlockCount; +    bool bPrevBlockAvailable; +    bool bBlockAvailable; +    bool bResult = true; + +    // The base block read function must be present +    assert(pStream->BlockCheck != NULL); +    assert(pStream->BlockRead != NULL); + +    // NOP reading of zero bytes +    if(dwBytesToRead == 0) +        return true; + +    // Get the current position in the stream +    ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : pStream->StreamPos; +    EndOffset = ByteOffset + dwBytesToRead; +    if(EndOffset > pStream->StreamSize)      { -        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(); +        SetLastError(ERROR_CAN_NOT_COMPLETE); +        return false;      } -    // 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; +    // Calculate the block parameters +    BlockOffset0 = BlockOffset = ByteOffset & ~((ULONGLONG)BlockSize - 1); +    BlockCount  = (DWORD)(((EndOffset - BlockOffset) + (BlockSize - 1)) / BlockSize); +    BytesNeeded = (DWORD)(EndOffset - BlockOffset); -        // Initiate connection with the server -        szFileName = BaseHttp_ExtractServerName(pStream->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(); +    // Remember where we have our data +    assert((BlockSize & (BlockSize - 1)) == 0); +    BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1)); + +    // Allocate buffer for reading blocks +    TransferBuffer = BlockBuffer = STORM_ALLOC(BYTE, (BlockCount * BlockSize)); +    if(TransferBuffer == NULL) +    { +        SetLastError(ERROR_NOT_ENOUGH_MEMORY); +        return false;      } -    // Now try to query the file size -    if(nError == ERROR_SUCCESS) +    // If all blocks are available, just read all blocks at once +    if(pStream->IsComplete == 0)      { -        // 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) +        // Now parse the blocks and send the block read request +        // to all blocks with the same availability +        bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); + +        // Loop as long as we have something to read +        while(BlockOffset < EndOffset)          { -            if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) +            // Determine availability of the next block +            bBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); + +            // If the availability has changed, read all blocks up to this one +            if(bBlockAvailable != bPrevBlockAvailable)              { -                ULONGLONG FileTime = 0; -                DWORD dwFileSize = 0; -                DWORD dwDataSize; -                DWORD dwIndex = 0; +                // Load the continuous blocks with the same availability +                assert(BlockOffset > BlockOffset0); +                bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); +                if(!bResult) +                    break; + +                // Move the block offset +                BlockBuffer += (DWORD)(BlockOffset - BlockOffset0); +                BytesNeeded -= (DWORD)(BlockOffset - BlockOffset0); +                bPrevBlockAvailable = bBlockAvailable; +                BlockOffset0 = BlockOffset; +            } -                // 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->FileTime = FileTime; +            // Move to the block offset in the stream +            BlockOffset += BlockSize; +        } -                // 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->FileSize = dwFileSize; -                        pStream->FilePos = 0; -                        bFileAvailable = true; -                    } -                } -            } -            InternetCloseHandle(hRequest); +        // If there is a block(s) remaining to be read, do it +        if(BlockOffset > BlockOffset0) +        { +            // Read the complete blocks from the file +            if(BlockOffset > pStream->StreamSize) +                BlockOffset = pStream->StreamSize; +            bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable);          }      } +    else +    { +        // Read the complete blocks from the file +        if(EndOffset > pStream->StreamSize) +            EndOffset = pStream->StreamSize; +        bResult = pStream->BlockRead(pStream, BlockOffset, EndOffset, BlockBuffer, BytesNeeded, true); +    } -    // If the file is not there and is not available for random access, -    // report error -    if(bFileAvailable == false) +    // Now copy the data to the user buffer +    if(bResult)      { -        BaseHttp_Close(pStream); -        return false; +        memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead); +        pStream->StreamPos = ByteOffset + dwBytesToRead;      } -    // Fill-in entry points -    pStream->BaseRead    = BaseHttp_Read; -    pStream->BaseClose   = BaseHttp_Close; +    // Free the block buffer and return +    STORM_FREE(TransferBuffer); +    return bResult; +} + +static bool BlockStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ +    *pFileSize = pStream->StreamSize;      return true; +} -#else +static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ +    *pByteOffset = pStream->StreamPos; +    return true; +} -    // Not supported -    SetLastError(ERROR_NOT_SUPPORTED); -    pStream = pStream; -    return false; +static void BlockStream_Close(TBlockStream * pStream) +{ +    // Free the data map, if any +    if(pStream->FileBitmap != NULL) +        STORM_FREE(pStream->FileBitmap); +    pStream->FileBitmap = NULL; -#endif +    // Call the base class for closing the stream +    pStream->BaseClose(pStream);  }  //----------------------------------------------------------------------------- -// Local functions - bitmap for a complete file - -#define DEFAULT_BLOCK_SIZE 0x4000 - -static bool CompleteFile_GetBmp( -    TFileStream * pStream, -    void * pvBitmap, -    DWORD Length, -    LPDWORD LengthNeeded) -{ -    TFileBitmap * pBitmap = (TFileBitmap *)pvBitmap; -    ULONGLONG FileSize = pStream->FileSize; -    DWORD TotalLength; -    DWORD BlockCount = (DWORD)(((FileSize - 1) / DEFAULT_BLOCK_SIZE) + 1); -    DWORD BitmapSize = (DWORD)(((BlockCount - 1) / 8) + 1); -    DWORD LastByte; - -    // 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)) -    { -        pBitmap->StartOffset = 0; -        pBitmap->EndOffset   = FileSize; -        pBitmap->BitmapSize  = BitmapSize; -        pBitmap->BlockSize   = DEFAULT_BLOCK_SIZE; -        pBitmap->BlockCount  = BlockCount; -        pBitmap->IsComplete  = 1; - -        // Do we have enough space to fill the bitmap as well? -        if(Length >= TotalLength) -        { -            LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); +// File stream allocation function -            // Fill the full blocks -            memset(pbBitmap, 0xFF, (BlockCount / 8)); -            pbBitmap += (BlockCount / 8); +static STREAM_INIT StreamBaseInit[4] = +{ +    BaseFile_Init, +    BaseMap_Init,  +    BaseHttp_Init, +    BaseNone_Init +}; -            // Supply the last block -            if(BlockCount & 7) -            { -                LastByte = (1 << (BlockCount & 7)) - 1; -                pbBitmap[0] = (BYTE)LastByte; -            } -        } +// This function allocates an empty structure for the file stream +// The stream structure is created as variable length, linear block of data +// The file name is placed after the end of the stream structure data +static TFileStream * AllocateFileStream( +    const TCHAR * szFileName, +    size_t StreamSize, +    DWORD dwStreamFlags) +{ +    TFileStream * pMaster = NULL; +    TFileStream * pStream; +    const TCHAR * szNextFile = szFileName; +    size_t FileNameSize; -        return true; +    // Sanity check +    assert(StreamSize != 0); + +    // The caller can specify chain of files in the following form: +    // C:\archive.MPQ*http://www.server.com/MPQs/archive-server.MPQ +    // In that case, we use the part after "*" as master file name +    while(szNextFile[0] != 0 && szNextFile[0] != _T('*')) +        szNextFile++; +    FileNameSize = (size_t)((szNextFile - szFileName) * sizeof(TCHAR)); + +    // If we have a next file, we need to open it as master stream +    // Note that we don't care if the master stream exists or not, +    // If it doesn't, later attempts to read missing file block will fail +    if(szNextFile[0] == _T('*')) +    { +        // Don't allow another master file in the string +        if(_tcschr(szNextFile + 1, _T('*')) != NULL) +        { +            SetLastError(ERROR_INVALID_PARAMETER); +            return NULL; +        } +         +        // Open the master file +        pMaster = FileStream_OpenFile(szNextFile + 1, STREAM_FLAG_READ_ONLY);      } -    else + +    // Allocate the stream structure for the given stream type +    pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR)); +    if(pStream != NULL)      { -        SetLastError(ERROR_INSUFFICIENT_BUFFER); -        return false; +        // Zero the entire structure +        memset(pStream, 0, StreamSize); +        pStream->pMaster = pMaster; +        pStream->dwFlags = dwStreamFlags; + +        // Initialize the file name +        pStream->szFileName = (TCHAR *)((BYTE *)pStream + StreamSize); +        memcpy(pStream->szFileName, szFileName, FileNameSize); +        pStream->szFileName[FileNameSize / sizeof(TCHAR)] = 0; + +        // Initialize the stream functions +        StreamBaseInit[dwStreamFlags & 0x03](pStream);      } + +    return pStream;  }  //-----------------------------------------------------------------------------  // Local functions - linear stream support -typedef struct _DATA_BLOCK_INFO +static DWORD LinearStream_CheckFile(TBlockStream * pStream)  { -    ULONGLONG BlockOffset0;                 // Offset of the first block in the continuous array -    ULONGLONG BlockOffset;                  // Offset of the current block -    ULONGLONG ByteOffset;                   // Offset of the loaded data -    ULONGLONG EndOffset;                    // Offset of the end of the block -    LPBYTE ReadBuffer;                      // Pointer to the buffer where to read the data -    DWORD BlockSize;                        // Length of one block, in bytes -    DWORD ByteIndex0;                       // Byte index of the first block in the continuous array -    DWORD ByteIndex;                        // Index of the byte in the file bitmap -    BYTE MirrorUpdated;                     // If set to nonzero, the mirror stream has been updated -    BYTE BitMask0;                          // Bit mask of the first block in the continuous array -    BYTE BitMask;                           // Bit mask of the current bit in file bitmap +    LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; +    DWORD WholeByteCount = (pStream->BlockCount / 8); +    DWORD ExtraBitsCount = (pStream->BlockCount & 7); +    BYTE ExpectedValue; + +    // Verify the whole bytes - their value must be 0xFF +    for(DWORD i = 0; i < WholeByteCount; i++) +    { +        if(FileBitmap[i] != 0xFF) +            return 0; +    } -} DATA_BLOCK_INFO, *PDATA_BLOCK_INFO; +    // If there are extra bits, calculate the mask +    if(ExtraBitsCount != 0) +    { +        ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); +        if(FileBitmap[WholeByteCount] != ExpectedValue) +            return 0; +    } -static bool LinearStream_LoadBitmap( -    TLinearStream * pStream)                // Pointer to an open stream +    // Yes, the file is complete +    return 1; +} + +static bool LinearStream_LoadBitmap(TBlockStream * pStream)  {      FILE_BITMAP_FOOTER Footer; -    TFileBitmap * pBitmap;      ULONGLONG ByteOffset;  +    LPBYTE FileBitmap;      DWORD BlockCount;      DWORD BitmapSize; -    // Only if the size is greater than sizeof bitmap footer -    if(pStream->FileSize > sizeof(FILE_BITMAP_FOOTER)) +    // Do not load the bitmap if we should not have to +    if(!(pStream->dwFlags & STREAM_FLAG_USE_BITMAP)) +        return false; + +    // Only if the size is greater than size of bitmap footer +    if(pStream->Base.File.FileSize > sizeof(FILE_BITMAP_FOOTER))      {          // Load the bitmap footer -        ByteOffset = pStream->FileSize - sizeof(FILE_BITMAP_FOOTER); +        ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER);          if(pStream->StreamRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER)))          {              // Make sure that the array is properly BSWAP-ed              BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&Footer), sizeof(FILE_BITMAP_FOOTER));              // Verify if there is actually a footer -            if(Footer.dwSignature == ID_FILE_BITMAP_FOOTER && Footer.dwAlways3 == 0x03) +            if(Footer.Signature == ID_FILE_BITMAP_FOOTER && Footer.Version == 0x03)              { -                // Get offset of the bitmap, size of the bitmap and check for match -                ByteOffset = MAKE_OFFSET64(Footer.dwMapOffsetHi, Footer.dwMapOffsetLo); -                BlockCount = (DWORD)(((ByteOffset - 1) / Footer.dwBlockSize) + 1); +                // Get the offset of the bitmap, number of blocks and size of the bitmap +                ByteOffset = MAKE_OFFSET64(Footer.MapOffsetHi, Footer.MapOffsetLo); +                BlockCount = (DWORD)(((ByteOffset - 1) / Footer.BlockSize) + 1);                  BitmapSize = ((BlockCount + 7) / 8);                  // Check if the sizes match -                if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->FileSize) +                if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize)                  {                      // Allocate space for the linear bitmap -                    pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + BitmapSize); -                    if(pBitmap != NULL) +                    FileBitmap = STORM_ALLOC(BYTE, BitmapSize); +                    if(FileBitmap != NULL)                      { -                        // Fill the bitmap header -                        pBitmap->StartOffset = 0; -                        pBitmap->EndOffset  = ByteOffset; -                        pBitmap->BitmapSize = BitmapSize; -                        pBitmap->BlockSize  = Footer.dwBlockSize; -                        pBitmap->BlockCount = BlockCount; -                          // Load the bitmap bits -                        if(!pStream->BaseRead(pStream, &ByteOffset, (pBitmap + 1), BitmapSize)) +                        if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize))                          { -                            STORM_FREE(pBitmap); +                            STORM_FREE(FileBitmap);                              return false;                          } -                        // Verify if the file is complete or not -                        pBitmap->IsComplete = FileBitmap_CheckFile(pBitmap); -                         -                        // Set the file bitmap into the file stream -                        pStream->FileSize = ByteOffset; -                        pStream->pBitmap = pBitmap; +                        // Update the stream size +                        pStream->StreamSize = ByteOffset; + +                        // Fill the bitmap information +                        pStream->FileBitmap = FileBitmap; +                        pStream->BitmapSize = BitmapSize; +                        pStream->BlockSize  = Footer.BlockSize; +                        pStream->BlockCount = BlockCount; +                        pStream->IsComplete = LinearStream_CheckFile(pStream);                          return true;                      }                  } @@ -969,370 +1106,171 @@ static bool LinearStream_LoadBitmap(      return false;  } -static bool LinearStream_LoadMissingBlocks( -    TLinearStream * pStream, -    ULONGLONG ByteOffset, -    ULONGLONG EndOffset, -    void * pvBuffer) -{ -    TFileBitmap * pBitmap = pStream->pBitmap; -    ULONGLONG BlockSizeMask = pStream->pBitmap->BlockSize; -    ULONGLONG BlockOffset = ByteOffset & ~(BlockSizeMask - 1); -    ULONGLONG BlockEnd = (EndOffset + (pStream->pBitmap->BlockSize - 1)) & ~(BlockSizeMask - 1); -    LPBYTE pbDataBlock; -    DWORD cbBytesToCopy = (DWORD)(EndOffset - ByteOffset); -    DWORD cbBlockSize = (DWORD)(BlockEnd - BlockOffset); -    bool bResult = false; - -    // Sanity check -    assert(pStream->pBitmap != NULL); -     -    // Cannot load missing blocks if no master file -    if(pStream->pMaster == NULL) -        return false; - -    // Allocate space for the file block -    pbDataBlock = STORM_ALLOC(BYTE, cbBlockSize); -    if(pbDataBlock != NULL) -    { -        // Load the entire missing block from the master MPQ -        if(FileStream_Read(pStream->pMaster, &BlockOffset, pbDataBlock, cbBlockSize)) -        { -            // We can satisfy the read from the loaded data -            assert(cbBytesToCopy <= cbBlockSize); -            memcpy(pvBuffer, pbDataBlock + (DWORD)(ByteOffset - BlockOffset), cbBytesToCopy); -            bResult = true; - -            // Write the file block to the cached archive -            if(pStream->BaseWrite(pStream, &BlockOffset, pbDataBlock, cbBlockSize)) -            { -                // Update the file bitmap -                FileBitmap_SetRange(pStream->pBitmap, BlockOffset, BlockEnd); - -                // If this fails, the data blocks will be re-downloaded next time, -                // but the file is not corrupt -                ByteOffset = pBitmap->EndOffset; -                if(!pStream->BaseWrite(pStream, &ByteOffset, pBitmap + 1, pBitmap->BitmapSize)) -                    bResult = false; -            } -        } - -        // Free the file block -        STORM_FREE(pbDataBlock); -    } - -    return bResult; -} - -static bool LinearStream_ReadBlocks( -    TLinearStream * pStream,                // Pointer to an open stream -    DATA_BLOCK_INFO & bi, -    bool bBlocksAreAvailable) -{ -    ULONGLONG EndOffset; -    LPBYTE pbBlockBuffer; -    DWORD BlockToRead = 0; -    DWORD BytesToRead = 0; -    DWORD ReadOffset; -    bool bResult = true; - -    // Only do something if there is at least one block to be read -    if(bi.BlockOffset > bi.BlockOffset0) -    { -        // Get the read range -        EndOffset = STORMLIB_MIN(bi.BlockOffset, bi.EndOffset); -        BytesToRead = (DWORD)(EndOffset - bi.ByteOffset); - -        // If the block is not available, we need to load them from the master and store to the mirror -        if(bBlocksAreAvailable == false) -        { -            // If we have no master, we cannot satisfy read request -            if(pStream->pMaster == NULL) -                return false; - -            // Allocate buffer and read the complete blocks -            BlockToRead = (DWORD)(bi.BlockOffset - bi.BlockOffset0); -            pbBlockBuffer = STORM_ALLOC(BYTE, BlockToRead); -            if(pbBlockBuffer == NULL) -                return false; - -            // Load the block buffer from the master stream -            if(FileStream_Read(pStream->pMaster, &bi.BlockOffset0, pbBlockBuffer, BlockToRead)) -            { -                // We can now satisfy the request -                ReadOffset = (DWORD)(bi.ByteOffset - bi.BlockOffset0); -                memcpy(bi.ReadBuffer, pbBlockBuffer + ReadOffset, BytesToRead); - -                // Store the loaded blocks to the mirror file -                if(pStream->BaseWrite(pStream, &bi.BlockOffset0, pbBlockBuffer, BlockToRead)) -                    bi.MirrorUpdated = 1; -            } - -            // Free the transfer buffer -            STORM_FREE(pbBlockBuffer); -        } - -        // If the blocks are available, we just read them from the mirror -        else -        { -            // Perform the file read -            bResult = pStream->BaseRead(pStream, &bi.ByteOffset, bi.ReadBuffer, BytesToRead); -        } - -        // Move the offsets and bit masks -        bi.BlockOffset0 = bi.BlockOffset; -        bi.ByteOffset  += BytesToRead; -        bi.ReadBuffer  += BytesToRead; -    } -    return bResult; -} - -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 +static void LinearStream_UpdateBitmap( +    TBlockStream * pStream,                // Pointer to an open stream +    ULONGLONG StartOffset, +    ULONGLONG EndOffset)  { -    DATA_BLOCK_INFO bi; -    TFileBitmap * pBitmap = pStream->pBitmap; -    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->FilePos; -    LPBYTE FileBitmap; +    LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap;      DWORD BlockIndex; -    bool bPrevBlockAvailable; -    bool bBlockAvailable; - -    // NOP reading zero bytes -    if(dwBytesToRead == 0) -        return true; - -    // Cannot read past the end of the file -    if((ByteOffset + dwBytesToRead) > pStream->FileSize) -    { -        SetLastError(ERROR_HANDLE_EOF); -        return false; -    } +    DWORD BlockSize = pStream->BlockSize; +    DWORD ByteIndex; +    BYTE BitMask; -    // If we have no data bitmap, we assume that the file is complete. -    if(pBitmap == NULL) -        return pStream->BaseRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); +    // Sanity checks +    assert((StartOffset & (BlockSize - 1)) == 0); +    assert(FileBitmap != NULL);      // Calculate the index of the block -    FileBitmap = (LPBYTE)(pBitmap + 1); -    BlockIndex = (DWORD)(ByteOffset / pBitmap->BlockSize); - -    // Fill the data block info -    bi.BlockOffset0 =  -    bi.BlockOffset  = ByteOffset & ~((ULONGLONG)pBitmap->BlockSize - 1); -    bi.ByteOffset   = ByteOffset; -    bi.EndOffset    = ByteOffset + dwBytesToRead; -    bi.ReadBuffer   = (LPBYTE)pvBuffer; -    bi.BlockSize    = pBitmap->BlockSize; -    bi.ByteIndex0   = -    bi.ByteIndex    = (BlockIndex / 0x08); -    bi.BitMask0     = -    bi.BitMask      = (BYTE)(0x01 << (BlockIndex & 0x07)); -     -    // Check if the current block is available -    bPrevBlockAvailable = (FileBitmap[bi.ByteIndex] & bi.BitMask) ? true : false; - -    // Loop as long as we have something to read -    while(bi.BlockOffset < bi.EndOffset) -    { -        // Determine if that block is available in the mirror file -        bBlockAvailable = (FileBitmap[bi.ByteIndex] & bi.BitMask) ? true : false; - -        // If the availability has changed, -        // reload all the previous blocks with the same availability -        if(bBlockAvailable != bPrevBlockAvailable) -        { -            if(!LinearStream_ReadBlocks(pStream, bi, bPrevBlockAvailable)) -            { -                SetLastError(ERROR_CAN_NOT_COMPLETE); -                return false; -            } - -            bPrevBlockAvailable = bBlockAvailable; -        } +    BlockIndex = (DWORD)(StartOffset / BlockSize); +    ByteIndex = (BlockIndex / 0x08); +    BitMask = (BYTE)(1 << (BlockIndex & 0x07)); -        // Move to the next block in the stream -        bi.BlockOffset += bi.BlockSize; -        bi.ByteIndex += (bi.BitMask >> 0x07); -        bi.BitMask = (bi.BitMask >> 0x07) | (bi.BitMask << 0x01); -    } - -    // We now need to read the last blocks that weren't loaded in the loop -    if(!LinearStream_ReadBlocks(pStream, bi, bPrevBlockAvailable)) +    // Set all bits for the specified range +    while(StartOffset < EndOffset)      { -        SetLastError(ERROR_CAN_NOT_COMPLETE); -        return false; -    } +        // Set the bit +        FileBitmap[ByteIndex] |= BitMask; -    // We also need to update the file bitmap in the file -    if(bi.MirrorUpdated) -    { -        // Update all bits in the bitmap -        while(bi.ByteIndex0 != bi.ByteIndex || bi.BitMask0 != bi.BitMask) -        { -            FileBitmap[bi.ByteIndex0] |= bi.BitMask0; -            bi.ByteIndex0 += (bi.BitMask0 >> 0x07); -            bi.BitMask0 = (bi.BitMask0 >> 0x07) | (bi.BitMask0 << 0x01); -        } - -        // Write the updated bitmap to the mirror file -        pStream->BaseWrite(pStream, &pBitmap->EndOffset, FileBitmap, pBitmap->BitmapSize); +        // Move all +        StartOffset += BlockSize; +        ByteIndex += (BitMask >> 0x07); +        BitMask = (BitMask >> 0x07) | (BitMask << 0x01);      } - -    // Increment the position -    pStream->FilePos = ByteOffset + dwBytesToRead; -    return true;  } -static bool LinearStream_Write(TLinearStream *, ULONGLONG *, const void *, DWORD) +static bool LinearStream_BlockCheck( +    TBlockStream * pStream,                // Pointer to an open stream +    ULONGLONG BlockOffset)  { -    // Writing to linear stream with bitmap is not allowed -    SetLastError(ERROR_ACCESS_DENIED); -    return false; -} +    LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; +    DWORD BlockIndex; +    BYTE BitMask; -static bool LinearStream_GetSize(TLinearStream * pStream, ULONGLONG * pFileSize) -{ -    *pFileSize = pStream->FileSize; -    return true; -} +    // Sanity checks +    assert((BlockOffset & (pStream->BlockSize - 1)) == 0); +    assert(FileBitmap != NULL); +     +    // Calculate the index of the block +    BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize); +    BitMask = (BYTE)(1 << (BlockIndex & 0x07)); -static bool LinearStream_GetTime(TLinearStream * pStream, ULONGLONG * pFileTime) -{ -    *pFileTime = pStream->FileTime; -    return true; +    // Check if the bit is present +    return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false;  } -static bool LinearStream_GetPos(TLinearStream * pStream, ULONGLONG * pByteOffset) +static bool LinearStream_BlockRead( +    TBlockStream * pStream,                // Pointer to an open stream +    ULONGLONG StartOffset, +    ULONGLONG EndOffset, +    LPBYTE BlockBuffer, +    DWORD BytesNeeded, +    bool bAvailable)  { -    *pByteOffset = pStream->FilePos; -    return true; -} +    DWORD BytesToRead = (DWORD)(EndOffset - StartOffset); -static bool LinearStream_GetBmp( -    TLinearStream * pStream, -    void * pvBitmap, -    DWORD Length, -    LPDWORD LengthNeeded) -{ -    DWORD TotalLength; -    DWORD CopyLength = sizeof(TFileBitmap); +    // The starting offset must be aligned to size of the block +    assert(pStream->FileBitmap != NULL && pStream->IsComplete == 0); +    assert((StartOffset & (pStream->BlockSize - 1)) == 0); +    assert(StartOffset < EndOffset); + +    // If the blocks are not available, we need to load them from the master +    // and then save to the mirror +    if(bAvailable == false) +    { +        // If we have no master, we cannot satisfy read request +        if(pStream->pMaster == NULL) +            return false; -    // Assumed that we have bitmap now -    assert(pStream->pBitmap != NULL); +        // Load the blocks from the master stream +        // Note that we always have to read complete blocks +        // so they get properly stored to the mirror stream +        if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) +            return false; -    // Give the bitmap length -    TotalLength = sizeof(TFileBitmap) + pStream->pBitmap->BitmapSize; -    if(LengthNeeded != NULL) -        *LengthNeeded = TotalLength; +        // Store the loaded blocks to the mirror file. +        // Note that this operation is not required to succeed +        if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead) && bAvailable == false) +            LinearStream_UpdateBitmap(pStream, StartOffset, EndOffset); -    // Do we have enough space to fill at least the bitmap structure? -    if(Length >= sizeof(TFileBitmap)) -    { -        // Enough space for the complete bitmap? -        if(Length >= TotalLength) -            CopyLength = TotalLength; -        memcpy(pvBitmap, pStream->pBitmap, CopyLength);          return true;      }      else      { -        SetLastError(ERROR_INSUFFICIENT_BUFFER); -        return false; +        if(BytesToRead > BytesNeeded) +            BytesToRead = BytesNeeded; +        return pStream->BaseRead(pStream, &StartOffset, BlockBuffer, BytesToRead);      }  } -static bool LinearStream_Switch(TLinearStream * pStream, TLinearStream * pNewStream) +static bool LinearStream_CreateMirror(TBlockStream * pStream)  { -    // 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->dwFlags)) +    // Do we have master function and base creation function? +    if(pStream->pMaster == NULL || pStream->BaseCreate == NULL)          return false; -    // We need to cleanup the new data stream -    FileStream_Close(pNewStream); -    return true; +    return false;  } -static void LinearStream_Close(TLinearStream * pStream) +static TFileStream * LinearStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)  { -    // Free the souce stream, if any -    if(pStream->pMaster != NULL) -        FileStream_Close(pStream->pMaster); -    pStream->pMaster = NULL; +    TBlockStream * pStream;     -    // Free the data map, if any -    if(pStream->pBitmap != NULL) -        STORM_FREE(pStream->pBitmap); -    pStream->pBitmap = NULL; +    // Create new empty stream +    pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); +    if(pStream == NULL) +        return NULL; -    // Call the base class for closing the stream -    pStream->BaseClose(pStream); -} +    // Attempt to open the base stream. If this fails +    // and we have a master stream, we can create new mirror +    assert(pStream->BaseOpen != NULL); +    if(!pStream->BaseOpen(pStream, dwStreamFlags)) +    { +        // Do we have base create function and master stream? +//      if(!LinearStream_CreateMirror(pStream)) +        { +            FileStream_Close(pStream); +            SetLastError(ERROR_FILE_NOT_FOUND); +            return NULL; +        } +    } -static bool LinearStream_Open(TLinearStream * pStream) -{ -    // Set the entry points +    // Set the stream function as if this was linear file without bitmap      pStream->StreamRead    = pStream->BaseRead;      pStream->StreamWrite   = pStream->BaseWrite; -    pStream->StreamSetSize = pStream->BaseSetSize; -    pStream->StreamGetSize = (STREAM_GETSIZE)LinearStream_GetSize; -    pStream->StreamGetTime = (STREAM_GETTIME)LinearStream_GetTime; -    pStream->StreamGetPos  = (STREAM_GETPOS)LinearStream_GetPos; -    pStream->StreamGetBmp  = (STREAM_GETBMP)CompleteFile_GetBmp; -    pStream->StreamSwitch  = (STREAM_SWITCH)LinearStream_Switch; -    pStream->StreamClose   = (STREAM_CLOSE)LinearStream_Close; - -    // If the caller wanted us to load stream bitmap, do it -    if(pStream->dwFlags & STREAM_FLAG_USE_BITMAP) +    pStream->StreamResize  = pStream->BaseResize; +    pStream->StreamGetSize = pStream->BaseGetSize; +    pStream->StreamGetPos  = pStream->BaseGetPos; +    pStream->StreamClose   = pStream->BaseClose; + +    // Setup the stream size +    pStream->StreamSize = pStream->Base.File.FileSize; + +    // If we load the bitmap and find out that it's an incomplete file, +    // We set the reading functions which check presence of each file block +    if(LinearStream_LoadBitmap(pStream))      { -        // Attempt to load the file bitmap -        LinearStream_LoadBitmap(pStream); +        // Set the stream position to zero. Stream size is already set +        assert(pStream->StreamSize != 0); +        pStream->StreamPos = 0; +        pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -        // Reset the position to zero after manipulating with file pointer -        pStream->FilePos = 0; -         -        // If there is a file bitmap and the file is not complete, -        // we need to set the reading function so that it verifies each block -        if(pStream->pBitmap != NULL) +        // Set the reading function +        if(pStream->IsComplete == 0)          { -            // Set different function for retrieving file bitmap -            pStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBmp; - -            // If the file is not complete, we also need to set different function for file read+write -            if(pStream->pBitmap->IsComplete == 0) -            { -                // If we also have source file, open that one  -                if(pStream->szSourceName != NULL) -                    pStream->pMaster = FileStream_OpenFile(pStream->szSourceName, STREAM_FLAG_READ_ONLY | STREAM_FLAG_USE_BITMAP); - -                // Change functions for read+write -                pStream->StreamWrite = (STREAM_WRITE)LinearStream_Write; -                pStream->StreamRead = (STREAM_READ)LinearStream_Read; -                pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -            } +            // Supply the reading function +            pStream->StreamRead    = (STREAM_READ)BlockStream_Read; +            pStream->StreamGetSize = BlockStream_GetSize; +            pStream->StreamGetPos  = BlockStream_GetPos; +            pStream->StreamClose   = (STREAM_CLOSE)BlockStream_Close; + +            // Supply the block functions +            pStream->BlockCheck    = (BLOCK_CHECK)LinearStream_BlockCheck; +            pStream->BlockRead     = (BLOCK_READ)LinearStream_BlockRead;          }      } -    return true; +    return pStream;  }  //----------------------------------------------------------------------------- @@ -1355,260 +1293,188 @@ static bool IsPartHeader(PPART_FILE_HEADER pPartHdr)      return false;  } -static bool PartialStream_Read( -    TPartialStream * pStream, -    ULONGLONG * pByteOffset, -    void * pvBuffer, -    DWORD dwBytesToRead) +static bool PartialStream_LoadBitmap(TBlockStream * pStream)  { -    ULONGLONG RawByteOffset; -    LPBYTE pbBuffer = (LPBYTE)pvBuffer; -    DWORD dwBytesRemaining = dwBytesToRead; -    DWORD dwPartOffset; -    DWORD dwPartIndex; -    DWORD dwBytesRead = 0; -    DWORD dwBlockSize = pStream->BlockSize; -    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); +    PPART_FILE_MAP_ENTRY FileBitmap; +    PART_FILE_HEADER PartHdr; +    ULONGLONG ByteOffset = 0; +    ULONGLONG StreamSize = 0; +    DWORD BlockCount; +    DWORD BitmapSize; -    // Read all data, one part at a time -    while(dwBytesRemaining != 0) +    // Only if the size is greater than size of the bitmap header +    if(pStream->Base.File.FileSize > sizeof(PART_FILE_HEADER))      { -        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) +        // Attempt to read PART file header +        if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER)))          { -            nFailReason = ERROR_FILE_CORRUPT; -            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); +            // We need to swap PART file header on big-endian platforms +            BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER)); -        // Get the number of bytes reamining in the current part -        dwBytesInPart = dwBlockSize - dwPartOffset; +            // Verify the PART file header +            if(IsPartHeader(&PartHdr)) +            { +                // Get the number of blocks and size of one block +                StreamSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); +                ByteOffset = sizeof(PART_FILE_HEADER); +                BlockCount = (DWORD)((StreamSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); +                BitmapSize = BlockCount * sizeof(PART_FILE_MAP_ENTRY); + +                // Check if sizes match +                if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize) +                { +                    // Allocate space for the array of PART_FILE_MAP_ENTRY +                    FileBitmap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); +                    if(FileBitmap != NULL) +                    { +                        // Load the block map +                        if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) +                        { +                            STORM_FREE(FileBitmap); +                            return false; +                        } -        // Compute the raw file offset of the file part -        RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo); -        if(RawByteOffset == 0) -        { -            nFailReason = ERROR_FILE_CORRUPT; -            break; -        } +                        // Make sure that the byte order is correct +                        BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize); -        // If the number of bytes in part is too big, cut it -        if(dwBytesInPart > dwBytesRemaining) -            dwBytesInPart = dwBytesRemaining; +                        // Update the stream size +                        pStream->StreamSize = StreamSize; -        // Append the offset within the part -        RawByteOffset += dwPartOffset; -        if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) -        { -            nFailReason = ERROR_FILE_CORRUPT; -            break; +                        // Fill the bitmap information +                        pStream->FileBitmap = FileBitmap; +                        pStream->BitmapSize = BitmapSize; +                        pStream->BlockSize  = PartHdr.BlockSize; +                        pStream->BlockCount = BlockCount; +                        pStream->IsComplete = 0; +                        return true; +                    } +                } +            }          } - -        // 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); +    return false;  } -static bool PartialStream_GetPos( -    TPartialStream * pStream, -    ULONGLONG & ByteOffset) +static bool PartialStream_BlockCheck( +    TBlockStream * pStream,                // Pointer to an open stream +    ULONGLONG BlockOffset)  { -    ByteOffset = pStream->VirtualPos; -    return true; -} +    PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap; +    DWORD BlockIndex; -static bool PartialStream_GetSize( -    TPartialStream * pStream,               // Pointer to an open stream -    ULONGLONG & FileSize)                   // Pointer where to store file size -{ -    FileSize = pStream->VirtualSize; -    return true; +    // Sanity checks +    assert((BlockOffset & (pStream->BlockSize - 1)) == 0); +    assert(FileBitmap != NULL); +     +    // Calculate the index of the block +    BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize); + +    // Check if the flags are present +    return (FileBitmap[BlockIndex].Flags & 0x03) ? true : false;  } -static bool PartialStream_GetBitmap( -    TPartialStream * pStream, -    TFileBitmap * pBitmap, -    DWORD Length, -    LPDWORD LengthNeeded) +static bool PartialStream_BlockRead( +    TBlockStream * pStream, +    ULONGLONG StartOffset, +    ULONGLONG EndOffset, +    LPBYTE BlockBuffer, +    DWORD BytesNeeded, +    bool bAvailable)  { -    LPBYTE pbBitmap; -    DWORD TotalLength; -    DWORD BitmapSize = 0; -    DWORD ByteOffset; -    DWORD BitMask; -    bool bResult = false; +    PPART_FILE_MAP_ENTRY FileBitmap; +    ULONGLONG ByteOffset; +    DWORD BytesToRead; +    DWORD BlockIndex = (DWORD)(StartOffset / pStream->BlockSize); -    // Do we have stream bitmap? -    BitmapSize = ((pStream->BlockCount - 1) / 8) + 1; +    // The starting offset must be aligned to size of the block +    assert(pStream->FileBitmap != NULL); +    assert((StartOffset & (pStream->BlockSize - 1)) == 0); +    assert(StartOffset < EndOffset); -    // Give the bitmap length -    TotalLength = sizeof(TFileBitmap) + BitmapSize; -    if(LengthNeeded != NULL) -        *LengthNeeded = TotalLength; +    // Get the file map entry +    FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex; -    // Do we have enough to fill at least the header? -    if(Length >= sizeof(TFileBitmap)) +    // If the blocks are not available, we need to load them from the master +    // and then save to the mirror +    if(bAvailable == false)      { -        // Fill the bitmap header -        pBitmap->StartOffset = 0; -        pBitmap->EndOffset   = pStream->VirtualSize; -        pBitmap->BitmapSize  = BitmapSize; -        pBitmap->BlockSize   = pStream->BlockSize; -        pBitmap->BlockCount  = pStream->BlockCount; -        pBitmap->IsComplete  = 1; -         -        // 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; +        // TODO: Support for downloading blocks +        return false;      } -     -    // Do we have enough space for supplying the bitmap? -    if(Length >= TotalLength) +    else      { -        // Fill the file bitmap -        pbBitmap = (LPBYTE)(pBitmap + 1); -        for(DWORD i = 0; i < pStream->BlockCount; i++) +        while(StartOffset < EndOffset)          { -            // Is the block there? -            if(pStream->PartMap[i].Flags == 3) -            { -                ByteOffset = i / 8; -                BitMask = 1 << (i & 7); -                pbBitmap[ByteOffset] |= BitMask; -            } +            // Get the number of bytes to be read +            BytesToRead = (DWORD)(EndOffset - StartOffset); +            if(BytesToRead > pStream->BlockSize) +                BytesToRead = pStream->BlockSize; +            if(BytesToRead > BytesNeeded) +                BytesToRead = BytesNeeded; + +            // Read the block +            ByteOffset = MAKE_OFFSET64(FileBitmap->BlockOffsHi, FileBitmap->BlockOffsLo); +            if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead)) +                return false; + +            // Move the pointers +            StartOffset += pStream->BlockSize; +            BlockBuffer += pStream->BlockSize; +            BytesNeeded -= pStream->BlockSize;          } -        bResult = true;      } -    return bResult; +    return true;  } -static void PartialStream_Close(TPartialStream * pStream) +static TFileStream * PartialStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)  { -    // Free the part map -    if(pStream->PartMap != NULL) -        STORM_FREE(pStream->PartMap); -    pStream->PartMap = NULL; - -    // Clear variables -    pStream->VirtualSize = 0; -    pStream->VirtualPos = 0; +    TBlockStream * pStream; -    // 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; +    // Create new empty stream +    pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); +    if(pStream == NULL) +        return NULL; -    // Sanity check -    assert(pStream->BaseRead != NULL); +    // Attempt to open the base stream. If this fails +    // and we have a master stream, we can create new mirror +    assert(pStream->BaseOpen != NULL); +    if(!pStream->BaseOpen(pStream, dwStreamFlags)) +    { +        // Do we have base create function and master stream? +//      if(!PartialStream_CreateMirror(pStream)) +        { +            FileStream_Close(pStream); +            SetLastError(ERROR_FILE_NOT_FOUND); +            return NULL; +        } +    } -    // Attempt to read PART file header -    if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) +    // Load the stream bitmap +    if(PartialStream_LoadBitmap(pStream))      { -        // We need to swap PART file header on big-endian platforms -        BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER)); +        // Set the stream position to zero. Stream size is already set +        assert(pStream->StreamSize != 0); +        pStream->StreamPos = 0; +        pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -        // 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 = (STREAM_GETTIME)LinearStream_GetTime; -                    pStream->StreamGetBmp  = (STREAM_GETBMP)PartialStream_GetBitmap; -                    pStream->StreamClose   = (STREAM_CLOSE)PartialStream_Close; -                    return true; -                } +        // Set new function pointers +        pStream->StreamRead    = (STREAM_READ)BlockStream_Read; +        pStream->StreamGetPos  = BlockStream_GetPos; +        pStream->StreamGetSize = BlockStream_GetSize; +        pStream->StreamClose   = (STREAM_CLOSE)BlockStream_Close; -                // Free the part map -                STORM_FREE(pStream->PartMap); -                pStream->PartMap = NULL; -            } -        } +        // Supply the block functions +        pStream->BlockCheck    = (BLOCK_CHECK)PartialStream_BlockCheck; +        pStream->BlockRead     = (BLOCK_READ)PartialStream_BlockRead; +        return pStream;      } +    // Cleanup the stream and return +    FileStream_Close(pStream);      SetLastError(ERROR_BAD_FORMAT); -    return false; +    return NULL;  }  //----------------------------------------------------------------------------- @@ -1806,32 +1672,79 @@ static void DecryptFileChunk(      }  } -static bool DetectFileKey(LPBYTE pbKeyBuffer, LPBYTE pbEncryptedHeader) +static bool EncryptedStream_DetectFileKey(TEncryptedStream * pStream)  {      ULONGLONG ByteOffset = 0; +    BYTE EncryptedHeader[MPQE_CHUNK_SIZE];      BYTE FileHeader[MPQE_CHUNK_SIZE]; -    // We just try all known keys one by one -    for(int i = 0; AuthCodeArray[i] != NULL; i++) +    // Read the first file chunk +    if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader)))      { -        // Prepare they decryption key from game serial number -        CreateKeyFromAuthCode(pbKeyBuffer, AuthCodeArray[i]); - -        // Try to decrypt with the given key  -        memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE); -        DecryptFileChunk((LPDWORD)FileHeader, pbKeyBuffer, 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 true; +        // We just try all known keys one by one +        for(int i = 0; AuthCodeArray[i] != NULL; i++) +        { +            // Prepare they decryption key from game serial number +            CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]); + +            // Try to decrypt with the given key  +            memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE); +            DecryptFileChunk((LPDWORD)FileHeader, pStream->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') +            { +                // Update the stream size +                pStream->StreamSize = pStream->Base.File.FileSize; + +                // Fill the block information +                pStream->BlockSize  = MPQE_CHUNK_SIZE; +                pStream->BlockCount = (DWORD)(pStream->Base.File.FileSize + MPQE_CHUNK_SIZE - 1) / MPQE_CHUNK_SIZE; +                pStream->IsComplete = 1; +                return true; +            } +        }      }      // Key not found, sorry      return false;  } +static bool EncryptedStream_BlockCheck(TEncryptedStream *, ULONGLONG) +{ +    return true; +} + +static bool EncryptedStream_BlockRead( +    TEncryptedStream * pStream, +    ULONGLONG StartOffset, +    ULONGLONG EndOffset, +    LPBYTE BlockBuffer, +    DWORD BytesNeeded, +    bool bAvailable) +{ +    DWORD dwBytesToRead; + +    assert((StartOffset & (pStream->BlockSize - 1)) == 0); +    assert(StartOffset < EndOffset); +    assert(bAvailable != false); +    BytesNeeded = BytesNeeded; + +    // Read the file from the stream as-is +    // Limit the reading to number of blocks really needed +    dwBytesToRead = (DWORD)(EndOffset - StartOffset); +    if(!pStream->BaseRead(pStream, &StartOffset, BlockBuffer, dwBytesToRead)) +        return false; + +    // Decrypt the data +    dwBytesToRead = (dwBytesToRead + MPQE_CHUNK_SIZE - 1) & ~(MPQE_CHUNK_SIZE - 1); +    DecryptFileChunk((LPDWORD)BlockBuffer, pStream->Key, StartOffset, dwBytesToRead); +    return true; +} + +/*  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 @@ -1848,7 +1761,7 @@ static bool EncryptedStream_Read(      bool bResult = false;      // Get the byte offset -    ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->FilePos; +    ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->StreamPos;      // Cut it down to MPQE chunk size      StartOffset = ByteOffset; @@ -1857,7 +1770,7 @@ static bool EncryptedStream_Read(      // Calculate number of bytes to decrypt      dwBytesToDecrypt = (DWORD)(EndOffset - StartOffset); -    dwBytesToAllocate = (dwBytesToDecrypt + (MPQE_CHUNK_SIZE - 1)) & ~(MPQE_CHUNK_SIZE - 1); +    dwBytesToAllocate = (dwBytesToDecrypt + MPQE_CHUNK_SIZE - 1) & ~(MPQE_CHUNK_SIZE - 1);      // Allocate buffers for encrypted and decrypted data      pbMpqData = STORM_ALLOC(BYTE, dwBytesToAllocate); @@ -1885,136 +1798,50 @@ static bool EncryptedStream_Read(          STORM_FREE(pbMpqData);      } -    // Free buffers and exit +    // Update stream position +    pStream->StreamPos = ByteOffset + dwBytesToRead;      return bResult;  } +*/ -static bool EncryptedStream_Open(TEncryptedStream * pStream) -{ -    ULONGLONG ByteOffset = 0; -    BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; - -    // 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 -        if(DetectFileKey(pStream->Key, EncryptedHeader)) -        { -            // Assign functions -            pStream->StreamRead    = (STREAM_READ)EncryptedStream_Read; -            pStream->StreamGetSize = (STREAM_GETSIZE)LinearStream_GetSize; -            pStream->StreamGetTime = (STREAM_GETTIME)LinearStream_GetTime; -            pStream->StreamGetPos  = (STREAM_GETPOS)LinearStream_GetPos; -            pStream->StreamGetBmp  = (STREAM_GETBMP)CompleteFile_GetBmp; -            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; -} - -//----------------------------------------------------------------------------- -// File stream allocation function - -/** - * This function allocates an empty structure for the file stream - * The stream structure is created as variable length, linear block of data - * The file name is placed after the end of the stream structure data - * - * \a szFileName    Name of the file - * \a dwStreamFlags Stream flags telling what kind of stream structure to create - */ - -static TFileStream * AllocateFileStream( -    const TCHAR * szFileName, -    DWORD dwStreamFlags) +static TFileStream * EncryptedStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)  { -    TFileStream * pStream; -    TCHAR * szSourceName; -    size_t FileNameSize = (_tcslen(szFileName) + 1) * sizeof(TCHAR); -    size_t StreamSize = 0; -    DWORD dwStreamProvider = dwStreamFlags & STREAM_PROVIDER_MASK; -    DWORD dwBaseProvider = dwStreamFlags & BASE_PROVIDER_MASK; +    TEncryptedStream * pStream; -    // The "file:" prefix forces the BASE_PROVIDER_FILE -    if(!_tcsicmp(szFileName, _T("file:"))) -    { -        dwBaseProvider = BASE_PROVIDER_FILE; -        szFileName += 5; -    } -     -    // The "map:" prefix forces the BASE_PROVIDER_MAP -    if(!_tcsicmp(szFileName, _T("map:"))) -    { -        dwBaseProvider = BASE_PROVIDER_MAP; -        szFileName += 4; -    } -     -    // The "http:" prefix forces the BASE_PROVIDER_HTTP -    if(!_tcsicmp(szFileName, _T("http:"))) -    { -        dwBaseProvider = BASE_PROVIDER_HTTP; -        szFileName += 5; -    } +    // Create new empty stream +    pStream = (TEncryptedStream *)AllocateFileStream(szFileName, sizeof(TEncryptedStream), dwStreamFlags); +    if(pStream == NULL) +        return NULL; -    // Re-create the stream flags -    dwStreamFlags = (dwStreamFlags & STREAM_FLAG_MASK) | dwStreamProvider | dwBaseProvider; +    // Attempt to open the base stream +    assert(pStream->BaseOpen != NULL); +    if(!pStream->BaseOpen(pStream, dwStreamFlags)) +        return NULL; -    // Allocate file stream for each stream provider -    switch(dwStreamFlags & STREAM_PROVIDER_MASK) +    // Determine the encryption key for the MPQ +    if(EncryptedStream_DetectFileKey(pStream))      { -        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; +        // Set the stream position and size +        assert(pStream->StreamSize != 0); +        pStream->StreamPos = 0; +        pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -        case STREAM_PROVIDER_ENCRYPTED: -            dwStreamFlags |= STREAM_FLAG_READ_ONLY; -            StreamSize = sizeof(TEncryptedStream); -            break; +        // Set new function pointers +        pStream->StreamRead    = (STREAM_READ)BlockStream_Read; +        pStream->StreamGetPos  = BlockStream_GetPos; +        pStream->StreamGetSize = BlockStream_GetSize; +        pStream->StreamClose   = pStream->BaseClose; -        default: -            return NULL; +        // Supply the block functions +        pStream->BlockCheck    = (BLOCK_CHECK)EncryptedStream_BlockCheck; +        pStream->BlockRead     = (BLOCK_READ)EncryptedStream_BlockRead; +        return pStream;      } -    // Allocate the stream structure for the given stream type -    pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize + FileNameSize); -    if(pStream == NULL) -    { -        SetLastError(ERROR_NOT_ENOUGH_MEMORY); -        return NULL; -    } - -    // Fill the stream structure with zeros -    memset(pStream, 0, StreamSize); - -    // Remember the file name -    pStream->szFileName = (TCHAR *)((BYTE *)pStream + StreamSize); -    pStream->dwFlags = dwStreamFlags; -    memcpy(pStream->szFileName, szFileName, FileNameSize); - -    // If we have source file name, setup it as well -    szSourceName = _tcschr(pStream->szFileName, _T('*')); -    if(szSourceName != NULL) -    { -        // Remember the source name and cut these two file names -        pStream->szSourceName = szSourceName + 1; -        szSourceName[0] = 0; -    } -    return pStream; +    // Cleanup the stream and return +    FileStream_Close(pStream); +    SetLastError(ERROR_UNKNOWN_FILE_KEY); +    return NULL;  }  //----------------------------------------------------------------------------- @@ -2044,14 +1871,14 @@ TFileStream * FileStream_CreateFile(      TFileStream * pStream;      // We only support creation of linear, local file -    if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) +    if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE))      {          SetLastError(ERROR_NOT_SUPPORTED);          return NULL;      }      // Allocate file stream structure for linear stream -    pStream = AllocateFileStream(szFileName, dwStreamFlags); +    pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);      if(pStream != NULL)      {          // Attempt to create the disk file @@ -2060,12 +1887,9 @@ TFileStream * FileStream_CreateFile(              // Fill the stream provider functions              pStream->StreamRead    = pStream->BaseRead;              pStream->StreamWrite   = pStream->BaseWrite; -            pStream->StreamSetSize = pStream->BaseSetSize; -            pStream->StreamGetSize = (STREAM_GETSIZE)LinearStream_GetSize; -            pStream->StreamGetTime = (STREAM_GETTIME)LinearStream_GetTime; -            pStream->StreamGetPos  = (STREAM_GETPOS)LinearStream_GetPos; -            pStream->StreamGetBmp  = (STREAM_GETBMP)CompleteFile_GetBmp; -            pStream->StreamSwitch  = (STREAM_SWITCH)LinearStream_Switch; +            pStream->StreamResize  = pStream->BaseResize; +            pStream->StreamGetSize = pStream->BaseGetSize; +            pStream->StreamGetPos  = pStream->BaseGetPos;              pStream->StreamClose   = pStream->BaseClose;              return pStream;          } @@ -2087,8 +1911,6 @@ TFileStream * FileStream_CreateFile(   * - 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, @@ -2102,83 +1924,46 @@ TFileStream * FileStream_OpenFile(      const TCHAR * szFileName,      DWORD dwStreamFlags)  { -    TFileStream * pStream = NULL; -    DWORD dwClearMask = STREAM_OPTIONS_MASK; -    bool bStreamResult = false; -    bool bBaseResult = false; - -    // Allocate the stream of the given type -    pStream = AllocateFileStream(szFileName, dwStreamFlags); -    if(pStream == NULL) -        return NULL; +    DWORD dwBaseProvider = dwStreamFlags & BASE_PROVIDER_MASK; -    // Few special checks when we want the stream to be cached from another source, -    if(pStream->szSourceName != NULL) +    // The "file:" prefix forces the BASE_PROVIDER_FILE +    if(!_tcsicmp(szFileName, _T("file:")))      { -        // We don't allow other base types than BASE_PROVIDER_FILE -        if((pStream->dwFlags & BASE_PROVIDER_MASK) != BASE_PROVIDER_FILE) -        { -            SetLastError(ERROR_INVALID_PARAMETER); -            STORM_FREE(pStream); -            return NULL; -        } - -        // Clear the STREAM_FLAG_READ_ONLY flag for the base file when being open -        // as local cache of a remote file -        dwClearMask &= ~STREAM_FLAG_READ_ONLY; +        dwBaseProvider = BASE_PROVIDER_FILE; +        szFileName += 5;      } - -    // Now initialize the respective base provider -    switch(pStream->dwFlags & BASE_PROVIDER_MASK) +     +    // The "map:" prefix forces the BASE_PROVIDER_MAP +    if(!_tcsicmp(szFileName, _T("map:")))      { -        case BASE_PROVIDER_FILE: -            bBaseResult = BaseFile_Open(pStream, pStream->dwFlags & dwClearMask); -            break; - -        case BASE_PROVIDER_MAP: -            pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -            bBaseResult = BaseMap_Open(pStream); -            break; - -        case BASE_PROVIDER_HTTP: -            pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -            bBaseResult = BaseHttp_Open(pStream); -            break; +        dwBaseProvider = BASE_PROVIDER_MAP; +        szFileName += 4;      } - -    // If we failed to open the base storage, fail the operation -    if(bBaseResult == false) +     +    // The "http:" prefix forces the BASE_PROVIDER_HTTP +    if(!_tcsicmp(szFileName, _T("http:")))      { -        STORM_FREE(pStream); -        return NULL; +        dwBaseProvider = BASE_PROVIDER_HTTP; +        szFileName += 5;      } -    // Now initialize the stream provider -    switch(pStream->dwFlags & STREAM_PROVIDER_MASK) +    // Re-assemble the stream flags +    dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | (dwStreamFlags & STREAM_PROVIDER_MASK) | dwBaseProvider; +    switch(dwStreamFlags & STREAM_PROVIDER_MASK)      {          case STREAM_PROVIDER_LINEAR: -            bStreamResult = LinearStream_Open((TLinearStream *)pStream); -            break; +            return LinearStream_Open(szFileName, dwStreamFlags);          case STREAM_PROVIDER_PARTIAL: -            bStreamResult = PartialStream_Open((TPartialStream *)pStream); -            break; +            return PartialStream_Open(szFileName, dwStreamFlags);          case STREAM_PROVIDER_ENCRYPTED: -            bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream); -            break; -    } +            return EncryptedStream_Open(szFileName, dwStreamFlags); -    // 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; +        default: +            SetLastError(ERROR_INVALID_PARAMETER); +            return NULL;      } - -    return pStream;  }  /** @@ -2221,24 +2006,16 @@ bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBu  bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)  {      if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED);          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 * pByteOffset) -{ -    assert(pStream->StreamGetPos != NULL); -    return pStream->StreamGetPos(pStream, pByteOffset); -} - -/**   * Returns the size of a file   *   * \a pStream Pointer to an open stream @@ -2259,10 +2036,24 @@ bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize)  bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)  {                                       if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED);          return false; +    } + +    assert(pStream->StreamResize != NULL); +    return pStream->StreamResize(pStream, NewFileSize); +} -    assert(pStream->StreamSetSize != NULL); -    return pStream->StreamSetSize(pStream, NewFileSize); +/** + * This function returns the current file position + * \a pStream + * \a pByteOffset + */ +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ +    assert(pStream->StreamGetPos != NULL); +    return pStream->StreamGetPos(pStream, pByteOffset);  }  /** @@ -2273,8 +2064,9 @@ bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)   */  bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)  { -    assert(pStream->StreamGetTime != NULL); -    return pStream->StreamGetTime(pStream, pFileTime); +    // Just use the saved filetime value +    *pFileTime = pStream->Base.File.FileTime; +    return true;  }  /** @@ -2298,15 +2090,39 @@ bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags)   * 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) + * \a pNewStream Temporary ("working") stream (created during archive compacting)   */ -bool FileStream_Switch(TFileStream * pStream, TFileStream * pNewStream) +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream)  { +    // Only supported on linear files +    if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) +    { +        SetLastError(ERROR_NOT_SUPPORTED); +        return false; +    } + +    // Not supported on read-only streams      if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED); +        return false; +    } + +    // Close both stream's base providers +    pNewStream->BaseClose(pNewStream); +    pStream->BaseClose(pStream); + +    // Now we have to delete the (now closed) old file and rename the new file +    if(!BaseFile_Replace(pStream, pNewStream)) +        return false; + +    // Now open the base file again +    if(BaseFile_Open(pStream, pStream->dwFlags))          return false; -    assert(pStream->StreamSwitch != NULL); -    return pStream->StreamSwitch(pStream, pNewStream); +    // Cleanup the new stream +    FileStream_Close(pNewStream); +    return true;  }  /** @@ -2331,23 +2147,6 @@ bool FileStream_IsReadOnly(TFileStream * pStream)  }  /** - * 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, void * pvBitmap, DWORD Length, LPDWORD LengthNeeded) -{ -    assert(pStream->StreamGetBmp != NULL); -    return pStream->StreamGetBmp(pStream, pvBitmap, 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 @@ -2360,10 +2159,18 @@ void FileStream_Close(TFileStream * pStream)      // Check if the stream structure is allocated at all      if(pStream != NULL)      { +        // Free the master stream, if any +        if(pStream->pMaster != NULL) +            FileStream_Close(pStream->pMaster); +        pStream->pMaster = NULL; +          // Close the stream provider. -        // This will also close the base stream -        assert(pStream->StreamClose != NULL); -        pStream->StreamClose(pStream); +        if(pStream->StreamClose != NULL) +            pStream->StreamClose(pStream); +         +        // Also close base stream, if any +        else if(pStream->BaseClose != NULL) +            pStream->BaseClose(pStream);          // Free the stream itself          STORM_FREE(pStream); diff --git a/src/FileStream.h b/src/FileStream.h index a44b48e..f660a87 100644 --- a/src/FileStream.h +++ b/src/FileStream.h @@ -14,6 +14,19 @@  //-----------------------------------------------------------------------------  // Function prototypes +typedef void (*STREAM_INIT)( +    struct TFileStream * pStream        // Pointer to an unopened stream +); + +typedef bool (*STREAM_CREATE)( +    struct TFileStream * pStream        // Pointer to an unopened stream +    ); + +typedef bool (*STREAM_OPEN)( +    struct TFileStream * pStream,       // Pointer to an unopened stream +    DWORD dwStreamFlags                 // Stream flags +    ); +  typedef bool (*STREAM_READ)(      struct TFileStream * pStream,       // Pointer to an open stream      ULONGLONG * pByteOffset,            // Pointer to file byte offset. If NULL, it reads from the current position @@ -28,7 +41,7 @@ typedef bool (*STREAM_WRITE)(      DWORD dwBytesToWrite                // Number of bytes to read from the file      ); -typedef bool (*STREAM_SETSIZE)( +typedef bool (*STREAM_RESIZE)(      struct TFileStream * pStream,       // Pointer to an open stream      ULONGLONG FileSize                  // New size for the file, in bytes      ); @@ -38,36 +51,34 @@ typedef bool (*STREAM_GETSIZE)(      ULONGLONG * pFileSize               // Receives the file size, in bytes      ); -typedef bool (*STREAM_GETTIME)( -    struct TFileStream * pStream, -    ULONGLONG * pFT -    ); -  typedef bool (*STREAM_GETPOS)(      struct TFileStream * pStream,       // Pointer to an open stream      ULONGLONG * pByteOffset             // Pointer to store current file position      ); -typedef bool (*STREAM_GETBMP)( -    TFileStream * pStream, -    void * pvBitmap, -    DWORD Length, -    LPDWORD LengthNeeded +typedef void (*STREAM_CLOSE)( +    struct TFileStream * pStream      ); -typedef bool (*STREAM_SWITCH)( -    struct TFileStream * pStream, -    struct TFileStream * pNewStream +typedef bool (*BLOCK_READ)( +    struct TFileStream * pStream,       // Pointer to an opened block stream +    ULONGLONG StartOffset,              // Byte offset of start of the block array +    ULONGLONG EndOffset,                // End offset (either end of the block or end of the file) +    LPBYTE BlockBuffer,                 // Pointer to block-aligned buffer +    DWORD BytesNeeded,                  // Number of bytes that are really needed +    bool bAvailable                     // true if the block is available      ); -typedef void (*STREAM_CLOSE)( -    struct TFileStream * pStream +typedef DWORD (*BLOCK_CHECK)( +    struct TFileStream * pStream, +    ULONGLONG BlockOffset      );  //-----------------------------------------------------------------------------  // Local structures - partial file structure and bitmap footer  #define ID_FILE_BITMAP_FOOTER   0x33767470  // Signature of the file bitmap footer ('ptv3') +#define DEFAULT_BLOCK_SIZE      0x00004000  // Default size of the stream block  typedef struct _PART_FILE_HEADER  { @@ -93,91 +104,94 @@ typedef struct _PART_FILE_MAP_ENTRY  typedef struct _FILE_BITMAP_FOOTER  { -    DWORD dwSignature;                      // 'ptv3' (MPQ_DATA_BITMAP_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) +    DWORD Signature;                      // 'ptv3' (ID_FILE_BITMAP_FOOTER) +    DWORD Version;                        // Unknown, seems to always have value of 3 (version?) +    DWORD BuildNumber;                    // Game build number for that MPQ +    DWORD MapOffsetLo;                    // Low 32-bits of the offset of the bit map +    DWORD MapOffsetHi;                    // High 32-bits of the offset of the bit map +    DWORD BlockSize;                      // Size of one block (usually 0x4000 bytes)  } FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER;  //----------------------------------------------------------------------------- -// Local structures +// Structure for file stream -union TBaseData +union TBaseProviderData  {      struct      { +        ULONGLONG FileSize;                 // Size of the file +        ULONGLONG FilePos;                  // Current file position +        ULONGLONG FileTime;                 // Last write time          HANDLE hFile;                       // File handle      } File;      struct      { +        ULONGLONG FileSize;                 // Size of the file +        ULONGLONG FilePos;                  // Current file position +        ULONGLONG FileTime;                 // Last write time          LPBYTE pbFile;                      // Pointer to mapped view      } Map;      struct      { +        ULONGLONG FileSize;                 // Size of the file +        ULONGLONG FilePos;                  // Current file position +        ULONGLONG FileTime;                 // Last write time          HANDLE hInternet;                   // Internet handle          HANDLE hConnect;                    // Connection to the internet server      } Http;  }; -//----------------------------------------------------------------------------- -// 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_SETSIZE StreamSetSize;           // Pointer to function changing file size +    STREAM_RESIZE  StreamResize;            // Pointer to function changing file size      STREAM_GETSIZE StreamGetSize;           // Pointer to function returning file size -    STREAM_GETTIME StreamGetTime;           // Pointer to function retrieving the file time      STREAM_GETPOS  StreamGetPos;            // Pointer to function that returns current file position -    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 +    // Block-oriented functions +    BLOCK_READ     BlockRead;               // Pointer to function reading one or more blocks +    BLOCK_CHECK    BlockCheck;              // Pointer to function checking whether the block is present +      // Base provider functions -    STREAM_READ    BaseRead; -    STREAM_WRITE   BaseWrite; -    STREAM_SETSIZE BaseSetSize;             // Pointer to function changing file size +    STREAM_CREATE  BaseCreate;              // Pointer to base create function +    STREAM_OPEN    BaseOpen;                // Pointer to base open function +    STREAM_READ    BaseRead;                // Read from the stream +    STREAM_WRITE   BaseWrite;               // Write to the stream +    STREAM_RESIZE  BaseResize;              // Pointer to function changing file size +    STREAM_GETSIZE BaseGetSize;             // Pointer to function returning file size +    STREAM_GETPOS  BaseGetPos;              // Pointer to function that returns current file position      STREAM_CLOSE   BaseClose;               // Pointer to function closing the stream -    ULONGLONG FileSize;                     // Size of the file -    ULONGLONG FilePos;                      // Current file position -    ULONGLONG FileTime;                     // Date/time of last modification of the file -    TCHAR * szSourceName;                   // Name of the source file (might be HTTP file server or local file) +    // Base provider data (file size, file position) +    TBaseProviderData Base; + +    // Stream provider data +    TFileStream * pMaster;                  // Master stream (e.g. MPQ on a web server)      TCHAR * szFileName;                     // File name (self-relative pointer) -    DWORD dwFlags;                          // Stream flags -    TBaseData Base;                         // Base provider data +    ULONGLONG StreamSize;                   // Stream size (can be less than file size) +    ULONGLONG StreamPos;                    // Stream position +    DWORD dwFlags;                          // Stream flags      // Followed by stream provider data, with variable length  };  //----------------------------------------------------------------------------- -// Structures for linear stream +// Structures for block-oriented stream -struct TLinearStream : public TFileStream +struct TBlockStream : public TFileStream  { -    TFileStream * pMaster;                  // Master file for loading missing data blocks -    TFileBitmap * pBitmap;                  // Pointer to the linear 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 +    void * FileBitmap;                      // Array of bits for file blocks +    DWORD BitmapSize;                       // Size of the file bitmap (in bytes) +    DWORD BlockSize;                        // Size of one block, in bytes +    DWORD BlockCount;                       // Number of data blocks in the file +    DWORD IsComplete;                       // If nonzero, no blocks are missing  };  //----------------------------------------------------------------------------- @@ -185,7 +199,7 @@ struct TPartialStream : public TFileStream  #define MPQE_CHUNK_SIZE 0x40                // Size of one chunk to be decrypted -struct TEncryptedStream : public TFileStream +struct TEncryptedStream : public TBlockStream  {      BYTE Key[MPQE_CHUNK_SIZE];              // File key  }; diff --git a/src/SFileCompactArchive.cpp b/src/SFileCompactArchive.cpp index 5ca4065..b5482c8 100644 --- a/src/SFileCompactArchive.cpp +++ b/src/SFileCompactArchive.cpp @@ -575,7 +575,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR      // If succeeded, switch the streams      if(nError == ERROR_SUCCESS)      { -        if(FileStream_Switch(ha->pStream, pTempStream)) +        if(FileStream_Replace(ha->pStream, pTempStream))              pTempStream = NULL;          else              nError = ERROR_CAN_NOT_COMPLETE; diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 31ee594..e56e2f6 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -163,10 +163,20 @@ bool WINAPI SFileGetFileInfo(              }              break; -        case SFileMpqFileBitmap: +        case SFileMpqStreamBlockSize:              ha = IsValidMpqHandle(hMpqOrFile);              if(ha != NULL) -                return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); +            { +                // TODO +            } +            break; + +        case SFileMpqStreamBlockAvailable: +            ha = IsValidMpqHandle(hMpqOrFile); +            if(ha != NULL) +            { +                // TODO +            }              break;          case SFileMpqUserDataOffset: diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index e5dcf03..9970791 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -161,8 +161,13 @@ bool WINAPI SFileOpenArchive(      // Open the MPQ archive file      if(nError == ERROR_SUCCESS)      { +        DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK); + +        // If not forcing MPQ v 1.0, also use file bitmap +        dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP; +          // Initialize the stream -        pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK) | STREAM_FLAG_USE_BITMAP); +        pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);          if(pStream == NULL)              nError = GetLastError();      } diff --git a/src/StormLib.h b/src/StormLib.h index fcc1925..e528283 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -272,8 +272,10 @@ extern "C" {  #define STREAM_FLAG_READ_ONLY       0x00000100  // Stream is read only  #define STREAM_FLAG_WRITE_SHARE     0x00000200  // Allow write sharing when open for write  #define STREAM_FLAG_USE_BITMAP      0x00000400  // If the file has a file bitmap, load it and use it -#define STREAM_FLAG_MASK            0x0000FF00  // Mask for stream flags -#define STREAM_OPTIONS_MASK         0x0000FFFF  // Mask for all stream options +#define STREAM_OPTIONS_MASK         0x0000FF00  // Mask for stream options + +#define STREAM_PROVIDERS_MASK       0x000000FF  // Mask to get stream providers +#define STREAM_FLAGS_MASK           0x0000FFFF  // Mask for all stream flags (providers+options)  #define MPQ_OPEN_NO_LISTFILE        0x00010000  // Don't load the internal listfile  #define MPQ_OPEN_NO_ATTRIBUTES      0x00020000  // Don't open the attributes @@ -361,7 +363,8 @@ typedef enum _SFileInfoClass  {      // Info classes for archives      SFileMpqFileName,                       // Name of the archive file (TCHAR []) -    SFileMpqFileBitmap,                     // Bitmap of the archive (TFileBitmap + BYTE[]) +    SFileMpqStreamBlockSize,                // Size of one stream block in bytes (DWORD) +    SFileMpqStreamBlockAvailable,           // Nonzero if the stream block at the given offset is available (input: ByteOffset, ULONGLONG)      SFileMpqUserDataOffset,                 // Offset of the user data header (ULONGLONG)      SFileMpqUserDataHeader,                 // Raw (unfixed) user data header (TMPQUserData)      SFileMpqUserData,                       // MPQ USer data, without the header (BYTE []) @@ -695,19 +698,6 @@ typedef struct _TPatchHeader  #define SIZE_OF_XFRM_HEADER  0x0C -// Structure for file bitmap. Used by SFileGetFileInfo(SFileMpqFileBitmap) -typedef struct _TFileBitmap -{ -    ULONGLONG StartOffset;                      // Starting offset of the file, covered by bitmap -    ULONGLONG EndOffset;                        // Ending offset of the file, covered by bitmap -    DWORD BitmapSize;                           // Size of the file bitmap (in bytes) -    DWORD BlockSize;                            // Size of one block, in bytes -    DWORD BlockCount;                           // Number of data blocks in the file -    DWORD IsComplete;                           // If nonzero, no blocks are missing - -    // Followed by file bitmap (variable length), array of BYTEs) -} TFileBitmap; -  // This is the combined file entry for maintaining file list in the MPQ.  // This structure is combined from block table, hi-block table,  // (attributes) file and from (listfile). @@ -949,14 +939,12 @@ const TCHAR * FileStream_GetFileName(TFileStream * pStream);  bool FileStream_IsReadOnly(TFileStream * pStream);  bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);  bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); -bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); -bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset); -bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize);  bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset);  bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT);  bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags); -bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream); -bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD Length, LPDWORD LengthNeeded); +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream);  void FileStream_Close(TFileStream * pStream);  //-----------------------------------------------------------------------------  | 
