summaryrefslogtreecommitdiff
path: root/lib/StormLib/src/SFileCreateArchive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StormLib/src/SFileCreateArchive.cpp')
-rw-r--r--lib/StormLib/src/SFileCreateArchive.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/lib/StormLib/src/SFileCreateArchive.cpp b/lib/StormLib/src/SFileCreateArchive.cpp
new file mode 100644
index 0000000..4facb47
--- /dev/null
+++ b/lib/StormLib/src/SFileCreateArchive.cpp
@@ -0,0 +1,277 @@
+/*****************************************************************************/
+/* SFileCreateArchive.cpp Copyright (c) Ladislav Zezula 2003 */
+/*---------------------------------------------------------------------------*/
+/* MPQ Editing functions */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.03.03 1.00 Lad Splitted from SFileOpenArchive.cpp */
+/* 08.06.10 1.00 Lad Renamed to SFileCreateArchive.cpp */
+/*****************************************************************************/
+
+#define __STORMLIB_SELF__
+#include "StormLib.h"
+#include "StormCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+static const DWORD MpqHeaderSizes[] =
+{
+ MPQ_HEADER_SIZE_V1,
+ MPQ_HEADER_SIZE_V2,
+ MPQ_HEADER_SIZE_V3,
+ MPQ_HEADER_SIZE_V4
+};
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static USHORT GetSectorSizeShift(DWORD dwSectorSize)
+{
+ USHORT wSectorSizeShift = 0;
+
+ while(dwSectorSize > 0x200)
+ {
+ dwSectorSize >>= 1;
+ wSectorSizeShift++;
+ }
+
+ return wSectorSizeShift;
+}
+
+static DWORD WriteNakedMPQHeader(TMPQArchive * ha)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ TMPQHeader Header;
+ DWORD dwBytesToWrite = pHeader->dwHeaderSize;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Prepare the naked MPQ header
+ memset(&Header, 0, sizeof(TMPQHeader));
+ Header.dwID = pHeader->dwID;
+ Header.dwHeaderSize = pHeader->dwHeaderSize;
+ Header.dwArchiveSize = pHeader->dwHeaderSize;
+ Header.wFormatVersion = pHeader->wFormatVersion;
+ Header.wSectorSize = pHeader->wSectorSize;
+
+ // Write it to the file
+ BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_1);
+ BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_2);
+ BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_3);
+ BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_4);
+ if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
+ dwErrCode = GetLastError();
+
+ return dwErrCode;
+}
+
+//-----------------------------------------------------------------------------
+// Creates a new MPQ archive.
+
+bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
+{
+ SFILE_CREATE_MPQ CreateInfo;
+
+ // Fill the create structure
+ memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
+ CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
+ CreateInfo.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
+ CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE;
+ CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
+ CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
+ CreateInfo.dwFileFlags3 = (dwCreateFlags & MPQ_CREATE_SIGNATURE) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
+ CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0;
+ CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
+ CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
+ CreateInfo.dwMaxFileCount = dwMaxFileCount;
+
+ // Set the proper attribute parts
+ if((CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) && (dwCreateFlags & MPQ_CREATE_ATTRIBUTES))
+ CreateInfo.dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
+
+ // Backward compatibility: SFileCreateArchive always used to add (listfile)
+ // We would break loads of applications if we change that
+ CreateInfo.dwFileFlags1 = MPQ_FILE_DEFAULT_INTERNAL;
+
+ // Let the main function create the archive
+ return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
+}
+
+bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
+{
+ TFileStream * pStream = NULL; // File stream
+ TMPQArchive * ha = NULL; // MPQ archive handle
+ TMPQHeader * pHeader;
+ ULONGLONG MpqPos = 0; // Position of MPQ header in the file
+ HANDLE hMpq = NULL;
+ DWORD dwBlockTableSize = 0; // Initial block table size
+ DWORD dwHashTableSize = 0;
+ DWORD dwReservedFiles = 0; // Number of reserved file entries
+ DWORD dwMpqFlags = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Check the parameters, if they are valid
+ if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Verify if all variables in SFILE_CREATE_MPQ are correct
+ if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
+ (pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
+ (pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
+ (pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
+ (pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
+ (pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // One time initialization of MPQ cryptography
+ InitializeMpqCryptography();
+
+ // 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_FLAT | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
+ {
+ SFileCloseArchive(hMpq);
+ SetLastError(ERROR_ALREADY_EXISTS);
+ return false;
+ }
+
+ //
+ // At this point, we have to create the archive.
+ // - If the file exists, convert it to MPQ archive.
+ // - If the file doesn't exist, create new empty file
+ //
+
+ pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
+ if(pStream == NULL)
+ {
+ pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
+ if(pStream == NULL)
+ return false;
+ }
+
+ // Increment the maximum amount of files to have space for (listfile)
+ if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1)
+ {
+ dwMpqFlags |= MPQ_FLAG_LISTFILE_NEW;
+ dwReservedFiles++;
+ }
+
+ // Increment the maximum amount of files to have space for (attributes)
+ if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags2 && pCreateInfo->dwAttrFlags)
+ {
+ dwMpqFlags |= MPQ_FLAG_ATTRIBUTES_NEW;
+ dwReservedFiles++;
+ }
+
+ // Increment the maximum amount of files to have space for (signature)
+ if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags3)
+ {
+ dwMpqFlags |= MPQ_FLAG_SIGNATURE_NEW;
+ dwReservedFiles++;
+ }
+
+ // If file count is not zero, initialize the hash table size
+ dwHashTableSize = GetNearestPowerOfTwo(pCreateInfo->dwMaxFileCount + dwReservedFiles);
+
+ // Retrieve the file size and round it up to 0x200 bytes
+ FileStream_GetSize(pStream, &MpqPos);
+ MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL;
+ if(!FileStream_SetSize(pStream, MpqPos))
+ dwErrCode = GetLastError();
+
+#ifdef _DEBUG
+ // Debug code, used for testing StormLib
+// dwBlockTableSize = dwHashTableSize * 2;
+#endif
+
+ // Create the archive handle
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Fill the MPQ archive handle structure
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ memset(ha, 0, sizeof(TMPQArchive));
+ ha->pfnHashString = HashStringSlash;
+ ha->pStream = pStream;
+ ha->dwSectorSize = pCreateInfo->dwSectorSize;
+ ha->UserDataPos = MpqPos;
+ ha->MpqPos = MpqPos;
+ ha->pHeader = pHeader = (TMPQHeader *)ha->HeaderData;
+ ha->dwMaxFileCount = dwHashTableSize;
+ ha->dwFileTableSize = 0;
+ ha->dwReservedFiles = dwReservedFiles;
+ ha->dwValidFileFlags = (pCreateInfo->dwMpqVersion > 1) ? MPQ_FILE_VALID_FLAGS : MPQ_FILE_VALID_FLAGS_W3X;
+ ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
+ ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
+ ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0;
+ ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
+ ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED;
+ ha->useFreeSpaceOptimization = true;
+ ha->lastFreeSpaceEntry = nullptr;
+ pStream = NULL;
+
+ // Fill the MPQ header
+ memset(pHeader, 0, sizeof(ha->HeaderData));
+ pHeader->dwID = g_dwMpqSignature;
+ pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
+ pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
+ pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
+ pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
+ pHeader->dwHashTablePos = pHeader->dwHeaderSize;
+ pHeader->dwHashTableSize = dwHashTableSize;
+ pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash);
+ pHeader->dwBlockTableSize = dwBlockTableSize;
+
+ // For MPQs version 4 and higher, we set the size of raw data block
+ // for calculating MD5
+ if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
+ pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
+
+ // Write the naked MPQ header
+ dwErrCode = WriteNakedMPQHeader(ha);
+ }
+
+ // Create initial HET table, if the caller required an MPQ format 3.0 or newer
+ if(dwErrCode == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3 && pCreateInfo->dwMaxFileCount != 0)
+ {
+ ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL);
+ if(ha->pHetTable == NULL)
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Create initial hash table
+ if(dwErrCode == ERROR_SUCCESS && dwHashTableSize != 0)
+ {
+ dwErrCode = CreateHashTable(ha, dwHashTableSize);
+ }
+
+ // Create initial file table
+ if(dwErrCode == ERROR_SUCCESS && ha->dwMaxFileCount != 0)
+ {
+ dwErrCode = CreateFileTable(ha, ha->dwMaxFileCount);
+ }
+
+ // Cleanup : If an error, delete all buffers and return
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ FileStream_Close(pStream);
+ FreeArchiveHandle(ha);
+ SetLastError(dwErrCode);
+ ha = NULL;
+ }
+
+ // Return the values
+ *phMpq = (HANDLE)ha;
+ return (dwErrCode == ERROR_SUCCESS);
+}