📄 scommon.cpp.svn-base
字号:
}
// Get pointers to both block entries of the file
pBlockEx = ha->pExtBlockTable + pHash->dwBlockIndex;
pBlock = ha->pBlockTable + pHash->dwBlockIndex;
// Save the file size info
pBlockEx->wFilePosHigh = (USHORT)RelativePos.HighPart;
pBlock->dwFilePos = RelativePos.LowPart;
pBlock->dwFSize = GetFileSize(hFile, NULL);
pBlock->dwFlags = dwFlags | MPQ_FILE_EXISTS;
// Create seed1 for file encryption
if(szTemp != NULL)
szArchivedName = szTemp + 1;
if(dwFlags & MPQ_FILE_ENCRYPTED)
{
dwSeed1 = DecryptFileSeed(szArchivedName);
if(dwFlags & MPQ_FILE_FIXSEED)
dwSeed1 = (dwSeed1 + pBlock->dwFilePos) ^ pBlock->dwFSize;
}
}
// Allocate buffer for the input data
if(nError == ERROR_SUCCESS)
{
nBlocks = (pBlock->dwFSize / ha->dwBlockSize) + 1;
if(pBlock->dwFSize % ha->dwBlockSize)
nBlocks++;
pBlock->dwCSize = 0;
if((pbFileData = ALLOCMEM(BYTE, ha->dwBlockSize)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
pbToWrite = pbFileData;
}
// Allocate buffers for the compressed data
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_COMPRESSED))
{
pdwBlockPos = ALLOCMEM(DWORD, nBlocks + 1);
pbCompressed = ALLOCMEM(BYTE, ha->dwBlockSize * 2);
if(pdwBlockPos == NULL || pbCompressed == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
pbToWrite = pbCompressed;
}
// Set the file position to the point where the file will be stored
if(nError == ERROR_SUCCESS)
{
// Set the file pointer to file data position
FilePos.QuadPart = ha->MpqPos.QuadPart + RelativePos.QuadPart;
if(FilePos.QuadPart != ha->FilePointer.QuadPart)
{
SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN);
ha->FilePointer = FilePos;
}
}
// Write block positions (if the file will be compressed)
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_COMPRESSED))
{
dwBlockPosLen = nBlocks * sizeof(DWORD);
if(dwFlags & MPQ_FILE_HAS_EXTRA)
dwBlockPosLen += sizeof(DWORD);
memset(pdwBlockPos, 0, dwBlockPosLen);
pdwBlockPos[0] = dwBlockPosLen;
// Write the block positions
BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks);
WriteFile(ha->hFile, pdwBlockPos, dwBlockPosLen, &dwTransferred, NULL);
BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks);
if(dwTransferred == dwBlockPosLen)
pBlock->dwCSize += dwBlockPosLen;
else
nError = GetLastError();
// Update the current position in the file
ha->HashTablePos.QuadPart = FilePos.QuadPart + dwTransferred;
}
// Write all file blocks
if(nError == ERROR_SUCCESS)
{
nCmp = nCmpFirst;
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
for(nBlock = 0; nBlock < nBlocks-1; nBlock++)
{
DWORD dwInLength = ha->dwBlockSize;
DWORD dwOutLength = ha->dwBlockSize;
// Load the block from the file
ReadFile(hFile, pbFileData, ha->dwBlockSize, &dwInLength, NULL);
if(dwInLength == 0)
break;
// Compress the block, if necessary
dwOutLength = dwInLength;
if(pBlock->dwFlags & MPQ_FILE_COMPRESSED)
{
// Should be enough for compression
int nOutLength = ha->dwBlockSize * 2;
int nCmpType = 0;
if(pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE)
Compress_pklib((char *)pbCompressed, &nOutLength, (char *)pbFileData, dwInLength, &nCmpType, 0);
if(pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI)
SCompCompress((char *)pbCompressed, &nOutLength, (char *)pbFileData, dwInLength, nCmp, 0, nCmpLevel);
// The compressed block size must NOT be the same or greater like
// the original block size. If yes, do not compress the block
// and store the data as-is.
if(nOutLength >= (int)dwInLength)
{
memcpy(pbCompressed, pbFileData, dwInLength);
nOutLength = dwInLength;
}
// Update block positions
dwOutLength = nOutLength;
pdwBlockPos[nBlock+1] = pdwBlockPos[nBlock] + dwOutLength;
nCmp = nCmpNext;
}
// Encrypt the block, if necessary
if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED)
{
BSWAP_ARRAY32_UNSIGNED((DWORD *)pbToWrite, dwOutLength / sizeof(DWORD));
EncryptMPQBlock((DWORD *)pbToWrite, dwOutLength, dwSeed1 + nBlock);
BSWAP_ARRAY32_UNSIGNED((DWORD *)pbToWrite, dwOutLength / sizeof(DWORD));
}
// Write the block
WriteFile(ha->hFile, pbToWrite, dwOutLength, &dwTransferred, NULL);
if(dwTransferred != dwOutLength)
{
nError = ERROR_DISK_FULL;
break;
}
// Update the hash table position and the compressed file size
ha->HashTablePos.QuadPart += dwTransferred;
pBlock->dwCSize += dwOutLength;
}
}
// Now save the block positions
if(nError == ERROR_SUCCESS && (pBlock->dwFlags & MPQ_FILE_COMPRESSED))
{
if(dwFlags & MPQ_FILE_HAS_EXTRA)
pdwBlockPos[nBlocks] = pdwBlockPos[nBlocks-1];
// If file is encrypted, block positions are also encrypted
if(dwFlags & MPQ_FILE_ENCRYPTED)
EncryptMPQBlock(pdwBlockPos, dwBlockPosLen, dwSeed1 - 1);
// Set the position back to the block table
SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN);
// Write block positions to the archive
BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks);
WriteFile(ha->hFile, pdwBlockPos, dwBlockPosLen, &dwTransferred, NULL);
if(dwTransferred != dwBlockPosLen)
nError = ERROR_DISK_FULL;
ha->FilePointer.QuadPart = ha->FilePointer.QuadPart + dwTransferred;
}
// If success, we have to change the settings
// in MPQ header. If failed, we have to clean hash entry
if(nError == ERROR_SUCCESS)
{
ha->pLastFile = NULL;
ha->dwBlockPos = 0;
ha->dwBuffPos = 0;
ha->dwFlags |= MPQ_FLAG_CHANGED;
// Add new entry to the block table (if needed)
if(pHash->dwBlockIndex >= ha->pHeader->dwBlockTableSize)
ha->pHeader->dwBlockTableSize++;
// Hash table size in the TMPQArchive is already set at this point
RelativePos.QuadPart = ha->HashTablePos.QuadPart - ha->MpqPos.QuadPart;
ha->pHeader->dwHashTablePos = RelativePos.LowPart;
ha->pHeader->wHashTablePosHigh = (USHORT)RelativePos.HighPart;
// Update block table pos
RelativePos.QuadPart += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
ha->pHeader->wBlockTablePosHigh = (USHORT)RelativePos.HighPart;
ha->pHeader->dwBlockTablePos = RelativePos.LowPart;
ha->BlockTablePos.QuadPart = RelativePos.QuadPart + ha->MpqPos.QuadPart;
// If the archive size exceeded 4GB, we have to use extended block table pos
RelativePos.QuadPart += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
if(RelativePos.HighPart != 0)
{
ha->pHeader->ExtBlockTablePos = RelativePos;
ha->ExtBlockTablePos.QuadPart = RelativePos.QuadPart + ha->MpqPos.QuadPart;
RelativePos.QuadPart += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx));
}
// Update archive size (only valid for version V1)
ha->MpqSize = RelativePos;
ha->pHeader->dwArchiveSize = ha->MpqSize.LowPart;
}
else
{
// Clear the hash table entry
if(pHash != NULL)
memset(pHash, 0xFF, sizeof(TMPQHash));
}
// Cleanup
if(pbCompressed != NULL)
FREEMEM(pbCompressed);
if(pdwBlockPos != NULL)
FREEMEM(pdwBlockPos);
if(pbFileData != NULL)
FREEMEM(pbFileData);
if(pbReplaced != NULL)
*pbReplaced = bReplaced;
return nError;
}
int SetDataCompression(int nDataCompression)
{
nDataCmp = nDataCompression;
return 0;
}
// This method saves MPQ header, hash table and block table.
// TODO: Test for archives > 4GB
int SaveMPQTables(TMPQArchive * ha)
{
BYTE * pbBuffer = NULL;
DWORD dwBytes;
DWORD dwWritten;
DWORD dwBuffSize = max(ha->pHeader->dwHashTableSize, ha->pHeader->dwBlockTableSize);
int nError = ERROR_SUCCESS;
// Allocate buffer for encrypted tables
if(nError == ERROR_SUCCESS)
{
// Allocate temporary buffer for tables encryption
pbBuffer = ALLOCMEM(BYTE, sizeof(TMPQHash) * dwBuffSize);
if(pbBuffer == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Write the MPQ Header
if(nError == ERROR_SUCCESS)
{
DWORD dwHeaderSize = ha->pHeader->dwHeaderSize;
// Write the MPQ header
SetFilePointer(ha->hFile, ha->MpqPos.LowPart, &ha->MpqPos.HighPart, FILE_BEGIN);
// Convert to little endian for file save
BSWAP_TMPQHEADER(ha->pHeader);
WriteFile(ha->hFile, ha->pHeader, dwHeaderSize, &dwWritten, NULL);
BSWAP_TMPQHEADER(ha->pHeader);
if(dwWritten != ha->pHeader->dwHeaderSize)
nError = ERROR_DISK_FULL;
}
// Write the hash table
if(nError == ERROR_SUCCESS)
{
// Copy the hash table to temporary buffer
dwBytes = ha->pHeader->dwHashTableSize * sizeof(TMPQHash);
memcpy(pbBuffer, ha->pHashTable, dwBytes);
// Convert to little endian for file save
EncryptHashTable((DWORD *)pbBuffer, (BYTE *)"(hash table)", dwBytes >> 2);
BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBuffer, dwBytes / sizeof(DWORD));
// Set the file pointer to the offset of the hash table and write it
SetFilePointer(ha->hFile, ha->HashTablePos.LowPart, (PLONG)&ha->HashTablePos.HighPart, FILE_BEGIN);
WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL);
if(dwWritten != dwBytes)
nError = ERROR_DISK_FULL;
}
// Write the block table
if(nError == ERROR_SUCCESS)
{
// Copy the block table to temporary buffer
dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock);
memcpy(pbBuffer, ha->pBlockTable, dwBytes);
// Encrypt the block table and write it to the file
EncryptBlockTable((DWORD *)pbBuffer, (BYTE *)"(block table)", dwBytes >> 2);
// Convert to little endian for file save
BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBuffer, dwBytes / sizeof(DWORD));
WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL);
if(dwWritten != dwBytes)
nError = ERROR_DISK_FULL;
}
// Write the extended block table
if(nError == ERROR_SUCCESS && ha->pHeader->ExtBlockTablePos.QuadPart != 0)
{
// We expect format V2 or newer in this case
assert(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2);
// Copy the block table to temporary buffer
dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx);
memcpy(pbBuffer, ha->pExtBlockTable, dwBytes);
// Convert to little endian for file save
BSWAP_ARRAY16_UNSIGNED((USHORT *)pbBuffer, dwBytes / sizeof(USHORT));
WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL);
if(dwWritten != dwBytes)
nError = ERROR_DISK_FULL;
}
// Set end of file here
if(nError == ERROR_SUCCESS)
{
SetEndOfFile(ha->hFile);
}
// Cleanup and exit
if(pbBuffer != NULL)
FREEMEM(pbBuffer);
return nError;
}
// Frees the MPQ archive
// TODO: Test for archives > 4GB
void FreeMPQArchive(TMPQArchive *& ha)
{
if(ha != NULL)
{
FREEMEM(ha->pbBlockBuffer);
FREEMEM(ha->pBlockTable);
FREEMEM(ha->pExtBlockTable);
FREEMEM(ha->pHashTable);
if(ha->pListFile != NULL)
SListFileFreeListFile(ha);
if(ha->hFile != INVALID_HANDLE_VALUE)
CloseHandle(ha->hFile);
FREEMEM(ha);
ha = NULL;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -