summaryrefslogtreecommitdiff
path: root/StormLib/src/SFileCreateArchive.cpp
diff options
context:
space:
mode:
authorNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-01 16:05:05 -0400
committerNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-16 20:35:52 -0400
commitb3e299dbdee829c12ada556a1c3aa1f109a0e05b (patch)
treea6e647da673ba7df9389cb1f382a11fc8350bc23 /StormLib/src/SFileCreateArchive.cpp
parent941c19f2c33a0d1af75a10a64fe0905387d1e440 (diff)
Replaced StormLib .lib files with source code.
Some modifications to handle backslashes and forward slashes, along with some optimizations to speed up OTR generation.
Diffstat (limited to 'StormLib/src/SFileCreateArchive.cpp')
-rw-r--r--StormLib/src/SFileCreateArchive.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/StormLib/src/SFileCreateArchive.cpp b/StormLib/src/SFileCreateArchive.cpp
new file mode 100644
index 000000000..6f398add5
--- /dev/null
+++ b/StormLib/src/SFileCreateArchive.cpp
@@ -0,0 +1,276 @@
+/*****************************************************************************/
+/* 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->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);
+}