diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/FileStream.cpp | 801 | ||||
| -rw-r--r-- | src/FileStream.h | 18 | ||||
| -rw-r--r-- | src/SBaseFileTable.cpp | 95 | ||||
| -rw-r--r-- | src/SFileAddFile.cpp | 2 | ||||
| -rw-r--r-- | src/SFileAttributes.cpp | 45 | ||||
| -rw-r--r-- | src/SFileCompactArchive.cpp | 2 | ||||
| -rw-r--r-- | src/SFileCreateArchive.cpp | 6 | ||||
| -rw-r--r-- | src/SFileExtractFile.cpp | 2 | ||||
| -rw-r--r-- | src/SFileGetFileInfo.cpp | 18 | ||||
| -rw-r--r-- | src/SFileOpenArchive.cpp | 31 | ||||
| -rw-r--r-- | src/SFileOpenFileEx.cpp | 2 | ||||
| -rw-r--r-- | src/SFilePatchArchives.cpp | 2 | ||||
| -rw-r--r-- | src/StormCommon.h | 2 | ||||
| -rw-r--r-- | src/StormLib.h | 38 | 
14 files changed, 782 insertions, 282 deletions
diff --git a/src/FileStream.cpp b/src/FileStream.cpp index 58543e3..c196f11 100644 --- a/src/FileStream.cpp +++ b/src/FileStream.cpp @@ -97,7 +97,7 @@ static bool BaseFile_Create(TFileStream * pStream)      return true;  } -static bool BaseFile_Open(TFileStream * pStream, DWORD dwStreamFlags) +static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)  {  #ifdef PLATFORM_WINDOWS      { @@ -106,7 +106,7 @@ static bool BaseFile_Open(TFileStream * pStream, DWORD dwStreamFlags)          DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0;          // Open the file -        pStream->Base.File.hFile = CreateFile(pStream->szFileName, +        pStream->Base.File.hFile = CreateFile(szFileName,                                                FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess,                                                FILE_SHARE_READ | dwWriteShare,                                                NULL, @@ -417,7 +417,7 @@ static void BaseFile_Init(TFileStream * pStream)  //-----------------------------------------------------------------------------  // Local functions - base memory-mapped file support -static bool BaseMap_Open(TFileStream * pStream, DWORD dwStreamFlags) +static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)  {  #ifdef PLATFORM_WINDOWS @@ -430,7 +430,7 @@ static bool BaseMap_Open(TFileStream * pStream, DWORD dwStreamFlags)      dwStreamFlags = dwStreamFlags;      // Open the file for read access -    hFile = CreateFile(pStream->szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); +    hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);      if(hFile != NULL)      {          // Retrieve file size. Don't allow mapping file of a zero size. @@ -588,11 +588,10 @@ static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR      return szFileName;  } -static bool BaseHttp_Open(TFileStream * pStream, DWORD dwStreamFlags) +static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)  {  #ifdef PLATFORM_WINDOWS -    const TCHAR * szFileName;      HINTERNET hRequest;      DWORD dwTemp = 0;      bool bFileAvailable = false; @@ -624,7 +623,7 @@ static bool BaseHttp_Open(TFileStream * pStream, DWORD dwStreamFlags)          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); +        szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);          pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,                                                        szServerName,                                                        INTERNET_DEFAULT_HTTP_PORT, @@ -821,11 +820,11 @@ static bool BlockStream_Read(      DWORD BlockSize = pStream->BlockSize;      DWORD BlockCount;      bool bPrevBlockAvailable; +    bool bCallbackCalled = false;      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 @@ -837,7 +836,7 @@ static bool BlockStream_Read(      EndOffset = ByteOffset + dwBytesToRead;      if(EndOffset > pStream->StreamSize)      { -        SetLastError(ERROR_CAN_NOT_COMPLETE); +        SetLastError(ERROR_HANDLE_EOF);          return false;      } @@ -863,6 +862,7 @@ static bool BlockStream_Read(      {          // Now parse the blocks and send the block read request          // to all blocks with the same availability +        assert(pStream->BlockCheck != NULL);          bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset);          // Loop as long as we have something to read @@ -874,6 +874,13 @@ static bool BlockStream_Read(              // If the availability has changed, read all blocks up to this one              if(bBlockAvailable != bPrevBlockAvailable)              { +                // Call the file stream callback, if the block is not available +                if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) +                { +                    pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); +                    bCallbackCalled = true; +                } +                  // Load the continuous blocks with the same availability                  assert(BlockOffset > BlockOffset0);                  bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); @@ -894,6 +901,13 @@ static bool BlockStream_Read(          // If there is a block(s) remaining to be read, do it          if(BlockOffset > BlockOffset0)          { +            // Call the file stream callback, if the block is not available +            if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) +            { +                pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); +                bCallbackCalled = true; +            } +              // Read the complete blocks from the file              if(BlockOffset > pStream->StreamSize)                  BlockOffset = pStream->StreamSize; @@ -914,6 +928,15 @@ static bool BlockStream_Read(          memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead);          pStream->StreamPos = ByteOffset + dwBytesToRead;      } +    else +    { +        // If the block read failed, set the last error +        SetLastError(ERROR_FILE_INCOMPLETE); +    } + +    // Call the callback to indicate we are done +    if(bCallbackCalled) +        pStream->pfnCallback(pStream->UserData, 0, 0);      // Free the block buffer and return      STORM_FREE(TransferBuffer); @@ -955,7 +978,7 @@ static STREAM_INIT StreamBaseInit[4] =  };  // This function allocates an empty structure for the file stream -// The stream structure is created as variable length, linear block of data +// The stream structure is created as flat block, variable length  // The file name is placed after the end of the stream structure data  static TFileStream * AllocateFileStream(      const TCHAR * szFileName, @@ -1015,9 +1038,9 @@ static TFileStream * AllocateFileStream(  }  //----------------------------------------------------------------------------- -// Local functions - linear stream support +// Local functions - flat stream support -static DWORD LinearStream_CheckFile(TBlockStream * pStream) +static DWORD FlatStream_CheckFile(TBlockStream * pStream)  {      LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap;      DWORD WholeByteCount = (pStream->BlockCount / 8); @@ -1043,7 +1066,7 @@ static DWORD LinearStream_CheckFile(TBlockStream * pStream)      return 1;  } -static bool LinearStream_LoadBitmap(TBlockStream * pStream) +static bool FlatStream_LoadBitmap(TBlockStream * pStream)  {      FILE_BITMAP_FOOTER Footer;      ULONGLONG ByteOffset;  @@ -1060,7 +1083,7 @@ static bool LinearStream_LoadBitmap(TBlockStream * pStream)      {          // Load the bitmap footer          ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER); -        if(pStream->StreamRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER))) +        if(pStream->BaseRead(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)); @@ -1076,7 +1099,7 @@ static bool LinearStream_LoadBitmap(TBlockStream * pStream)                  // Check if the sizes match                  if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize)                  { -                    // Allocate space for the linear bitmap +                    // Allocate space for the bitmap                      FileBitmap = STORM_ALLOC(BYTE, BitmapSize);                      if(FileBitmap != NULL)                      { @@ -1095,7 +1118,7 @@ static bool LinearStream_LoadBitmap(TBlockStream * pStream)                          pStream->BitmapSize = BitmapSize;                          pStream->BlockSize  = Footer.BlockSize;                          pStream->BlockCount = BlockCount; -                        pStream->IsComplete = LinearStream_CheckFile(pStream); +                        pStream->IsComplete = FlatStream_CheckFile(pStream);                          return true;                      }                  } @@ -1106,7 +1129,7 @@ static bool LinearStream_LoadBitmap(TBlockStream * pStream)      return false;  } -static void LinearStream_UpdateBitmap( +static void FlatStream_UpdateBitmap(      TBlockStream * pStream,                // Pointer to an open stream      ULONGLONG StartOffset,      ULONGLONG EndOffset) @@ -1137,9 +1160,12 @@ static void LinearStream_UpdateBitmap(          ByteIndex += (BitMask >> 0x07);          BitMask = (BitMask >> 0x07) | (BitMask << 0x01);      } + +    // Increment the bitmap update count +    pStream->IsModified = 1;  } -static bool LinearStream_BlockCheck( +static bool FlatStream_BlockCheck(      TBlockStream * pStream,                // Pointer to an open stream      ULONGLONG BlockOffset)  { @@ -1159,7 +1185,7 @@ static bool LinearStream_BlockCheck(      return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false;  } -static bool LinearStream_BlockRead( +static bool FlatStream_BlockRead(      TBlockStream * pStream,                // Pointer to an open stream      ULONGLONG StartOffset,      ULONGLONG EndOffset, @@ -1170,7 +1196,7 @@ static bool LinearStream_BlockRead(      DWORD BytesToRead = (DWORD)(EndOffset - StartOffset);      // The starting offset must be aligned to size of the block -    assert(pStream->FileBitmap != NULL && pStream->IsComplete == 0); +    assert(pStream->FileBitmap != NULL);      assert((StartOffset & (pStream->BlockSize - 1)) == 0);      assert(StartOffset < EndOffset); @@ -1190,8 +1216,8 @@ static bool LinearStream_BlockRead(          // 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); +        if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead)) +            FlatStream_UpdateBitmap(pStream, StartOffset, EndOffset);          return true;      } @@ -1203,16 +1229,107 @@ static bool LinearStream_BlockRead(      }  } -static bool LinearStream_CreateMirror(TBlockStream * pStream) +static void FlatStream_Close(TBlockStream * pStream) +{ +    FILE_BITMAP_FOOTER Footer; + +    if(pStream->FileBitmap && pStream->IsModified) +    { +        // Write the file bitmap +        pStream->BaseWrite(pStream, &pStream->StreamSize, pStream->FileBitmap, pStream->BitmapSize); +         +        // Prepare and write the file footer +        Footer.Signature   = ID_FILE_BITMAP_FOOTER; +        Footer.Version     = 3; +        Footer.BuildNumber = 10958;     // BUGBUG: What build number shall we set??? +        Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); +        Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20); +        Footer.BlockSize   = pStream->BlockSize; +        BSWAP_ARRAY32_UNSIGNED(&Footer, sizeof(FILE_BITMAP_FOOTER)); +        pStream->BaseWrite(pStream, NULL, &Footer, sizeof(FILE_BITMAP_FOOTER)); +    } + +    // Close the base class +    BlockStream_Close(pStream); +} + +static bool FlatStream_CreateMirror(TBlockStream * pStream)  { +    ULONGLONG MasterSize = 0; +    ULONGLONG MirrorSize = 0; +    LPBYTE FileBitmap = NULL; +    DWORD dwBitmapSize; +    DWORD dwBlockCount; +    bool bNeedCreateMirrorStream = true; +    bool bNeedResizeMirrorStream = true; +      // Do we have master function and base creation function?      if(pStream->pMaster == NULL || pStream->BaseCreate == NULL)          return false; -    return false; +    // Retrieve the master file size, block count and bitmap size +    FileStream_GetSize(pStream->pMaster, &MasterSize); +    dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); +    dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8); + +    // Setup stream size and position +    pStream->StreamSize = MasterSize; +    pStream->StreamPos = 0; + +    // Open the base stream for write access +    if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) +    { +        // If the file open succeeded, check if the file size matches required size +        pStream->BaseGetSize(pStream, &MirrorSize); +        if(MirrorSize == MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER)) +        { +            // Attempt to load an existing file bitmap +            if(FlatStream_LoadBitmap(pStream)) +                return true; + +            // We need to create new file bitmap +            bNeedResizeMirrorStream = false; +        } + +        // We need to create mirror stream +        bNeedCreateMirrorStream = false; +    } + +    // Create a new stream, if needed +    if(bNeedCreateMirrorStream) +    { +        if(!pStream->BaseCreate(pStream)) +            return false; +    } + +    // If we need to, then resize the mirror stream +    if(bNeedResizeMirrorStream) +    { +        if(!pStream->BaseResize(pStream, MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER))) +            return false; +    } + +    // Allocate the bitmap array +    FileBitmap = STORM_ALLOC(BYTE, dwBitmapSize); +    if(FileBitmap == NULL) +        return false; + +    // Initialize the bitmap +    memset(FileBitmap, 0, dwBitmapSize); +    pStream->FileBitmap = FileBitmap; +    pStream->BitmapSize = dwBitmapSize; +    pStream->BlockSize  = DEFAULT_BLOCK_SIZE; +    pStream->BlockCount = dwBlockCount; +    pStream->IsComplete = 0; +    pStream->IsModified = 1; + +    // Note: Don't write the stream bitmap right away. +    // Doing so would cause sparse file resize on NTFS, +    // which would take long time on larger files. +    return true;  } -static TFileStream * LinearStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)  {      TBlockStream * pStream;     @@ -1221,53 +1338,63 @@ static TFileStream * LinearStream_Open(const TCHAR * szFileName, DWORD dwStreamF      if(pStream == NULL)          return 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 a master stream? +    if(pStream->pMaster != NULL)      { -        // Do we have base create function and master stream? -//      if(!LinearStream_CreateMirror(pStream)) +        if(!FlatStream_CreateMirror(pStream))          {              FileStream_Close(pStream);              SetLastError(ERROR_FILE_NOT_FOUND);              return NULL;          }      } +    else +    { +        // Attempt to open the base stream +        if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) +            return false; -    // Set the stream function as if this was linear file without bitmap -    pStream->StreamRead    = pStream->BaseRead; -    pStream->StreamWrite   = pStream->BaseWrite; -    pStream->StreamResize  = pStream->BaseResize; -    pStream->StreamGetSize = pStream->BaseGetSize; -    pStream->StreamGetPos  = pStream->BaseGetPos; -    pStream->StreamClose   = pStream->BaseClose; +        // Load the bitmap, if required to +        if(dwStreamFlags & STREAM_FLAG_USE_BITMAP) +            FlatStream_LoadBitmap(pStream); -    // Setup the stream size -    pStream->StreamSize = pStream->Base.File.FileSize; +        // Setup the stream size +        if(pStream->FileBitmap == NULL) +            pStream->StreamSize = pStream->Base.File.FileSize; +        pStream->StreamPos = 0; +    } -    // 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)) +    // If we have a stream bitmap, set the reading functions +    // which check presence of each file block +    if(pStream->FileBitmap != NULL)      {          // Set the stream position to zero. Stream size is already set          assert(pStream->StreamSize != 0);          pStream->StreamPos = 0;          pStream->dwFlags |= STREAM_FLAG_READ_ONLY; -        // Set the reading function -        if(pStream->IsComplete == 0) -        { -            // 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; -        } +        // Supply the stream functions +        pStream->StreamRead    = (STREAM_READ)BlockStream_Read; +        pStream->StreamGetSize = BlockStream_GetSize; +        pStream->StreamGetPos  = BlockStream_GetPos; +        pStream->StreamClose   = (STREAM_CLOSE)FlatStream_Close; + +        // Supply the block functions +        pStream->BlockCheck    = (BLOCK_CHECK)FlatStream_BlockCheck; +        pStream->BlockRead     = (BLOCK_READ)FlatStream_BlockRead; +    } +    else +    { +        // Reset the file position to zero +        pStream->Base.File.FilePos = 0; + +        // Set the base functions +        pStream->StreamRead    = pStream->BaseRead; +        pStream->StreamWrite   = pStream->BaseWrite; +        pStream->StreamResize  = pStream->BaseResize; +        pStream->StreamGetSize = pStream->BaseGetSize; +        pStream->StreamGetPos  = pStream->BaseGetPos; +        pStream->StreamClose   = pStream->BaseClose;      }      return pStream; @@ -1440,7 +1567,7 @@ static TFileStream * PartialStream_Open(const TCHAR * szFileName, DWORD dwStream      // 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)) +    if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))      {          // Do we have base create function and master stream?  //      if(!PartialStream_CreateMirror(pStream)) @@ -1478,7 +1605,7 @@ static TFileStream * PartialStream_Open(const TCHAR * szFileName, DWORD dwStream  }  //----------------------------------------------------------------------------- -// Local functions - encrypted stream support +// Local functions - MPQE stream support  static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; @@ -1672,7 +1799,7 @@ static void DecryptFileChunk(      }  } -static bool EncryptedStream_DetectFileKey(TEncryptedStream * pStream) +static bool MpqeStream_DetectFileKey(TEncryptedStream * pStream)  {      ULONGLONG ByteOffset = 0;      BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; @@ -1712,12 +1839,7 @@ static bool EncryptedStream_DetectFileKey(TEncryptedStream * pStream)      return false;  } -static bool EncryptedStream_BlockCheck(TEncryptedStream *, ULONGLONG) -{ -    return true; -} - -static bool EncryptedStream_BlockRead( +static bool MpqeStream_BlockRead(      TEncryptedStream * pStream,      ULONGLONG StartOffset,      ULONGLONG EndOffset, @@ -1731,6 +1853,7 @@ static bool EncryptedStream_BlockRead(      assert(StartOffset < EndOffset);      assert(bAvailable != false);      BytesNeeded = BytesNeeded; +    bAvailable = bAvailable;      // Read the file from the stream as-is      // Limit the reading to number of blocks really needed @@ -1744,67 +1867,7 @@ static bool EncryptedStream_BlockRead(      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 -    void * pvBuffer,                        // Pointer to data to be read -    DWORD dwBytesToRead)                    // Number of bytes to read from the file -{ -    ULONGLONG StartOffset;                  // Offset of the first byte to be read from the file -    ULONGLONG ByteOffset;                   // Offset that the caller wants -    ULONGLONG EndOffset;                    // End offset that is to be read from the file -    DWORD dwBytesToAllocate; -    DWORD dwBytesToDecrypt; -    DWORD dwOffsetInCache; -    LPBYTE pbMpqData = NULL; -    bool bResult = false; - -    // Get the byte offset -    ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->StreamPos; - -    // Cut it down to MPQE chunk size -    StartOffset = ByteOffset; -    StartOffset = StartOffset & ~(MPQE_CHUNK_SIZE - 1); -    EndOffset = ByteOffset + dwBytesToRead; - -    // Calculate number of bytes to decrypt -    dwBytesToDecrypt = (DWORD)(EndOffset - StartOffset); -    dwBytesToAllocate = (dwBytesToDecrypt + MPQE_CHUNK_SIZE - 1) & ~(MPQE_CHUNK_SIZE - 1); - -    // Allocate buffers for encrypted and decrypted data -    pbMpqData = STORM_ALLOC(BYTE, dwBytesToAllocate); -    if(pbMpqData) -    { -        // Get the offset of the desired data in the cache -        dwOffsetInCache = (DWORD)(ByteOffset - StartOffset); - -        // Read the file from the stream as-is -        if(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) -        { -            // Decrypt the data -            DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate); - -            // Copy the decrypted data -            memcpy(pvBuffer, pbMpqData + dwOffsetInCache, dwBytesToRead); -            bResult = true; -        } -        else -        { -            assert(false); -        } - -        // Free decryption buffer         -        STORM_FREE(pbMpqData); -    } - -    // Update stream position -    pStream->StreamPos = ByteOffset + dwBytesToRead; -    return bResult; -} -*/ - -static TFileStream * EncryptedStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +static TFileStream * MpqeStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)  {      TEncryptedStream * pStream; @@ -1815,11 +1878,11 @@ static TFileStream * EncryptedStream_Open(const TCHAR * szFileName, DWORD dwStre      // Attempt to open the base stream      assert(pStream->BaseOpen != NULL); -    if(!pStream->BaseOpen(pStream, dwStreamFlags)) +    if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))          return NULL;      // Determine the encryption key for the MPQ -    if(EncryptedStream_DetectFileKey(pStream)) +    if(MpqeStream_DetectFileKey(pStream))      {          // Set the stream position and size          assert(pStream->StreamSize != 0); @@ -1833,8 +1896,7 @@ static TFileStream * EncryptedStream_Open(const TCHAR * szFileName, DWORD dwStre          pStream->StreamClose   = pStream->BaseClose;          // Supply the block functions -        pStream->BlockCheck    = (BLOCK_CHECK)EncryptedStream_BlockCheck; -        pStream->BlockRead     = (BLOCK_READ)EncryptedStream_BlockRead; +        pStream->BlockRead     = (BLOCK_READ)MpqeStream_BlockRead;          return pStream;      } @@ -1845,6 +1907,203 @@ static TFileStream * EncryptedStream_Open(const TCHAR * szFileName, DWORD dwStre  }  //----------------------------------------------------------------------------- +// Local functions - Block4 stream support + +#define BLOCK4_BLOCK_SIZE   0x4000          // Size of one block +#define BLOCK4_HASH_SIZE    0x20            // Size of MD5 hash that is after each block +#define BLOCK4_MAX_BLOCKS   0x00002000      // Maximum amount of blocks per file +#define BLOCK4_MAX_FSIZE    0x08040000      // Max size of one file + +static bool Block4Stream_BlockRead( +    TBlockStream * pStream,                // Pointer to an open stream +    ULONGLONG StartOffset, +    ULONGLONG EndOffset, +    LPBYTE BlockBuffer, +    DWORD BytesNeeded, +    bool bAvailable) +{ +    TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; +    ULONGLONG ByteOffset; +    DWORD BytesToRead; +    DWORD StreamIndex; +    DWORD BlockIndex; + +    // The starting offset must be aligned to size of the block +    assert(pStream->FileBitmap != NULL); +    assert((StartOffset & (pStream->BlockSize - 1)) == 0); +    assert(StartOffset < EndOffset); +    assert(bAvailable == true); + +    while(BytesNeeded != 0) +    { +        // Calculate the block index and the file index +        StreamIndex = (DWORD)((StartOffset / pStream->BlockSize) / BLOCK4_MAX_BLOCKS); +        BlockIndex  = (DWORD)((StartOffset / pStream->BlockSize) % BLOCK4_MAX_BLOCKS); +        if(StreamIndex > pStream->BitmapSize) +            return false; + +        // Prepare the appropriate stream to the base variables +        memcpy(&pStream->Base, BaseArray + StreamIndex, sizeof(TBaseProviderData)); + +        // Calculate the block offset +        ByteOffset = ((ULONGLONG)BlockIndex * (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE)); +        BytesToRead = min(BytesNeeded, BLOCK4_BLOCK_SIZE); + +        // Read from the base stream +        if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead)) +            return false; + +        // Move pointers +        StartOffset += BytesToRead; +        BlockBuffer += BytesToRead; +        BytesNeeded -= BytesToRead; +    } + +    return true; +} + + +static void Block4Stream_Close(TBlockStream * pStream) +{ +    TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; + +    // If we have a non-zero count of base streams, +    // we have to close them all +    if(BaseArray != NULL) +    { +        // Close all base streams +        for(DWORD i = 0; i < pStream->BitmapSize; i++) +        { +            memcpy(&pStream->Base, BaseArray + i, sizeof(TBaseProviderData)); +            pStream->BaseClose(pStream); +        } +    } + +    // Free the data map, if any +    if(pStream->FileBitmap != NULL) +        STORM_FREE(pStream->FileBitmap); +    pStream->FileBitmap = NULL; + +    // Do not call the BaseClose function, +    // we closed all handles already +    return; +} + +static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ +    TBaseProviderData * NewBaseArray = NULL; +    ULONGLONG RemainderBlock; +    ULONGLONG BlockCount; +    ULONGLONG FileSize; +    TBlockStream * pStream; +    TCHAR * szNameBuff; +    size_t nNameLength; +    DWORD dwBaseFiles = 0; +    DWORD dwBaseFlags; + +    // Create new empty stream +    pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); +    if(pStream == NULL) +        return NULL; + +    // Sanity check +    assert(pStream->BaseOpen != NULL); + +    // Get the length of the file name without numeric suffix +    nNameLength = _tcslen(pStream->szFileName); +    if(pStream->szFileName[nNameLength - 2] == '.' && pStream->szFileName[nNameLength - 1] == '0') +        nNameLength -= 2; +    pStream->szFileName[nNameLength] = 0; + +    // Supply the stream functions +    pStream->StreamRead    = (STREAM_READ)BlockStream_Read; +    pStream->StreamGetSize = BlockStream_GetSize; +    pStream->StreamGetPos  = BlockStream_GetPos; +    pStream->StreamClose   = (STREAM_CLOSE)Block4Stream_Close; +    pStream->BlockRead     = (BLOCK_READ)Block4Stream_BlockRead; + +    // Allocate work space for numeric names +    szNameBuff = STORM_ALLOC(TCHAR, nNameLength + 4); +    if(szNameBuff != NULL) +    { +        // Set the base flags +        dwBaseFlags = (dwStreamFlags & STREAM_PROVIDERS_MASK) | STREAM_FLAG_READ_ONLY; + +        // Go all suffixes from 0 to 30 +        for(int nSuffix = 0; nSuffix < 30; nSuffix++) +        { +            // Open the n-th file +            _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix); +            if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags)) +                break; + +            // If the open succeeded, we re-allocate the base provider array +            NewBaseArray = STORM_ALLOC(TBaseProviderData, dwBaseFiles + 1); +            if(NewBaseArray == NULL) +            { +                SetLastError(ERROR_NOT_ENOUGH_MEMORY); +                return false; +            } + +            // Copy the old base data array to the new base data array +            if(pStream->FileBitmap != NULL) +            { +                memcpy(NewBaseArray, pStream->FileBitmap, sizeof(TBaseProviderData) * dwBaseFiles); +                STORM_FREE(pStream->FileBitmap); +            } + +            // Also copy the opened base array +            memcpy(NewBaseArray + dwBaseFiles, &pStream->Base, sizeof(TBaseProviderData)); +            pStream->FileBitmap = NewBaseArray; +            dwBaseFiles++; + +            // Get the size of the base stream +            pStream->BaseGetSize(pStream, &FileSize); +            assert(FileSize <= BLOCK4_MAX_FSIZE); +            RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); +            BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); +             +            // Increment the stream size and number of blocks             +            pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE); +            pStream->BlockCount += (DWORD)BlockCount; + +            // Is this the last file? +            if(FileSize < BLOCK4_MAX_FSIZE) +            { +                if(RemainderBlock) +                { +                    pStream->StreamSize += (RemainderBlock - BLOCK4_HASH_SIZE); +                    pStream->BlockCount++; +                } +                break; +            } +        } + +        // Fill the remainining block stream variables +        pStream->BitmapSize = dwBaseFiles; +        pStream->BlockSize  = BLOCK4_BLOCK_SIZE; +        pStream->IsComplete = 1; +        pStream->IsModified = 0; + +        // Fill the remaining stream variables +        pStream->StreamPos = 0; +        pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + +        STORM_FREE(szNameBuff); +    } + +    // If we opened something, return success +    if(dwBaseFiles == 0) +    { +        FileStream_Close(pStream); +        SetLastError(ERROR_FILE_NOT_FOUND); +        pStream = NULL; +    } + +    return pStream; +} + +//-----------------------------------------------------------------------------  // Public functions  /** @@ -1870,14 +2129,14 @@ TFileStream * FileStream_CreateFile(  {      TFileStream * pStream; -    // We only support creation of linear, local file -    if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) +    // We only support creation of flat, local file +    if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE))      {          SetLastError(ERROR_NOT_SUPPORTED);          return NULL;      } -    // Allocate file stream structure for linear stream +    // Allocate file stream structure for flat stream      pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);      if(pStream != NULL)      { @@ -1924,41 +2183,27 @@ TFileStream * FileStream_OpenFile(      const TCHAR * szFileName,      DWORD dwStreamFlags)  { -    DWORD dwBaseProvider = dwStreamFlags & BASE_PROVIDER_MASK; - -    // 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; -    } +    DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK; +    size_t nPrefixLength = FileStream_Prefix(szFileName, &dwProvider);      // Re-assemble the stream flags -    dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | (dwStreamFlags & STREAM_PROVIDER_MASK) | dwBaseProvider; +    dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | dwProvider; +    szFileName += nPrefixLength; + +    // Perform provider-specific open      switch(dwStreamFlags & STREAM_PROVIDER_MASK)      { -        case STREAM_PROVIDER_LINEAR: -            return LinearStream_Open(szFileName, dwStreamFlags); +        case STREAM_PROVIDER_FLAT: +            return FlatStream_Open(szFileName, dwStreamFlags);          case STREAM_PROVIDER_PARTIAL:              return PartialStream_Open(szFileName, dwStreamFlags); -        case STREAM_PROVIDER_ENCRYPTED: -            return EncryptedStream_Open(szFileName, dwStreamFlags); +        case STREAM_PROVIDER_MPQE: +            return MpqeStream_Open(szFileName, dwStreamFlags); + +        case STREAM_PROVIDER_BLOCK4: +            return Block4Stream_Open(szFileName, dwStreamFlags);          default:              SetLastError(ERROR_INVALID_PARAMETER); @@ -1967,6 +2212,215 @@ TFileStream * FileStream_OpenFile(  }  /** + * Returns the file name of the stream + * + * \a pStream Pointer to an open stream + */ +const TCHAR * FileStream_GetFileName(TFileStream * pStream) +{ +    assert(pStream != NULL); +    return pStream->szFileName; +} + +/** + * Returns the length of the provider prefix. Returns zero if no prefix + * + * \a szFileName Pointer to a stream name (file, mapped file, URL) + * \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX) + */ + +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider) +{ +    size_t nPrefixLength1 = 0; +    size_t nPrefixLength2 = 0; +    DWORD dwProvider = 0; + +    if(szFileName != NULL) +    { +        // +        // Determine the stream provider +        // + +        if(!_tcsnicmp(szFileName, _T("flat-"), 5)) +        { +            dwProvider |= STREAM_PROVIDER_FLAT; +            nPrefixLength1 = 5; +        } + +        if(!_tcsnicmp(szFileName, _T("part-"), 5)) +        { +            dwProvider |= STREAM_PROVIDER_PARTIAL; +            nPrefixLength1 = 5; +        } + +        if(!_tcsnicmp(szFileName, _T("mpqe-"), 5)) +        { +            dwProvider |= STREAM_PROVIDER_MPQE; +            nPrefixLength1 = 5; +        } + +        if(!_tcsnicmp(szFileName, _T("blk4-"), 5)) +        { +            dwProvider |= STREAM_PROVIDER_BLOCK4; +            nPrefixLength1 = 5; +        } + +        // +        // Determine the base provider +        // + +        if(!_tcsnicmp(szFileName+nPrefixLength1, _T("file:"), 5)) +        { +            dwProvider |= BASE_PROVIDER_FILE; +            nPrefixLength2 = 5; +        } + +        if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4)) +        { +            dwProvider |= BASE_PROVIDER_MAP; +            nPrefixLength2 = 4; +        } +         +        if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5)) +        { +            dwProvider |= BASE_PROVIDER_HTTP; +            nPrefixLength2 = 5; +        } + +        // Only accept stream provider if we recognized the base provider +        if(nPrefixLength2 != 0) +        { +            // It is also allowed to put "//" after the base provider, e.g. "file://", "http://" +            if(szFileName[nPrefixLength1+nPrefixLength2] == '/' && szFileName[nPrefixLength1+nPrefixLength2+1] == '/') +                nPrefixLength2 += 2; + +            if(pdwProvider != NULL) +                *pdwProvider = dwProvider; +            return nPrefixLength1 + nPrefixLength2; +        } +    } + +    return 0; +} + +/** + * Sets a download callback. Whenever the stream needs to download one or more blocks + * from the server, the callback is called + * + * \a pStream Pointer to an open stream + * \a pfnCallback Pointer to callback function + * \a pvUserData Arbitrary user pointer passed to the download callback + */ + +bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData) +{ +    TBlockStream * pBlockStream = (TBlockStream *)pStream; + +    if(pStream->BlockRead == NULL) +    { +        SetLastError(ERROR_NOT_SUPPORTED); +        return false; +    } + +    pBlockStream->pfnCallback = pfnCallback; +    pBlockStream->UserData = pvUserData; +    return true; +} + +/** + * This function gives the block map. The 'pvBitmap' pointer must point to a buffer + * of at least sizeof(STREAM_BLOCK_MAP) size. It can also have size of the complete + * block map (i.e. sizeof(STREAM_BLOCK_MAP) + BitmapSize). In that case, the function + * also copies the bit-based block map. + * + * \a pStream Pointer to an open stream + * \a pvBitmap Pointer to buffer where the block map will be stored + * \a cbBitmap Length of the buffer, of the block map + * \a cbLengthNeeded Length of the bitmap, in bytes + */ + +bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, PDWORD pcbLengthNeeded) +{ +    TStreamBitmap * pBitmap = (TStreamBitmap *)pvBitmap; +    TBlockStream * pBlockStream = (TBlockStream *)pStream; +    ULONGLONG BlockOffset; +    LPBYTE Bitmap = (LPBYTE)(pBitmap + 1); +    DWORD BitmapSize; +    DWORD BlockCount; +    DWORD BlockSize; +    bool bResult = false; + +    // Retrieve the size of one block +    if(pStream->BlockCheck != NULL) +    { +        BlockCount = pBlockStream->BlockCount; +        BlockSize = pBlockStream->BlockSize; +    } +    else +    { +        BlockCount = (DWORD)((pStream->StreamSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); +        BlockSize = DEFAULT_BLOCK_SIZE; +    } + +    // Fill-in the variables +    BitmapSize = (BlockCount + 7) / 8; + +    // Give the number of blocks +    if(pcbLengthNeeded != NULL) +        pcbLengthNeeded[0] = sizeof(TStreamBitmap) + BitmapSize; + +    // If the length of the buffer is not enough +    if(pvBitmap != NULL && cbBitmap != 0) +    { +        // Give the STREAM_BLOCK_MAP structure +        if(cbBitmap >= sizeof(TStreamBitmap)) +        { +            pBitmap->StreamSize = pStream->StreamSize; +            pBitmap->BitmapSize = BitmapSize; +            pBitmap->BlockCount = BlockCount; +            pBitmap->BlockSize  = BlockSize; +            pBitmap->IsComplete = (pStream->BlockCheck != NULL) ? pBlockStream->IsComplete : 1; +            bResult = true; +        } + +        // Give the block bitmap, if enough space +        if(cbBitmap >= sizeof(TStreamBitmap) + BitmapSize) +        { +            // Version with bitmap present +            if(pStream->BlockCheck != NULL) +            { +                DWORD ByteIndex = 0; +                BYTE BitMask = 0x01; + +                // Initialize the map with zeros +                memset(Bitmap, 0, BitmapSize); + +                // Fill the map +                for(BlockOffset = 0; BlockOffset < pStream->StreamSize; BlockOffset += BlockSize) +                { +                    // Set the bit if the block is present +                    if(pBlockStream->BlockCheck(pStream, BlockOffset)) +                        Bitmap[ByteIndex] |= BitMask; + +                    // Move bit position +                    ByteIndex += (BitMask >> 0x07); +                    BitMask = (BitMask >> 0x07) | (BitMask << 0x01); +                } +            } +            else +            { +                memset(Bitmap, 0xFF, BitmapSize); +            } +        } +    } + +    // Set last error value and return +    if(bResult == false) +        SetLastError(ERROR_INSUFFICIENT_BUFFER); +    return bResult; +} + +/**   * Reads data from the stream   *   * - Returns true if the read operation succeeded and all bytes have been read @@ -2094,8 +2548,8 @@ bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags)   */  bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream)  { -    // Only supported on linear files -    if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) +    // Only supported on flat files +    if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE))      {          SetLastError(ERROR_NOT_SUPPORTED);          return false; @@ -2117,7 +2571,7 @@ bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream)          return false;      // Now open the base file again -    if(BaseFile_Open(pStream, pStream->dwFlags)) +    if(BaseFile_Open(pStream, pStream->szFileName, pStream->dwFlags))          return false;      // Cleanup the new stream @@ -2126,27 +2580,6 @@ bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream)  }  /** - * Returns the file name of the stream - * - * \a pStream Pointer to an open stream - */ -const TCHAR * FileStream_GetFileName(TFileStream * pStream) -{ -    assert(pStream != NULL); -    return pStream->szFileName; -} - -/** - * Returns true if the stream is read-only - * - * \a pStream Pointer to an open stream - */ -bool FileStream_IsReadOnly(TFileStream * pStream) -{ -    return (pStream->dwFlags & STREAM_FLAG_READ_ONLY) ? true : false; -} - -/**   * This function 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 diff --git a/src/FileStream.h b/src/FileStream.h index f660a87..60f41d3 100644 --- a/src/FileStream.h +++ b/src/FileStream.h @@ -24,6 +24,7 @@ typedef bool (*STREAM_CREATE)(  typedef bool (*STREAM_OPEN)(      struct TFileStream * pStream,       // Pointer to an unopened stream +    const TCHAR * szFileName,           // Pointer to file name to be open      DWORD dwStreamFlags                 // Stream flags      ); @@ -57,11 +58,11 @@ typedef bool (*STREAM_GETPOS)(      );  typedef void (*STREAM_CLOSE)( -    struct TFileStream * pStream +    struct TFileStream * pStream        // Pointer to an open stream      );  typedef bool (*BLOCK_READ)( -    struct TFileStream * pStream,       // Pointer to an opened block stream +    struct TFileStream * pStream,       // Pointer to a block-oriented stream      ULONGLONG StartOffset,              // Byte offset of start of the block array      ULONGLONG EndOffset,                // End offset (either end of the block or end of the file)      LPBYTE BlockBuffer,                 // Pointer to block-aligned buffer @@ -70,8 +71,12 @@ typedef bool (*BLOCK_READ)(      );  typedef DWORD (*BLOCK_CHECK)( -    struct TFileStream * pStream, -    ULONGLONG BlockOffset +    struct TFileStream * pStream,       // Pointer to a block-oriented stream +    ULONGLONG BlockOffset               // Offset of the file to check +    ); + +typedef void (*BLOCK_SAVEMAP)( +    struct TFileStream * pStream        // Pointer to a block-oriented stream      );  //----------------------------------------------------------------------------- @@ -187,12 +192,15 @@ struct TFileStream  struct TBlockStream : public TFileStream  { +    SFILE_DOWNLOAD_CALLBACK pfnCallback;    // Callback for downloading      void * FileBitmap;                      // Array of bits for file blocks +    void * UserData;                        // User data to be passed to the download callback      DWORD BitmapSize;                       // Size of the file bitmap (in bytes)      DWORD BlockSize;                        // Size of one block, in bytes      DWORD BlockCount;                       // Number of data blocks in the file      DWORD IsComplete;                       // If nonzero, no blocks are missing -}; +    DWORD IsModified;                       // nonzero if the bitmap has been modified +};          //-----------------------------------------------------------------------------  // Structure for encrypted stream diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index fe02931..322211c 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -250,7 +250,7 @@ static DWORD GetMaxFileOffset32(TMPQArchive * ha)      return dwMaxFileOffset;  } -static ULONGLONG DetermineEndOfArchive_V1_V2( +static ULONGLONG DetermineArchiveSize_V1_V2(      TMPQArchive * ha,      TMPQHeader * pHeader,      ULONGLONG MpqOffset, @@ -264,9 +264,17 @@ static ULONGLONG DetermineEndOfArchive_V1_V2(      // Check if we can rely on the archive size in the header      if((FileSize >> 0x20) == 0)      { -        dwArchiveSize32 = (DWORD)(FileSize - MpqOffset); -        if(pHeader->dwBlockTablePos < pHeader->dwArchiveSize && pHeader->dwArchiveSize <= dwArchiveSize32) -            return pHeader->dwArchiveSize; +        if(pHeader->dwBlockTablePos < pHeader->dwArchiveSize) +        { +            // If the block table end matches the archive size, we trust the archive size +            if(pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) == pHeader->dwArchiveSize) +                return pHeader->dwArchiveSize; + +            // If the archive size in the header is less than real file size +            dwArchiveSize32 = (DWORD)(FileSize - MpqOffset); +            if(pHeader->dwArchiveSize <= dwArchiveSize32) +                return pHeader->dwArchiveSize; +        }      }      // Check if there is a signature header @@ -385,7 +393,7 @@ int ConvertMpqHeaderToFormat4(              // Determine the archive size on malformed MPQs              if(ha->dwFlags & MPQ_FLAG_MALFORMED)              { -                pHeader->ArchiveSize64 = DetermineEndOfArchive_V1_V2(ha, pHeader, MpqOffset, FileSize); +                pHeader->ArchiveSize64 = DetermineArchiveSize_V1_V2(ha, pHeader, MpqOffset, FileSize);                  pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64;              }              break; @@ -423,25 +431,25 @@ int ConvertMpqHeaderToFormat4(                  if(pHeader->HiBlockTablePos64 != 0)                  {                      // BlockTableSize64 may be less than TblSize * sizeof(TMPQBlock). -                    // That means that the hash table is compressed. +                    // That means that the hi-block table is compressed.                      pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - BlockTablePos64;                      assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock))); -                    // Determine the size of the hi-block table -                    pHeader->HiBlockTableSize64 = DetermineEndOfArchive_V1_V2(ha, pHeader, MpqOffset, FileSize) - pHeader->HiBlockTablePos64; -                    assert(pHeader->HiBlockTableSize64 == (pHeader->dwBlockTableSize * sizeof(USHORT))); +                    // Determine real archive size +                    pHeader->ArchiveSize64 = DetermineArchiveSize_V1_V2(ha, pHeader, MpqOffset, FileSize); -                    // Recalculate the archive size -                    pHeader->ArchiveSize64 = pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64; +                    // Calculate the size of the hi-block table +                    pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; +                    assert(pHeader->HiBlockTableSize64 == (pHeader->dwBlockTableSize * sizeof(USHORT)));                  }                  else                  { -                    // Block table size is the end of the archive minus the block table position -                    pHeader->BlockTableSize64 = DetermineEndOfArchive_V1_V2(ha, pHeader, MpqOffset, FileSize) - BlockTablePos64; -                    assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock))); +                    // Determine real archive size +                    pHeader->ArchiveSize64 = DetermineArchiveSize_V1_V2(ha, pHeader, MpqOffset, FileSize); -                    // dwArchiveSize in the header v 2.0 is 32-bit only -                    pHeader->ArchiveSize64 = BlockTablePos64 + pHeader->BlockTableSize64; +                    // Calculate size of the block table +                    pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - BlockTablePos64; +                    assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock)));                  }              }              else @@ -1041,7 +1049,7 @@ static void CreateHetHeader(                                pHetHeader->dwIndexTableSize;  } -TMPQHetTable * CreateHetTable(DWORD dwFileCount, DWORD dwNameHashBitSize, LPBYTE pbSrcData) +TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwNameHashBitSize, LPBYTE pbSrcData)  {      TMPQHetTable * pHetTable; @@ -1058,33 +1066,37 @@ TMPQHetTable * CreateHetTable(DWORD dwFileCount, DWORD dwNameHashBitSize, LPBYTE          pHetTable->AndMask64 = ((dwNameHashBitSize != 0x40) ? ((ULONGLONG)1 << dwNameHashBitSize) : 0) - 1;          pHetTable->OrMask64 = (ULONGLONG)1 << (dwNameHashBitSize - 1); +        // If the total count is not entered, use default +        if(dwTotalCount == 0) +            dwTotalCount = (dwEntryCount * 4) / 3; +          // Store the HET table parameters -        pHetTable->dwEntryCount        = dwFileCount; -        pHetTable->dwTotalCount        = (dwFileCount * 4) / 3; +        pHetTable->dwEntryCount        = dwEntryCount; +        pHetTable->dwTotalCount        = dwTotalCount;          pHetTable->dwNameHashBitSize   = dwNameHashBitSize; -        pHetTable->dwIndexSizeTotal    = GetNecessaryBitCount(dwFileCount); +        pHetTable->dwIndexSizeTotal    = GetNecessaryBitCount(dwEntryCount);          pHetTable->dwIndexSizeExtra    = 0;          pHetTable->dwIndexSize         = pHetTable->dwIndexSizeTotal;          // Allocate array of hashes -        pHetTable->pNameHashes = STORM_ALLOC(BYTE, pHetTable->dwTotalCount); +        pHetTable->pNameHashes = STORM_ALLOC(BYTE, dwTotalCount);          if(pHetTable->pNameHashes != NULL)          {              // Make sure the data are initialized -            memset(pHetTable->pNameHashes, 0, pHetTable->dwTotalCount); +            memset(pHetTable->pNameHashes, 0, dwTotalCount);              // Allocate the bit array for file indexes -            pHetTable->pBetIndexes = CreateBitArray(pHetTable->dwTotalCount * pHetTable->dwIndexSizeTotal, 0xFF); +            pHetTable->pBetIndexes = CreateBitArray(dwTotalCount * pHetTable->dwIndexSizeTotal, 0xFF);              if(pHetTable->pBetIndexes != NULL)              {                  // Initialize the HET table from the source data (if given)                  if(pbSrcData != NULL)                  {                      // Copy the name hashes -                    memcpy(pHetTable->pNameHashes, pbSrcData, pHetTable->dwTotalCount); +                    memcpy(pHetTable->pNameHashes, pbSrcData, dwTotalCount);                      // Copy the file indexes -                    memcpy(pHetTable->pBetIndexes->Elements, pbSrcData + pHetTable->dwTotalCount, pHetTable->pBetIndexes->NumberOfBytes); +                    memcpy(pHetTable->pBetIndexes->Elements, pbSrcData + dwTotalCount, pHetTable->pBetIndexes->NumberOfBytes);                  }                  // Return the result HET table @@ -1152,7 +1164,7 @@ static TMPQHetTable * TranslateHetTable(TMPQHetHeader * pHetHeader)      assert(pHetHeader->ExtHdr.dwVersion == 1);      // Verify size of the HET table -    if(pHetHeader->ExtHdr.dwDataSize >= sizeof(TMPQHetHeader)) +    if(pHetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader)))      {          // Verify the size of the table in the header          if(pHetHeader->dwTableSize == pHetHeader->ExtHdr.dwDataSize) @@ -1161,7 +1173,8 @@ static TMPQHetTable * TranslateHetTable(TMPQHetHeader * pHetHeader)              assert((sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader) + pHetHeader->dwTotalCount + pHetHeader->dwIndexTableSize) == pHetHeader->dwTableSize);              // So far, all MPQs with HET Table have had total number of entries equal to 4/3 of file count -            assert(((pHetHeader->dwEntryCount * 4) / 3) == pHetHeader->dwTotalCount); +            // Exception: "2010 - Starcraft II\!maps\Tya's Zerg Defense (unprotected).SC2Map" +//          assert(((pHetHeader->dwEntryCount * 4) / 3) == pHetHeader->dwTotalCount);              // The size of one index is predictable as well              assert(GetNecessaryBitCount(pHetHeader->dwEntryCount) == pHetHeader->dwIndexSizeTotal); @@ -1171,7 +1184,7 @@ static TMPQHetTable * TranslateHetTable(TMPQHetHeader * pHetHeader)              assert(((pHetHeader->dwTotalCount * pHetHeader->dwIndexSizeTotal) + 7) / 8 == pHetHeader->dwIndexTableSize);              // Create translated table -            pHetTable = CreateHetTable(pHetHeader->dwEntryCount, pHetHeader->dwNameHashBitSize, pbSrcData); +            pHetTable = CreateHetTable(pHetHeader->dwEntryCount, pHetHeader->dwTotalCount, pHetHeader->dwNameHashBitSize, pbSrcData);              if(pHetTable != NULL)              {                  // Now the sizes in the hash table should be already set @@ -1422,7 +1435,7 @@ static TMPQBetTable * TranslateBetTable(      ha = ha;      // Verify size of the HET table -    if(pBetHeader->ExtHdr.dwDataSize >= sizeof(TMPQBetHeader)) +    if(pBetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQBetHeader) - sizeof(TMPQExtHeader)))      {          // Verify the size of the table in the header          if(pBetHeader->dwTableSize == pBetHeader->ExtHdr.dwDataSize) @@ -2263,9 +2276,8 @@ TMPQHetTable * LoadHetTable(TMPQArchive * ha)      TMPQHetTable * pHetTable = NULL;      TMPQHeader * pHeader = ha->pHeader; -    // If the HET table position is not NULL, we expect -    // both HET and BET tables to be present. -    if(pHeader->HetTablePos64 != 0) +    // If the HET table position is not 0, we expect the table to be present +    if(pHeader->HetTablePos64 != 0 && pHeader->HetTableSize64 != 0)      {          // Attempt to load the HET table (Hash Extended Table)          pExtTable = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); @@ -2286,9 +2298,8 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha)      TMPQBetTable * pBetTable = NULL;      TMPQHeader * pHeader = ha->pHeader; -    // If the HET table position is not NULL, we expect -    // both HET and BET tables to be present. -    if(pHeader->BetTablePos64 != 0) +    // If the BET table position is not 0, we expect the table to be present +    if(pHeader->BetTablePos64 != 0 && pHeader->BetTableSize64 != 0)      {          // Attempt to load the HET table (Hash Extended Table)          pExtTable = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); @@ -2312,9 +2323,15 @@ int LoadAnyHashTable(TMPQArchive * ha)      if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0)          return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT); -    // Try to load HET table (the new hash table) and the classic HASH table -    ha->pHetTable = LoadHetTable(ha); -    ha->pHashTable = LoadHashTable(ha); +    // Try to load HET table +    if(pHeader->HetTablePos64 != 0) +        ha->pHetTable = LoadHetTable(ha); + +    // Try to load the hash table +    if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) +        ha->pHashTable = LoadHashTable(ha); + +    // At least one of the tables must be present      if(ha->pHetTable == NULL && ha->pHashTable == NULL)          return ERROR_FILE_CORRUPT; @@ -2580,7 +2597,7 @@ int RebuildHetTable(TMPQArchive * ha)      // Create new HET table based on the total number of entries in the file table      // Note that if we fail to create it, we just stop using HET table -    ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0x40, NULL); +    ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL);      if(ha->pHetTable != NULL)      {          // Go through the file table again and insert all existing files diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index 5246f89..fdc1524 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -815,7 +815,7 @@ bool WINAPI SFileAddFileEx(      // Open added file      if(nError == ERROR_SUCCESS)      { -        pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);          if(pStream == NULL)              nError = GetLastError();      } diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp index a0ba774..7e63993 100644 --- a/src/SFileAttributes.cpp +++ b/src/SFileAttributes.cpp @@ -51,8 +51,7 @@ static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)      return cbAttrFile;  } -#ifdef _DEBUG -static DWORD GetSizeOfAttributesFile_v2(DWORD dwAttrFlags, DWORD dwFileTableSize) +static bool CheckSizeOfAttributesFile(DWORD cbSizeOfAttr, DWORD dwAttrFlags, DWORD dwFileTableSize)  {      DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER); @@ -64,15 +63,31 @@ static DWORD GetSizeOfAttributesFile_v2(DWORD dwAttrFlags, DWORD dwFileTableSize      if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)          cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE; -    // interface.MPQ.part from WoW build 10958 has -    // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. -    // The array is filled with zeros, so we don't know what it should contain +    // Various variants with the patch bit      if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) -        cbAttrFile += dwFileTableSize * sizeof(DWORD); -     -    return cbAttrFile; +    { +        // Check for expected size +        if(cbSizeOfAttr == (cbAttrFile + (dwFileTableSize + 6) / 8)) +            return true; + +        // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing +        if(cbSizeOfAttr == cbSizeOfAttr) +            return false; + +        // interface.MPQ.part from WoW build 10958 has +        // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. +        // The array is filled with zeros, so we don't know what it should contain +        if(cbSizeOfAttr == (cbAttrFile + dwFileTableSize * sizeof(DWORD))) +            return false; + +        // Size mismatch +        assert(false); +        return false; +    } + +    assert(cbSizeOfAttr == cbAttrFile); +    return false;  } -#endif  static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile)  { @@ -80,6 +95,7 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF      LPBYTE pbAttrPtr = pbAttrFile;      DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;      DWORD i; +    bool bPatchBitsValid;      // Load and verify the header      if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd) @@ -90,15 +106,10 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF          if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1)              return ERROR_BAD_FORMAT; -        // Verify the size of the file +        // Verify the flags and of the file          assert((pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) == 0); -        // Verify the size of the file -#ifdef _DEBUG -        assert(cbAttrFile == GetSizeOfAttributesFile(pAttrHeader->dwFlags, dwBlockTableSize) || -               cbAttrFile == GetSizeOfAttributesFile_v2(pAttrHeader->dwFlags, dwBlockTableSize)); -#endif - +        bPatchBitsValid = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, dwBlockTableSize);          ha->dwAttrFlags = pAttrHeader->dwFlags;          pbAttrPtr = (LPBYTE)(pAttrHeader + 1);      } @@ -154,7 +165,7 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF      }      // Read the patch bit for each file (if present) -    if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) +    if((ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) && bPatchBitsValid)      {          LPBYTE pbBitArray = pbAttrPtr;          DWORD cbArraySize = (dwBlockTableSize + 7) / 8; diff --git a/src/SFileCompactArchive.cpp b/src/SFileCompactArchive.cpp index b5482c8..00efbb4 100644 --- a/src/SFileCompactArchive.cpp +++ b/src/SFileCompactArchive.cpp @@ -504,7 +504,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR          else              _tcscat(szTempFile, _T("_")); -        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);          if(pTempStream == NULL)              nError = GetLastError();      } diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp index 4ae71e9..5f375c8 100644 --- a/src/SFileCreateArchive.cpp +++ b/src/SFileCreateArchive.cpp @@ -77,7 +77,7 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWO      memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));      CreateInfo.cbSize         = sizeof(SFILE_CREATE_MPQ);      CreateInfo.dwMpqVersion   = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; -    CreateInfo.dwStreamFlags  = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE; +    CreateInfo.dwStreamFlags  = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE;      CreateInfo.dwFileFlags1   = (dwCreateFlags & MPQ_CREATE_LISTFILE)   ? MPQ_FILE_EXISTS : 0;      CreateInfo.dwFileFlags2   = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_EXISTS : 0;      CreateInfo.dwAttrFlags    = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0; @@ -133,7 +133,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea      // We verify if the file already exists and if it's a MPQ archive.      // If yes, we won't allow to overwrite it. -    if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq)) +    if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))      {          SFileCloseArchive(hMpq);          SetLastError(ERROR_ALREADY_EXISTS); @@ -227,7 +227,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea      // Create initial HET table, if the caller required an MPQ format 3.0 or newer      if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3 && pCreateInfo->dwMaxFileCount != 0)      { -        ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0x40, NULL); +        ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL);          if(ha->pHetTable == NULL)              nError = ERROR_NOT_ENOUGH_MEMORY;      } diff --git a/src/SFileExtractFile.cpp b/src/SFileExtractFile.cpp index c8053ed..314d8ff 100644 --- a/src/SFileExtractFile.cpp +++ b/src/SFileExtractFile.cpp @@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR      // Create the local file      if(nError == ERROR_SUCCESS)      { -        pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        pLocalFile = FileStream_CreateFile(szExtracted, 0);          if(pLocalFile == NULL)              nError = GetLastError();      } diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index e56e2f6..05bea29 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -163,20 +163,10 @@ bool WINAPI SFileGetFileInfo(              }              break; -        case SFileMpqStreamBlockSize: +        case SFileMpqStreamBitmap:              ha = IsValidMpqHandle(hMpqOrFile);              if(ha != NULL) -            { -                // TODO -            } -            break; - -        case SFileMpqStreamBlockAvailable: -            ha = IsValidMpqHandle(hMpqOrFile); -            if(ha != NULL) -            { -                // TODO -            } +                return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded);              break;          case SFileMpqUserDataOffset: @@ -616,7 +606,7 @@ bool WINAPI SFileGetFileInfo(              ha = IsValidMpqHandle(hMpqOrFile);              if(ha != NULL)              { -                dwInt32Value = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); +                dwInt32Value  = (ha->dwFlags & MPQ_FLAG_READ_ONLY) ? 1 : 0;                  pvSrcFileInfo = &dwInt32Value;                  cbSrcFileInfo = sizeof(DWORD);                  nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; @@ -954,7 +944,7 @@ static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * sz          }      } -    return ERROR_NOT_SUPPORTED; +    return ERROR_CAN_NOT_COMPLETE;  }  bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index 9970791..27c9972 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -177,7 +177,7 @@ bool WINAPI SFileOpenArchive(      {          FileStream_GetSize(pStream, &FileSize);          if(FileSize < MPQ_HEADER_SIZE_V1) -            nError = ERROR_FILE_CORRUPT; +            nError = ERROR_BAD_FORMAT;      }      // Allocate the MPQhandle @@ -191,6 +191,7 @@ bool WINAPI SFileOpenArchive(      if(nError == ERROR_SUCCESS)      {          ULONGLONG SearchOffset = 0; +        DWORD dwStreamFlags = 0;          DWORD dwHeaderID;          memset(ha, 0, sizeof(TMPQArchive)); @@ -198,9 +199,9 @@ bool WINAPI SFileOpenArchive(          ha->pStream = pStream;          pStream = NULL; -        // Remember if the archive is open for write -        if(FileStream_IsReadOnly(ha->pStream)) -            ha->dwFlags |= MPQ_FLAG_READ_ONLY; +        // Set the archive read only if the stream is read-only +        FileStream_GetFlags(ha->pStream, &dwStreamFlags); +        ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;          // Also remember if we shall check sector CRCs when reading file          if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) @@ -287,7 +288,7 @@ bool WINAPI SFileOpenArchive(              ha->MpqPos = SearchOffset;              // Sector size must be nonzero. -            if(ha->pHeader->wSectorSize == 0) +            if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)                  nError = ERROR_BAD_FORMAT;          }      } @@ -409,6 +410,26 @@ bool WINAPI SFileOpenArchive(  }  //----------------------------------------------------------------------------- +// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *); +// +// Sets a callback that is called when content is downloaded from the master MPQ +// + +bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    // Do nothing if 'hMpq' is bad parameter +    if(!IsValidMpqHandle(hMpq)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    return FileStream_SetCallback(ha->pStream, DownloadCB, pvUserData); +} + +//-----------------------------------------------------------------------------  // bool SFileFlushArchive(HANDLE hMpq)  //  // Saves all dirty data into MPQ archive. diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp index c2cf5d4..31e235e 100644 --- a/src/SFileOpenFileEx.cpp +++ b/src/SFileOpenFileEx.cpp @@ -46,7 +46,7 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)      CopyFileName(szFileNameT, szFileName, strlen(szFileName));      // Open the file and create the TMPQFile structure -    pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | STREAM_FLAG_READ_ONLY); +    pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);      if(pStream != NULL)      {          // Allocate and initialize file handle diff --git a/src/SFilePatchArchives.cpp b/src/SFilePatchArchives.cpp index cc10726..4b49c59 100644 --- a/src/SFilePatchArchives.cpp +++ b/src/SFilePatchArchives.cpp @@ -519,7 +519,7 @@ bool WINAPI SFileOpenPatchArchive(      if(nError == ERROR_SUCCESS)      { -        if(!FileStream_IsReadOnly(ha->pStream)) +        if(!(ha->dwFlags & MPQ_FLAG_READ_ONLY))              nError = ERROR_ACCESS_DENIED;      } diff --git a/src/StormCommon.h b/src/StormCommon.h index 2901c9f..2b2cf1d 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -193,7 +193,7 @@ int  RebuildHetTable(TMPQArchive * ha);  int  RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize, DWORD dwNewMaxFileCount);  int  SaveMPQTables(TMPQArchive * ha); -TMPQHetTable * CreateHetTable(DWORD dwFileCount, DWORD dwHashBitSize, LPBYTE pbSrcData); +TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData);  void FreeHetTable(TMPQHetTable * pHetTable);  TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount); diff --git a/src/StormLib.h b/src/StormLib.h index e528283..a0cfeb6 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -145,6 +145,7 @@ extern "C" {  #define ERROR_INTERNAL_FILE              10003  // The given operation is not allowed on internal file  #define ERROR_BASE_FILE_MISSING          10004  // The file is present as incremental patch file, but base file is missing  #define ERROR_MARKED_FOR_DELETE          10005  // The file was marked as "deleted" in the MPQ +#define ERROR_FILE_INCOMPLETE            10006  // The required file part is missing  // Values for SFileCreateArchive  #define HASH_TABLE_SIZE_MIN         0x00000004  // Verified: If there is 1 file, hash table size is 4 @@ -264,9 +265,10 @@ extern "C" {  #define BASE_PROVIDER_HTTP          0x00000002  // Base data source is a file on web server  #define BASE_PROVIDER_MASK          0x0000000F  // Mask for base provider value -#define STREAM_PROVIDER_LINEAR      0x00000000  // Stream is linear with no offset mapping +#define STREAM_PROVIDER_FLAT        0x00000000  // Stream is linear with no offset mapping  #define STREAM_PROVIDER_PARTIAL     0x00000010  // Stream is partial file (.part) -#define STREAM_PROVIDER_ENCRYPTED   0x00000020  // Stream is an encrypted MPQ +#define STREAM_PROVIDER_MPQE        0x00000020  // Stream is an encrypted MPQ +#define STREAM_PROVIDER_BLOCK4      0x00000030  // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file  #define STREAM_PROVIDER_MASK        0x000000F0  // Mask for stream provider value  #define STREAM_FLAG_READ_ONLY       0x00000100  // Stream is read only @@ -282,11 +284,7 @@ extern "C" {  #define MPQ_OPEN_NO_HEADER_SEARCH   0x00040000  // Don't search for the MPQ header past the begin of the file  #define MPQ_OPEN_FORCE_MPQ_V1       0x00080000  // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header  #define MPQ_OPEN_CHECK_SECTOR_CRC   0x00100000  // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file - -// Deprecated  #define MPQ_OPEN_READ_ONLY          STREAM_FLAG_READ_ONLY -#define MPQ_OPEN_ENCRYPTED          STREAM_PROVIDER_ENCRYPTED -#define MPQ_OPEN_PARTIAL            STREAM_PROVIDER_PARTIAL  // Flags for SFileCreateArchive  #define MPQ_CREATE_LISTFILE         0x00100000  // Also add the (listfile) file @@ -363,8 +361,7 @@ typedef enum _SFileInfoClass  {      // Info classes for archives      SFileMpqFileName,                       // Name of the archive file (TCHAR []) -    SFileMpqStreamBlockSize,                // Size of one stream block in bytes (DWORD) -    SFileMpqStreamBlockAvailable,           // Nonzero if the stream block at the given offset is available (input: ByteOffset, ULONGLONG) +    SFileMpqStreamBitmap,                   // Array of bits, each bit means availability of one block (BYTE [])      SFileMpqUserDataOffset,                 // Offset of the user data header (ULONGLONG)      SFileMpqUserDataHeader,                 // Raw (unfixed) user data header (TMPQUserData)      SFileMpqUserData,                       // MPQ USer data, without the header (BYTE []) @@ -426,6 +423,11 @@ typedef enum _SFileInfoClass  //-----------------------------------------------------------------------------  // Deprecated flags. These are going to be removed in next releases. +STORMLIB_DEPRECATED_FLAG(DWORD, STREAM_PROVIDER_LINEAR, STREAM_PROVIDER_FLAT); +STORMLIB_DEPRECATED_FLAG(DWORD, STREAM_PROVIDER_ENCRYPTED, STREAM_PROVIDER_MPQE); +STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_OPEN_ENCRYPTED, STREAM_PROVIDER_MPQE); +STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_OPEN_PARTIAL, STREAM_PROVIDER_PARTIAL); +  // MPQ_FILE_COMPRESSED is deprecated. Do not use.  STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_FILE_COMPRESSED, MPQ_FILE_COMPRESS_MASK); @@ -465,6 +467,7 @@ STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_PATCH_CHAIN, SFileInfoPatchC  #define CCB_COMPACTING_FILES                4   // Compacting archive (dwParam1 = current, dwParam2 = total)  #define CCB_CLOSING_ARCHIVE                 5   // Closing archive: No params used +typedef void (WINAPI * SFILE_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes);  typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall);  typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); @@ -931,12 +934,28 @@ typedef struct _SFILE_CREATE_MPQ  //-----------------------------------------------------------------------------  // Stream support - functions +// Structure used by FileStream_GetBitmap +typedef struct _TStreamBitmap +{ +    ULONGLONG StreamSize;                       // Size of the stream, in bytes +    DWORD BitmapSize;                           // Size of the block map, in bytes +    DWORD BlockCount;                           // Number of blocks in the stream +    DWORD BlockSize;                            // Size of one block +    DWORD IsComplete;                           // Nonzero if the file is complete + +    // Followed by the BYTE array, each bit means availability of one block + +} TStreamBitmap; +  // UNICODE versions of the file access functions  TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);  TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);  const TCHAR * FileStream_GetFileName(TFileStream * pStream); +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider); + +bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); -bool FileStream_IsReadOnly(TFileStream * pStream); +bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, LPDWORD pcbLengthNeeded);  bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);  bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite);  bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); @@ -973,6 +992,7 @@ bool   WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD  bool   WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq);  bool   WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); +bool   WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData);  bool   WINAPI SFileFlushArchive(HANDLE hMpq);  bool   WINAPI SFileCloseArchive(HANDLE hMpq);  | 
