bool WINAPI SFileAddFileEx( HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, // Compression of the first sector DWORD dwCompressionNext) // Compression of next sectors { LARGE_INTEGER FileSize = {0}; TMPQFileTime ft = {0, 0}; TFileStream * pStream = NULL; HANDLE hMpqFile = NULL; LPBYTE pbFileData = NULL; DWORD dwBytesRemaining = 0; DWORD dwBytesToRead; DWORD dwSectorSize = 0x1000; int nError = ERROR_SUCCESS; // Check parameters if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; // Open added file if(nError == ERROR_SUCCESS) { pStream = FileStream_OpenFile(szFileName, false); if(pStream == NULL) nError = GetLastError(); } // Get the file size and file time if(nError == ERROR_SUCCESS) { FileStream_GetLastWriteTime(pStream, &ft); FileStream_GetSize(pStream, &FileSize); if(FileSize.HighPart != 0) nError = ERROR_DISK_FULL; } // Allocate data buffer for reading from the source file if(nError == ERROR_SUCCESS) { dwBytesRemaining = FileSize.LowPart; pbFileData = ALLOCMEM(BYTE, dwSectorSize); if(pbFileData == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Deal with various combination of compressions if(nError == ERROR_SUCCESS) { // When the compression for next blocks is set to default, // we will copy the compression for the first sector if(dwCompressionNext == 0xFFFFFFFF) dwCompressionNext = dwCompression; // If the caller wants ADPCM compression, we make sure that the first sector is not // compressed with lossy compression if(dwCompressionNext & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO)) { // The first compression must not be WAVE if(dwCompression & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; } // Initiate adding file to the MPQ if(!SFileCreateFile(hMpq, szArchivedName, &ft, FileSize.LowPart, lcFileLocale, dwFlags, &hMpqFile)) nError = GetLastError(); } // Write the file data to the MPQ while(dwBytesRemaining != 0 && nError == ERROR_SUCCESS) { // Get the number of bytes remaining in the source file dwBytesToRead = dwBytesRemaining; if(dwBytesToRead > dwSectorSize) dwBytesToRead = dwSectorSize; // Read data from the local file if(!FileStream_Read(pStream, NULL, pbFileData, dwBytesToRead)) { nError = GetLastError(); break; } // Add the file sectors to the MPQ if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression)) { nError = GetLastError(); break; } // Set the next data compression dwBytesRemaining -= dwBytesToRead; dwCompression = dwCompressionNext; } // Finish the file writing if(hMpqFile != NULL) { if(!SFileFinishFile(hMpqFile)) nError = GetLastError(); } // Cleanup and exit if(pbFileData != NULL) FREEMEM(pbFileData); if(pStream != NULL) FileStream_Close(pStream); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
bool WINAPI SFileAddFileEx( HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, // Compression of the first sector DWORD dwCompressionNext) // Compression of next sectors { ULONGLONG FileSize = 0; ULONGLONG FileTime = 0; TFileStream * pStream = NULL; HANDLE hMpqFile = NULL; LPBYTE pbFileData = NULL; DWORD dwBytesRemaining = 0; DWORD dwBytesToRead; DWORD dwSectorSize = 0x1000; DWORD dwChannels = 0; bool bIsAdpcmCompression = false; bool bIsFirstSector = true; int nError = ERROR_SUCCESS; // Check parameters if(hMpq == NULL || szFileName == NULL || *szFileName == 0) { SetLastError(ERROR_INVALID_PARAMETER); return false; } // Open added file pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); if(pStream == NULL) return false; // Files bigger than 4GB cannot be added to MPQ FileStream_GetTime(pStream, &FileTime); FileStream_GetSize(pStream, &FileSize); if(FileSize >> 32) nError = ERROR_DISK_FULL; // Allocate data buffer for reading from the source file if(nError == ERROR_SUCCESS) { dwBytesRemaining = (DWORD)FileSize; pbFileData = STORM_ALLOC(BYTE, dwSectorSize); if(pbFileData == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Deal with various combination of compressions if(nError == ERROR_SUCCESS) { // When the compression for next blocks is set to default, // we will copy the compression for the first sector if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME) dwCompressionNext = dwCompression; // If the caller wants ADPCM compression, we make sure // that the first sector is not compressed with lossy compression if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) { // The compression of the first file sector must not be ADPCM // in order not to corrupt the headers if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; // Remove both flag mono and stereo flags. // They will be re-added according to WAVE type dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); bIsAdpcmCompression = true; } // Initiate adding file to the MPQ if(!SFileCreateFile(hMpq, szArchivedName, FileTime, (DWORD)FileSize, lcFileLocale, dwFlags, &hMpqFile)) nError = GetLastError(); } // Write the file data to the MPQ while(nError == ERROR_SUCCESS && dwBytesRemaining != 0) { // Get the number of bytes remaining in the source file dwBytesToRead = dwBytesRemaining; if(dwBytesToRead > dwSectorSize) dwBytesToRead = dwSectorSize; // Read data from the local file if(!FileStream_Read(pStream, NULL, pbFileData, dwBytesToRead)) { nError = GetLastError(); break; } // If the file being added is a WAVE file, we check number of channels if(bIsFirstSector && bIsAdpcmCompression) { // The file must really be a WAVE file with at least 16 bits per sample, // otherwise the ADPCM compression will corrupt it if(IsWaveFile_16BitsPerAdpcmSample(pbFileData, dwBytesToRead, &dwChannels)) { // Setup the compression of next sectors according to number of channels dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; } else { // Setup the compression of next sectors to a lossless compression dwCompressionNext = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? MPQ_COMPRESSION_PKWARE : dwCompression; } bIsFirstSector = false; } // Add the file sectors to the MPQ if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression)) { nError = GetLastError(); break; } // Set the next data compression dwBytesRemaining -= dwBytesToRead; dwCompression = dwCompressionNext; } // Finish the file writing if(hMpqFile != NULL) { if(!SFileFinishFile(hMpqFile)) nError = GetLastError(); } // Cleanup and exit if(pbFileData != NULL) STORM_FREE(pbFileData); if(pStream != NULL) FileStream_Close(pStream); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }