aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLadislav <Zezula>2014-01-03 18:56:23 +0100
committerLadislav <Zezula>2014-01-03 18:56:23 +0100
commit6cd009bc7cb60b4000e2267c7d100a3f0d9a42a0 (patch)
tree7e2b5dd6f85855c284067442fc6ebce5c95409f3
parent3a9a6ec46beaf839cfe4fe8b6a26e1ca5e2d0316 (diff)
+ Version 9.00 released
-rw-r--r--StormLib.vcproj160
-rw-r--r--src/FileStream.cpp359
-rw-r--r--src/FileStream.h4
-rw-r--r--src/SBaseFileTable.cpp4
-rw-r--r--src/SBaseSubTypes.cpp7
-rw-r--r--src/SFileAttributes.cpp2
-rw-r--r--src/StormPort.h1
-rw-r--r--stormlib_dll/StormLib.def1
-rw-r--r--test/Test.cpp344
9 files changed, 681 insertions, 201 deletions
diff --git a/StormLib.vcproj b/StormLib.vcproj
index c3e6866..1eac0a3 100644
--- a/StormLib.vcproj
+++ b/StormLib.vcproj
@@ -1561,6 +1561,166 @@
<File
RelativePath=".\src\SBaseSubTypes.cpp"
>
+ <FileConfiguration
+ Name="DebugAD|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugAD|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugAS|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugAS|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseAD|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseAD|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseAS|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseAS|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugUD|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugUD|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugUS|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugUS|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseUD|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseUD|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseUS|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseUS|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StormCommon.h"
+ WarningLevel="4"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\src\SCompression.cpp"
diff --git a/src/FileStream.cpp b/src/FileStream.cpp
index c196f11..9cea184 100644
--- a/src/FileStream.cpp
+++ b/src/FileStream.cpp
@@ -47,6 +47,19 @@ void SetLastError(int nError)
}
#endif
+static DWORD StringToInt(const char * szString)
+{
+ DWORD dwValue = 0;
+
+ while('0' <= szString[0] && szString[0] <= '9')
+ {
+ dwValue = (dwValue * 10) + (szString[0] - '9');
+ szString++;
+ }
+
+ return dwValue;
+}
+
//-----------------------------------------------------------------------------
// Dummy init function
@@ -199,10 +212,10 @@ static bool BaseFile_Read(
// If the byte offset is different from the current file position,
// we have to update the file position
- if(ByteOffset != pStream->FilePos)
+ if(ByteOffset != pStream->Base.File.FilePos)
{
lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET);
- pStream->FilePos = ByteOffset;
+ pStream->Base.File.FilePos = ByteOffset;
}
// Perform the read operation
@@ -320,6 +333,8 @@ static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize)
// Set the current file pointer as the end of the file
bResult = (bool)SetEndOfFile(pStream->Base.File.hFile);
+ if(bResult)
+ pStream->Base.File.FileSize = NewFileSize;
// Restore the file position
FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32);
@@ -337,6 +352,7 @@ static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize)
return false;
}
+ pStream->Base.File.FileSize = NewFileSize;
return true;
}
#endif
@@ -489,9 +505,9 @@ static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
// 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->FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
- pStream->FileSize = (ULONGLONG)fileinfo.st_size;
- pStream->FilePos = 0;
+ pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
+ pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size;
+ pStream->Base.Map.FilePos = 0;
bResult = true;
}
}
@@ -542,7 +558,7 @@ static void BaseMap_Close(TFileStream * pStream)
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
if(pStream->Base.Map.pbFile != NULL)
- munmap(pStream->Base.Map.pbFile, (size_t )pStream->FileSize);
+ munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize);
#endif
pStream->Base.Map.pbFile = NULL;
@@ -1111,6 +1127,7 @@ static bool FlatStream_LoadBitmap(TBlockStream * pStream)
}
// Update the stream size
+ pStream->BuildNumber = Footer.BuildNumber;
pStream->StreamSize = ByteOffset;
// Fill the bitmap information
@@ -1241,7 +1258,7 @@ static void FlatStream_Close(TBlockStream * pStream)
// 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.BuildNumber = pStream->BuildNumber;
Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF);
Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20);
Footer.BlockSize = pStream->BlockSize;
@@ -1273,6 +1290,7 @@ static bool FlatStream_CreateMirror(TBlockStream * pStream)
dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8);
// Setup stream size and position
+ pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really???
pStream->StreamSize = MasterSize;
pStream->StreamPos = 0;
@@ -1332,6 +1350,7 @@ static bool FlatStream_CreateMirror(TBlockStream * pStream)
static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
{
TBlockStream * pStream;
+ ULONGLONG ByteOffset = 0;
// Create new empty stream
pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
@@ -1357,11 +1376,6 @@ static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFla
// Load the bitmap, if required to
if(dwStreamFlags & STREAM_FLAG_USE_BITMAP)
FlatStream_LoadBitmap(pStream);
-
- // Setup the stream size
- if(pStream->FileBitmap == NULL)
- pStream->StreamSize = pStream->Base.File.FileSize;
- pStream->StreamPos = 0;
}
// If we have a stream bitmap, set the reading functions
@@ -1385,8 +1399,12 @@ static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFla
}
else
{
- // Reset the file position to zero
- pStream->Base.File.FilePos = 0;
+ // Reset the base position to zero
+ pStream->BaseRead(pStream, &ByteOffset, NULL, 0);
+
+ // Setup stream size and position
+ pStream->StreamSize = pStream->Base.File.FileSize;
+ pStream->StreamPos = 0;
// Set the base functions
pStream->StreamRead = pStream->BaseRead;
@@ -1420,7 +1438,32 @@ static bool IsPartHeader(PPART_FILE_HEADER pPartHdr)
return false;
}
-static bool PartialStream_LoadBitmap(TBlockStream * pStream)
+static DWORD PartStream_CheckFile(TBlockStream * pStream)
+{
+ PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap;
+ DWORD dwBlockCount;
+
+ // Get the number of blocks
+ dwBlockCount = (DWORD)((pStream->StreamSize + pStream->BlockSize - 1) / pStream->BlockSize);
+
+ // Check all blocks
+ for(DWORD i = 0; i < dwBlockCount; i++, FileBitmap++)
+ {
+ // Few sanity checks
+ assert(FileBitmap->LargeValueHi == 0);
+ assert(FileBitmap->LargeValueLo == 0);
+ assert(FileBitmap->Flags == 0 || FileBitmap->Flags == 3);
+
+ // Check if this block is present
+ if(FileBitmap->Flags != 3)
+ return 0;
+ }
+
+ // Yes, the file is complete
+ return 1;
+}
+
+static bool PartStream_LoadBitmap(TBlockStream * pStream)
{
PPART_FILE_MAP_ENTRY FileBitmap;
PART_FILE_HEADER PartHdr;
@@ -1465,6 +1508,7 @@ static bool PartialStream_LoadBitmap(TBlockStream * pStream)
BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize);
// Update the stream size
+ pStream->BuildNumber = StringToInt(PartHdr.GameBuildNumber);
pStream->StreamSize = StreamSize;
// Fill the bitmap information
@@ -1472,7 +1516,7 @@ static bool PartialStream_LoadBitmap(TBlockStream * pStream)
pStream->BitmapSize = BitmapSize;
pStream->BlockSize = PartHdr.BlockSize;
pStream->BlockCount = BlockCount;
- pStream->IsComplete = 0;
+ pStream->IsComplete = PartStream_CheckFile(pStream);
return true;
}
}
@@ -1483,25 +1527,58 @@ static bool PartialStream_LoadBitmap(TBlockStream * pStream)
return false;
}
-static bool PartialStream_BlockCheck(
+static void PartStream_UpdateBitmap(
+ TBlockStream * pStream, // Pointer to an open stream
+ ULONGLONG StartOffset,
+ ULONGLONG EndOffset,
+ ULONGLONG RealOffset)
+{
+ PPART_FILE_MAP_ENTRY FileBitmap;
+ DWORD BlockSize = pStream->BlockSize;
+
+ // Sanity checks
+ assert((StartOffset & (BlockSize - 1)) == 0);
+ assert(pStream->FileBitmap != NULL);
+
+ // Calculate the first entry in the block map
+ FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (StartOffset / BlockSize);
+
+ // Set all bits for the specified range
+ while(StartOffset < EndOffset)
+ {
+ // Set the bit
+ FileBitmap->BlockOffsHi = (DWORD)(RealOffset >> 0x20);
+ FileBitmap->BlockOffsLo = (DWORD)(RealOffset & 0xFFFFFFFF);
+ FileBitmap->Flags = 3;
+
+ // Move all
+ StartOffset += BlockSize;
+ RealOffset += BlockSize;
+ FileBitmap++;
+ }
+
+ // Increment the bitmap update count
+ pStream->IsModified = 1;
+}
+
+static bool PartStream_BlockCheck(
TBlockStream * pStream, // Pointer to an open stream
ULONGLONG BlockOffset)
{
- PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap;
- DWORD BlockIndex;
+ PPART_FILE_MAP_ENTRY FileBitmap;
// Sanity checks
assert((BlockOffset & (pStream->BlockSize - 1)) == 0);
- assert(FileBitmap != NULL);
+ assert(pStream->FileBitmap != NULL);
- // Calculate the index of the block
- BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize);
+ // Calculate the block map entry
+ FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (BlockOffset / pStream->BlockSize);
// Check if the flags are present
- return (FileBitmap[BlockIndex].Flags & 0x03) ? true : false;
+ return (FileBitmap->Flags & 0x03) ? true : false;
}
-static bool PartialStream_BlockRead(
+static bool PartStream_BlockRead(
TBlockStream * pStream,
ULONGLONG StartOffset,
ULONGLONG EndOffset,
@@ -1519,18 +1596,38 @@ static bool PartialStream_BlockRead(
assert((StartOffset & (pStream->BlockSize - 1)) == 0);
assert(StartOffset < EndOffset);
- // Get the file map entry
- FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex;
-
// If the blocks are not available, we need to load them from the master
// and then save to the mirror
if(bAvailable == false)
{
- // TODO: Support for downloading blocks
- return false;
+ // If we have no master, we cannot satisfy read request
+ if(pStream->pMaster == NULL)
+ return false;
+
+ // 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
+ BytesToRead = (DWORD)(EndOffset - StartOffset);
+ if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead))
+ return false;
+
+ // The loaded blocks are going to be stored to the end of the file
+ // Note that this operation is not required to succeed
+ if(pStream->BaseGetSize(pStream, &ByteOffset))
+ {
+ // Store the loaded blocks to the mirror file.
+ if(pStream->BaseWrite(pStream, &ByteOffset, BlockBuffer, BytesToRead))
+ {
+ PartStream_UpdateBitmap(pStream, StartOffset, EndOffset, ByteOffset);
+ }
+ }
}
else
{
+ // Get the file map entry
+ FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex;
+
+ // Read all blocks
while(StartOffset < EndOffset)
{
// Get the number of bytes to be read
@@ -1549,13 +1646,125 @@ static bool PartialStream_BlockRead(
StartOffset += pStream->BlockSize;
BlockBuffer += pStream->BlockSize;
BytesNeeded -= pStream->BlockSize;
+ FileBitmap++;
+ }
+ }
+
+ return true;
+}
+
+static void PartStream_Close(TBlockStream * pStream)
+{
+ PART_FILE_HEADER PartHeader;
+ ULONGLONG ByteOffset = 0;
+
+ if(pStream->FileBitmap && pStream->IsModified)
+ {
+ // Prepare the part file header
+ memset(&PartHeader, 0, sizeof(PART_FILE_HEADER));
+ PartHeader.PartialVersion = 2;
+ PartHeader.FileSizeHi = (DWORD)(pStream->StreamSize >> 0x20);
+ PartHeader.FileSizeLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF);
+ PartHeader.BlockSize = pStream->BlockSize;
+
+ // Make sure that the header is properly BSWAPed
+ BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER));
+ sprintf(PartHeader.GameBuildNumber, "%u", pStream->BuildNumber);
+
+ // Write the part header
+ pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER));
+
+ // Write the block bitmap
+ BSWAP_ARRAY32_UNSIGNED(pStream->FileBitmap, pStream->BitmapSize);
+ pStream->BaseWrite(pStream, NULL, pStream->FileBitmap, pStream->BitmapSize);
+ }
+
+ // Close the base class
+ BlockStream_Close(pStream);
+}
+
+static bool PartStream_CreateMirror(TBlockStream * pStream)
+{
+ ULONGLONG RemainingSize;
+ 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;
+
+ // 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 * sizeof(PART_FILE_MAP_ENTRY));
+
+ // Setup stream size and position
+ pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really???
+ 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 >= sizeof(PART_FILE_HEADER) + dwBitmapSize)
+ {
+ // Check if the remaining size is aligned to block
+ RemainingSize = MirrorSize - sizeof(PART_FILE_HEADER) - dwBitmapSize;
+ if((RemainingSize & (DEFAULT_BLOCK_SIZE - 1)) == 0 || RemainingSize == MasterSize)
+ {
+ // Attempt to load an existing file bitmap
+ if(PartStream_LoadBitmap(pStream))
+ return true;
+ }
}
+
+ // 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, sizeof(PART_FILE_HEADER) + dwBitmapSize))
+ 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 * PartialStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
+
+static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
{
TBlockStream * pStream;
@@ -1564,44 +1773,49 @@ static TFileStream * PartialStream_Open(const TCHAR * szFileName, DWORD dwStream
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, pStream->szFileName, dwStreamFlags))
+ // Do we have a master stream?
+ if(pStream->pMaster != NULL)
{
- // Do we have base create function and master stream?
-// if(!PartialStream_CreateMirror(pStream))
+ if(!PartStream_CreateMirror(pStream))
{
FileStream_Close(pStream);
SetLastError(ERROR_FILE_NOT_FOUND);
return NULL;
}
}
-
- // Load the stream bitmap
- if(PartialStream_LoadBitmap(pStream))
+ else
{
- // 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 new function pointers
- pStream->StreamRead = (STREAM_READ)BlockStream_Read;
- pStream->StreamGetPos = BlockStream_GetPos;
- pStream->StreamGetSize = BlockStream_GetSize;
- pStream->StreamClose = (STREAM_CLOSE)BlockStream_Close;
+ // Attempt to open the base stream
+ if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))
+ {
+ FileStream_Close(pStream);
+ return NULL;
+ }
- // Supply the block functions
- pStream->BlockCheck = (BLOCK_CHECK)PartialStream_BlockCheck;
- pStream->BlockRead = (BLOCK_READ)PartialStream_BlockRead;
- return pStream;
+ // Load the part stream block map
+ if(!PartStream_LoadBitmap(pStream))
+ {
+ FileStream_Close(pStream);
+ SetLastError(ERROR_BAD_FORMAT);
+ return NULL;
+ }
}
- // Cleanup the stream and return
- FileStream_Close(pStream);
- SetLastError(ERROR_BAD_FORMAT);
- return 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 new function pointers
+ pStream->StreamRead = (STREAM_READ)BlockStream_Read;
+ pStream->StreamGetPos = BlockStream_GetPos;
+ pStream->StreamGetSize = BlockStream_GetSize;
+ pStream->StreamClose = (STREAM_CLOSE)PartStream_Close;
+
+ // Supply the block functions
+ pStream->BlockCheck = (BLOCK_CHECK)PartStream_BlockCheck;
+ pStream->BlockRead = (BLOCK_READ)PartStream_BlockRead;
+ return pStream;
}
//-----------------------------------------------------------------------------
@@ -1927,6 +2141,7 @@ static bool Block4Stream_BlockRead(
DWORD BytesToRead;
DWORD StreamIndex;
DWORD BlockIndex;
+ bool bResult;
// The starting offset must be aligned to size of the block
assert(pStream->FileBitmap != NULL);
@@ -1934,6 +2149,10 @@ static bool Block4Stream_BlockRead(
assert(StartOffset < EndOffset);
assert(bAvailable == true);
+ // Keep compiler happy
+ bAvailable = bAvailable;
+ EndOffset = EndOffset;
+
while(BytesNeeded != 0)
{
// Calculate the block index and the file index
@@ -1942,15 +2161,17 @@ static bool Block4Stream_BlockRead(
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);
+ BytesToRead = STORMLIB_MIN(BytesNeeded, BLOCK4_BLOCK_SIZE);
// Read from the base stream
- if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead))
+ pStream->Base = BaseArray[StreamIndex];
+ bResult = pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead);
+ BaseArray[StreamIndex] = pStream->Base;
+
+ // Did the result succeed?
+ if(bResult == false)
return false;
// Move pointers
@@ -2197,7 +2418,7 @@ TFileStream * FileStream_OpenFile(
return FlatStream_Open(szFileName, dwStreamFlags);
case STREAM_PROVIDER_PARTIAL:
- return PartialStream_Open(szFileName, dwStreamFlags);
+ return PartStream_Open(szFileName, dwStreamFlags);
case STREAM_PROVIDER_MPQE:
return MpqeStream_Open(szFileName, dwStreamFlags);
@@ -2247,19 +2468,19 @@ size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider)
nPrefixLength1 = 5;
}
- if(!_tcsnicmp(szFileName, _T("part-"), 5))
+ else if(!_tcsnicmp(szFileName, _T("part-"), 5))
{
dwProvider |= STREAM_PROVIDER_PARTIAL;
nPrefixLength1 = 5;
}
- if(!_tcsnicmp(szFileName, _T("mpqe-"), 5))
+ else if(!_tcsnicmp(szFileName, _T("mpqe-"), 5))
{
dwProvider |= STREAM_PROVIDER_MPQE;
nPrefixLength1 = 5;
}
- if(!_tcsnicmp(szFileName, _T("blk4-"), 5))
+ else if(!_tcsnicmp(szFileName, _T("blk4-"), 5))
{
dwProvider |= STREAM_PROVIDER_BLOCK4;
nPrefixLength1 = 5;
@@ -2275,13 +2496,13 @@ size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider)
nPrefixLength2 = 5;
}
- if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4))
+ else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4))
{
dwProvider |= BASE_PROVIDER_MAP;
nPrefixLength2 = 4;
}
- if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5))
+ else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5))
{
dwProvider |= BASE_PROVIDER_HTTP;
nPrefixLength2 = 5;
@@ -2339,7 +2560,7 @@ bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCa
* \a cbLengthNeeded Length of the bitmap, in bytes
*/
-bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, PDWORD pcbLengthNeeded)
+bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, DWORD * pcbLengthNeeded)
{
TStreamBitmap * pBitmap = (TStreamBitmap *)pvBitmap;
TBlockStream * pBlockStream = (TBlockStream *)pStream;
diff --git a/src/FileStream.h b/src/FileStream.h
index 60f41d3..44beeed 100644
--- a/src/FileStream.h
+++ b/src/FileStream.h
@@ -70,7 +70,7 @@ typedef bool (*BLOCK_READ)(
bool bAvailable // true if the block is available
);
-typedef DWORD (*BLOCK_CHECK)(
+typedef bool (*BLOCK_CHECK)(
struct TFileStream * pStream, // Pointer to a block-oriented stream
ULONGLONG BlockOffset // Offset of the file to check
);
@@ -84,6 +84,7 @@ typedef void (*BLOCK_SAVEMAP)(
#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3')
#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block
+#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs
typedef struct _PART_FILE_HEADER
{
@@ -182,6 +183,7 @@ struct TFileStream
ULONGLONG StreamSize; // Stream size (can be less than file size)
ULONGLONG StreamPos; // Stream position
+ DWORD BuildNumber; // Game build number
DWORD dwFlags; // Stream flags
// Followed by stream provider data, with variable length
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp
index 322211c..54b66c4 100644
--- a/src/SBaseFileTable.cpp
+++ b/src/SBaseFileTable.cpp
@@ -270,6 +270,10 @@ static ULONGLONG DetermineArchiveSize_V1_V2(
if(pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) == pHeader->dwArchiveSize)
return pHeader->dwArchiveSize;
+ // If both block table and archive size seem to be out of the file size
+ if(pHeader->dwBlockTablePos > FileSize && pHeader->dwArchiveSize > FileSize)
+ return pHeader->dwArchiveSize;
+
// If the archive size in the header is less than real file size
dwArchiveSize32 = (DWORD)(FileSize - MpqOffset);
if(pHeader->dwArchiveSize <= dwArchiveSize32)
diff --git a/src/SBaseSubTypes.cpp b/src/SBaseSubTypes.cpp
index 288495f..b44cd9e 100644
--- a/src/SBaseSubTypes.cpp
+++ b/src/SBaseSubTypes.cpp
@@ -441,7 +441,7 @@ int ConvertMpkHeaderToFormat4(
// Attempts to search a free hash entry in the hash table being converted.
// The created hash table must always be of nonzero size,
// should have no duplicated items and no deleted entries
-TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD dwStartIndex, DWORD dwName1, DWORD dwName2)
+TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD dwStartIndex)
{
TMPQHash * pHash;
DWORD dwIndex;
@@ -456,7 +456,6 @@ TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD
// We are not expecting to find matching entry in the hash table being built
// We are not expecting to find deleted entry either
pHash = pHashTable + dwIndex;
- assert(pHash->dwName1 != dwName1 || pHash->dwName2 != dwName2);
// If we found a free entry, we need to stop searching
if(pHash->dwBlockIndex == HASH_ENTRY_FREE)
@@ -542,8 +541,8 @@ TMPQHash * LoadMpkHashTable(TMPQArchive * ha)
for(DWORD i = 0; i < dwHashTableSize; i++)
{
// Finds the free hash entry in the hash table
- // We don;t expect any errors here, because we are putting files to empty hash table
- pHash = FindFreeHashEntry(pHashTable, pHeader->dwHashTableSize, pMpkHash[i].dwName1, pMpkHash[i].dwName2, pMpkHash[i].dwName3);
+ // We don't expect any errors here, because we are putting files to empty hash table
+ pHash = FindFreeHashEntry(pHashTable, pHeader->dwHashTableSize, pMpkHash[i].dwName1);
assert(pHash->dwBlockIndex == HASH_ENTRY_FREE);
// Copy the MPK hash entry to the hash table
diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp
index 7e63993..f947d79 100644
--- a/src/SFileAttributes.cpp
+++ b/src/SFileAttributes.cpp
@@ -95,7 +95,7 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
LPBYTE pbAttrPtr = pbAttrFile;
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
DWORD i;
- bool bPatchBitsValid;
+ bool bPatchBitsValid = false;
// Load and verify the header
if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd)
diff --git a/src/StormPort.h b/src/StormPort.h
index 0d98bcb..218d5cf 100644
--- a/src/StormPort.h
+++ b/src/StormPort.h
@@ -168,6 +168,7 @@
#define _tcslen strlen
#define _tcscpy strcpy
#define _tcscat strcat
+ #define _tcschr strchr
#define _tcsrchr strrchr
#define _tcsstr strstr
#define _tprintf printf
diff --git a/stormlib_dll/StormLib.def b/stormlib_dll/StormLib.def
index 0ba9188..a85f942 100644
--- a/stormlib_dll/StormLib.def
+++ b/stormlib_dll/StormLib.def
@@ -13,7 +13,6 @@ EXPORTS
SFileOpenArchive
SFileCreateArchive
- SFileGetArchiveBitmap
SFileFlushArchive
SFileCloseArchive
diff --git a/test/Test.cpp b/test/Test.cpp
index 6f27f1e..4dce839 100644
--- a/test/Test.cpp
+++ b/test/Test.cpp
@@ -28,6 +28,10 @@
#pragma comment(lib, "winmm.lib")
#endif
+#ifdef PLATFORM_LINUX
+#include <dirent.h>
+#endif
+
//------------------------------------------------------------------------------
// Defines
@@ -393,13 +397,15 @@ static TFileStream * FileStream_CreateFileA(const char * szFileName, DWORD dwStr
static size_t FileStream_PrefixA(const char * szFileName, DWORD * pdwProvider)
{
TCHAR szFileNameT[MAX_PATH];
+ size_t nPrefixLength = 0;
if(szFileName != NULL)
{
CopyFileName(szFileNameT, szFileName, strlen(szFileName));
- return FileStream_Prefix(szFileNameT, pdwProvider);
+ nPrefixLength = FileStream_Prefix(szFileNameT, pdwProvider);
}
- return 0;
+
+ return nPrefixLength;
}
static void CreateFullPathName(char * szBuffer, const char * szSubDir, const char * szNamePart1, const char * szNamePart2 = NULL)
@@ -491,57 +497,205 @@ static void CreateFullPathName(char * szBuffer, const char * szSubDir, const cha
*szBuffer = 0;
}
-static int FindFilesInternal(FIND_FILE_CALLBACK pfnTest, TCHAR * szDirectoryT, char * szDirectoryA)
+static int CalculateFileSha1(TLogHelper * pLogger, const char * szFullPath, char * szFileSha1)
{
+ TFileStream * pStream;
+ unsigned char sha1_digest[SHA1_DIGEST_SIZE];
+ const char * szShortPlainName = GetShortPlainName(szFullPath);
+ hash_state sha1_state;
+ ULONGLONG ByteOffset = 0;
+ ULONGLONG FileSize = 0;
+ BYTE * pbFileBlock;
+ DWORD cbBytesToRead;
+ DWORD cbFileBlock = 0x100000;
int nError = ERROR_SUCCESS;
- if(szDirectoryT != NULL && szDirectoryA != NULL)
+ // Notify the user
+ pLogger->PrintProgress("Hashing file %s", szShortPlainName);
+ szFileSha1[0] = 0;
+
+ // Open the file to be verified
+ pStream = FileStream_OpenFileA(szFullPath, STREAM_FLAG_READ_ONLY);
+ if(pStream != NULL)
{
+ // Retrieve the size of the file
+ FileStream_GetSize(pStream, &FileSize);
+
+ // Allocate the buffer for loading file parts
+ pbFileBlock = STORM_ALLOC(BYTE, cbFileBlock);
+ if(pbFileBlock != NULL)
+ {
+ // Initialize SHA1 calculation
+ sha1_init(&sha1_state);
+
+ // Calculate the SHA1 of the file
+ while(ByteOffset < FileSize)
+ {
+ // Notify the user
+ pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize);
+
+ // Load the file block
+ cbBytesToRead = ((FileSize - ByteOffset) > cbFileBlock) ? cbFileBlock : (DWORD)(FileSize - ByteOffset);
+ if(!FileStream_Read(pStream, &ByteOffset, pbFileBlock, cbBytesToRead))
+ {
+ nError = GetLastError();
+ break;
+ }
+
+ // Add to SHA1
+ sha1_process(&sha1_state, pbFileBlock, cbBytesToRead);
+ ByteOffset += cbBytesToRead;
+ }
+
+ // Notify the user
+ pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize);
+
+ // Finalize SHA1
+ sha1_done(&sha1_state, sha1_digest);
+
+ // Convert the SHA1 to ANSI text
+ ConvertSha1ToText(sha1_digest, szFileSha1);
+ STORM_FREE(pbFileBlock);
+ }
+
+ FileStream_Close(pStream);
+ }
+
+ // If we calculated something, return OK
+ if(nError == ERROR_SUCCESS && szFileSha1[0] == 0)
+ nError = ERROR_CAN_NOT_COMPLETE;
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Directory search
+
+static HANDLE InitDirectorySearch(const char * szDirectory)
+{
#ifdef PLATFORM_WINDOWS
- WIN32_FIND_DATA wf;
- HANDLE hFind;
- TCHAR * szPlainNameT;
- char * szPlainNameA;
- size_t nLength = strlen(szDirectoryA);
- // Setup the search masks
- _tcscat(szDirectoryT, _T("\\*"));
- szPlainNameT = szDirectoryT + nLength + 1;
+ WIN32_FIND_DATA wf;
+ HANDLE hFind;
+ TCHAR szSearchMask[MAX_PATH];
- strcat(szDirectoryA, "\\*");
- szPlainNameA = szDirectoryA + nLength + 1;
+ // Keep compilers happy
+ CopyFileName(szSearchMask, szDirectory, strlen(szDirectory));
+ _tcscat(szSearchMask, _T("\\*"));
- // Initiate search. Use ANSI function only
- hFind = FindFirstFile(szDirectoryT, &wf);
- if(hFind != INVALID_HANDLE_VALUE)
+ // Construct the directory mask
+ hFind = FindFirstFile(szSearchMask, &wf);
+ return (hFind != INVALID_HANDLE_VALUE) ? hFind : NULL;
+
+#endif
+
+#ifdef PLATFORM_LINUX
+
+ // Keep compilers happy
+ return (HANDLE)opendir(szDirectory);
+
+#endif
+}
+
+static bool SearchDirectory(HANDLE hFind, char * szDirEntry, bool & IsDirectory)
+{
+#ifdef PLATFORM_WINDOWS
+
+ WIN32_FIND_DATA wf;
+ TCHAR szDirEntryT[MAX_PATH];
+ char szDirEntryA[MAX_PATH];
+
+ __SearchNextEntry:
+
+ // Search for the hnext entry.
+ if(FindNextFile(hFind, &wf))
+ {
+ // Verify if the directory entry is an UNICODE name that would be destroyed
+ // by Unicode->ANSI->Unicode conversion
+ if(CopyStringAndVerifyConversion(wf.cFileName, szDirEntryT, szDirEntryA) == false)
+ goto __SearchNextEntry;
+
+ IsDirectory = (wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
+ CopyFileName(szDirEntry, wf.cFileName, _tcslen(wf.cFileName));
+ return true;
+ }
+
+ return false;
+
+#endif
+
+#ifdef PLATFORM_LINUX
+
+ struct dirent * directory_entry;
+
+ directory_entry = readdir((DIR *)hFind);
+ if(directory_entry != NULL)
+ {
+ IsDirectory = (directory_entry->d_type == DT_DIR) ? true : false;
+ strcpy(szDirEntry, directory_entry->d_name);
+ return true;
+ }
+
+ return false;
+
+#endif
+}
+
+static void FreeDirectorySearch(HANDLE hFind)
+{
+#ifdef PLATFORM_WINDOWS
+ FindClose(hFind);
+#endif
+
+#ifdef PLATFORM_LINUX
+ closedir((DIR *)hFind);
+#endif
+}
+
+static int FindFilesInternal(FIND_FILE_CALLBACK pfnTest, char * szDirectory)
+{
+ HANDLE hFind;
+ char * szPlainName;
+ size_t nLength;
+ char szDirEntry[MAX_PATH];
+ bool IsDirectory = false;
+ int nError = ERROR_SUCCESS;
+
+ if(szDirectory != NULL)
+ {
+ // Initiate directory search
+ hFind = InitDirectorySearch(szDirectory);
+ if(hFind != NULL)
{
+ // Append slash at the end of the directory name
+ nLength = strlen(szDirectory);
+ szDirectory[nLength++] = PATH_SEPARATOR;
+ szPlainName = szDirectory + nLength;
+
// Skip the first entry, since it's always "." or ".."
- while(FindNextFile(hFind, &wf) && nError == ERROR_SUCCESS)
+ while(SearchDirectory(hFind, szDirEntry, IsDirectory) && nError == ERROR_SUCCESS)
{
- // If the file name can be converted to UNICODE and back
- if(CopyStringAndVerifyConversion(wf.cFileName, szPlainNameT, szPlainNameA))
+ // Copy the directory entry name to both names
+ strcpy(szPlainName, szDirEntry);
+
+ // Found a directory?
+ if(IsDirectory)
{
- // Found a directory?
- if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ if(szDirEntry[0] != '.')
{
- if(wf.cFileName[0] != '.')
- {
- nError = FindFilesInternal(pfnTest, szDirectoryT, szDirectoryA);
- }
+ nError = FindFilesInternal(pfnTest, szDirectory);
}
- else
+ }
+ else
+ {
+ if(pfnTest != NULL)
{
- if(pfnTest != NULL)
- {
- nError = pfnTest(szDirectoryA);
- }
+ nError = pfnTest(szDirectory);
}
}
}
- FindClose(hFind);
+ FreeDirectorySearch(hFind);
}
-#endif
}
// Free the path buffer, if any
@@ -550,12 +704,10 @@ static int FindFilesInternal(FIND_FILE_CALLBACK pfnTest, TCHAR * szDirectoryT, c
static int FindFiles(FIND_FILE_CALLBACK pfnFindFile, const char * szSubDirectory)
{
- TCHAR szWorkBuffT[MAX_PATH];
- char szWorkBuffA[MAX_PATH];
+ char szWorkBuff[MAX_PATH];
- CreateFullPathName(szWorkBuffA, szSubDirectory, NULL);
- CopyFileName(szWorkBuffT, szWorkBuffA, strlen(szWorkBuffA));
- return FindFilesInternal(pfnFindFile, szWorkBuffT, szWorkBuffA);
+ CreateFullPathName(szWorkBuff, szSubDirectory, NULL);
+ return FindFilesInternal(pfnFindFile, szWorkBuff);
}
static int FindFilePairsInternal(
@@ -627,76 +779,6 @@ static int FindFilePairs(FIND_PAIR_CALLBACK pfnFindPair, const char * szSourceSu
return FindFilePairsInternal(pfnFindPair, szSource, szTarget);
}
-static int CalculateFileSha1(TLogHelper * pLogger, const char * szFullPath, char * szFileSha1)
-{
- TFileStream * pStream;
- unsigned char sha1_digest[SHA1_DIGEST_SIZE];
- const char * szShortPlainName = GetShortPlainName(szFullPath);
- hash_state sha1_state;
- ULONGLONG ByteOffset = 0;
- ULONGLONG FileSize = 0;
- BYTE * pbFileBlock;
- DWORD cbBytesToRead;
- DWORD cbFileBlock = 0x100000;
- int nError = ERROR_SUCCESS;
-
- // Notify the user
- pLogger->PrintProgress("Hashing file %s", szShortPlainName);
- szFileSha1[0] = 0;
-
- // Open the file to be verified
- pStream = FileStream_OpenFileA(szFullPath, STREAM_FLAG_READ_ONLY);
- if(pStream != NULL)
- {
- // Retrieve the size of the file
- FileStream_GetSize(pStream, &FileSize);
-
- // Allocate the buffer for loading file parts
- pbFileBlock = STORM_ALLOC(BYTE, cbFileBlock);
- if(pbFileBlock != NULL)
- {
- // Initialize SHA1 calculation
- sha1_init(&sha1_state);
-
- // Calculate the SHA1 of the file
- while(ByteOffset < FileSize)
- {
- // Notify the user
- pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize);
-
- // Load the file block
- cbBytesToRead = ((FileSize - ByteOffset) > cbFileBlock) ? cbFileBlock : (DWORD)(FileSize - ByteOffset);
- if(!FileStream_Read(pStream, &ByteOffset, pbFileBlock, cbBytesToRead))
- {
- nError = GetLastError();
- break;
- }
-
- // Add to SHA1
- sha1_process(&sha1_state, pbFileBlock, cbBytesToRead);
- ByteOffset += cbBytesToRead;
- }
-
- // Notify the user
- pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize);
-
- // Finalize SHA1
- sha1_done(&sha1_state, sha1_digest);
-
- // Convert the SHA1 to ANSI text
- ConvertSha1ToText(sha1_digest, szFileSha1);
- STORM_FREE(pbFileBlock);
- }
-
- FileStream_Close(pStream);
- }
-
- // If we calculated something, return OK
- if(nError == ERROR_SUCCESS && szFileSha1[0] == 0)
- nError = ERROR_CAN_NOT_COMPLETE;
- return nError;
-}
-
static int InitializeMpqDirectory(char * argv[], int argc)
{
TLogHelper Logger("InitWorkDir");
@@ -1033,11 +1115,12 @@ static int CreateFileCopy(
int nError = ERROR_SUCCESS;
// Notify the user
+ szPlainName += FileStream_PrefixA(szPlainName, NULL);
pLogger->PrintProgress("Creating copy of %s ...", szPlainName);
// Construct both file names. Check if they are not the same
CreateFullPathName(szFileName1, szMpqSubDir, szPlainName);
- CreateFullPathName(szFileName2, NULL, szFileCopy);
+ CreateFullPathName(szFileName2, NULL, szFileCopy + FileStream_PrefixA(szFileCopy, NULL));
if(!_stricmp(szFileName1, szFileName2))
{
pLogger->PrintError("Failed to create copy of MPQ (the copy name is the same like the original name)");
@@ -1087,7 +1170,7 @@ static int CreateFileCopy(
FileStream_Close(pStream1);
if(szBuffer != NULL)
- strcpy(szBuffer, szFileName2);
+ CreateFullPathName(szBuffer, NULL, szFileCopy);
if(nError != ERROR_SUCCESS)
pLogger->PrintError("Failed to create copy of MPQ");
return nError;
@@ -1104,23 +1187,18 @@ static int CreateMasterAndMirrorPaths(
char szCopyPath[MAX_PATH];
int nError = ERROR_SUCCESS;
+ // Always delete the mirror file
+ CreateFullPathName(szMasterPath, szMpqSubDir, szMasterName);
+ CreateFullPathName(szCopyPath, NULL, szMirrorName);
+ remove(szCopyPath + FileStream_PrefixA(szCopyPath, NULL));
+
// Copy the mirrored file from the source to the work directory
if(bCopyMirrorFile)
- nError = CreateFileCopy(pLogger, szMirrorName, szMirrorName, szCopyPath);
- else
- {
- CreateFullPathName(szCopyPath, NULL, szMirrorName);
- remove(szCopyPath);
- }
+ nError = CreateFileCopy(pLogger, szMirrorName, szMirrorName, NULL);
+ // Create the mirror*master path
if(nError == ERROR_SUCCESS)
- {
- // Create the full path name of the master file
- CreateFullPathName(szMasterPath, szMpqSubDir, szMasterName);
-
- // Create the full path name of the mirror file
sprintf(szMirrorPath, "%s*%s", szCopyPath, szMasterPath);
- }
return nError;
}
@@ -1314,7 +1392,7 @@ static int CompareTwoLocalFilesRR(
if(!CompareBlocks(pbBuffer1, pbBuffer2, BytesToRead, &Difference))
{
- pLogger->PrintMessage("Difference at %u (Offset " I64u_a ", Length %u)", Difference, ByteOffset, BytesToRead);
+ pLogger->PrintMessage("Difference at %u (Offset " I64X_a ", Length %X)", Difference, ByteOffset, BytesToRead);
nError = ERROR_FILE_CORRUPT;
break;
}
@@ -3351,8 +3429,8 @@ int main(int argc, char * argv[])
// nError = FindFilePairs(ForEachFile_CreateArchiveLink, "2004 - WoW\\06080", "2004 - WoW\\06299");
// Search all testing archives and verify their SHA1 hash
-// if(nError == ERROR_SUCCESS)
-// nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqDirectory);
+ if(nError == ERROR_SUCCESS)
+ nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqSubDir);
// Test reading linear file without bitmap
if(nError == ERROR_SUCCESS)
@@ -3378,6 +3456,18 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = TestFileStreamOperations("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE", STREAM_PROVIDER_MPQE);
+ // Open a stream, paired with local master. The mirror file is created new
+ if(nError == ERROR_SUCCESS)
+ nError = TestReadFile_MasterMirror("part-file://MPQ_2009_v1_patch-created.MPQ.part", "MPQ_2009_v1_patch-original.MPQ", false);
+
+ // Open a stream, paired with local master. Only part of the mirror exists
+ if(nError == ERROR_SUCCESS)
+ nError = TestReadFile_MasterMirror("part-file://MPQ_2009_v1_patch-partial.MPQ.part", "MPQ_2009_v1_patch-original.MPQ", true);
+
+ // Open a stream, paired with local master. Only part of the mirror exists
+ if(nError == ERROR_SUCCESS)
+ nError = TestReadFile_MasterMirror("part-file://MPQ_2009_v1_patch-complete.MPQ.part", "MPQ_2009_v1_patch-original.MPQ", true);
+
// Open a stream, paired with local master
if(nError == ERROR_SUCCESS)
nError = TestReadFile_MasterMirror("MPQ_2013_v4_alternate-created.MPQ", "MPQ_2013_v4_alternate-original.MPQ", false);
@@ -3500,6 +3590,10 @@ int main(int argc, char * argv[])
// Downloadable MPQ archive
if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive_MasterMirror("part-file://MPQ_2009_v1_patch-partial.MPQ.part", "MPQ_2009_v1_patch-original.MPQ", "world\\Azeroth\\DEADMINES\\PASSIVEDOODADS\\GOBLINMELTINGPOT\\DUST2.BLP", false);
+
+ // Downloadable MPQ archive
+ if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_MasterMirror("MPQ_2013_v4_alternate-downloaded.MPQ", "MPQ_2013_v4_alternate-original.MPQ", "alternate\\DUNGEONS\\TEXTURES\\ICECROWN\\GATE\\jlo_IceC_Floor_Thrown.blp", false);
// Check archive signature