aboutsummaryrefslogtreecommitdiff
path: root/src/SFileReadFile.cpp
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2013-11-13 07:56:31 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2013-11-13 07:56:31 +0100
commita70863499e5e9e6560477b8f4a0a594d6e62650e (patch)
tree21118705bf1d412ed9aea0b51daf25f98b4fabad /src/SFileReadFile.cpp
parent66b71713c2a60e6f6ccc55245b067dad34fd6154 (diff)
+ Support for SQP and MPK archives
+ Makefiles fixed
Diffstat (limited to 'src/SFileReadFile.cpp')
-rw-r--r--src/SFileReadFile.cpp306
1 files changed, 220 insertions, 86 deletions
diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp
index 164b646..5a034b3 100644
--- a/src/SFileReadFile.cpp
+++ b/src/SFileReadFile.cpp
@@ -28,13 +28,6 @@ struct TFileHeader2Ext
//-----------------------------------------------------------------------------
// Local functions
-static void CopyFileName(char * szTarget, const TCHAR * szSource)
-{
- while(*szSource != 0)
- *szTarget++ = (char)*szSource++;
- *szTarget = 0;
-}
-
static DWORD GetMpqFileCount(TMPQArchive * ha)
{
TFileEntry * pFileTableEnd;
@@ -442,7 +435,99 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
return ERROR_CAN_NOT_COMPLETE;
}
-static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
+static int ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
+{
+ ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C)
+ TMPQArchive * ha = hf->ha;
+ TFileEntry * pFileEntry = hf->pFileEntry;
+ LPBYTE pbCompressed = NULL;
+ LPBYTE pbRawData = hf->pbFileSector;
+ int nError = ERROR_SUCCESS;
+
+ // We do not support patch files in MPK archives
+ assert(hf->pPatchInfo == NULL);
+
+ // If the file buffer is not allocated yet, do it.
+ if(hf->pbFileSector == NULL)
+ {
+ nError = AllocateSectorBuffer(hf);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Is the file compressed?
+ if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
+ {
+ // Allocate space for compressed data
+ pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
+ if(pbCompressed == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+ pbRawData = pbCompressed;
+ }
+
+ // Load the raw (compressed, encrypted) data
+ if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
+ {
+ STORM_FREE(pbCompressed);
+ return GetLastError();
+ }
+
+ // If the file is encrypted, we have to decrypt the data first
+ if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
+ {
+ DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize);
+ }
+
+ // If the file is compressed, we have to decompress it now
+ if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
+ {
+ int cbOutBuffer = (int)hf->dwDataSize;
+
+ if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize))
+ nError = ERROR_FILE_CORRUPT;
+ }
+ else
+ {
+ if(pbRawData != hf->pbFileSector)
+ memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize);
+ }
+
+ // Free the decompression buffer.
+ if(pbCompressed != NULL)
+ STORM_FREE(pbCompressed);
+
+ // The file sector is now properly loaded
+ hf->dwSectorOffs = 0;
+ }
+
+ // At this moment, we have the file loaded into the file buffer.
+ // Copy as much as the caller wants
+ if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0)
+ {
+ // File position is greater or equal to file size ?
+ if(dwFilePos >= hf->dwDataSize)
+ {
+ *pdwBytesRead = 0;
+ return ERROR_SUCCESS;
+ }
+
+ // If not enough bytes remaining in the file, cut them
+ if((hf->dwDataSize - dwFilePos) < dwToRead)
+ dwToRead = (hf->dwDataSize - dwFilePos);
+
+ // Copy the bytes
+ memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead);
+
+ // Give the number of bytes read
+ *pdwBytesRead = dwToRead;
+ return ERROR_SUCCESS;
+ }
+
+ // An error, sorry
+ return ERROR_CAN_NOT_COMPLETE;
+}
+
+
+static int ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
{
TMPQArchive * ha = hf->ha;
LPBYTE pbBuffer = (BYTE *)pvBuffer;
@@ -582,7 +667,7 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos,
if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead);
else
- nError = ReadMpqFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead);
+ nError = ReadMpqFileSectorFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead);
// Fix error code
if(nError == ERROR_SUCCESS && dwBytesRead != hf->cbFileData)
@@ -620,6 +705,38 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos,
return nError;
}
+static int ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
+{
+ ULONGLONG FilePosition1 = dwFilePos;
+ ULONGLONG FilePosition2;
+ DWORD dwBytesRead = 0;
+ int nError = ERROR_SUCCESS;
+
+ assert(hf->pStream != NULL);
+
+ // Because stream I/O functions are designed to read
+ // "all or nothing", we compare file position before and after,
+ // and if they differ, we assume that number of bytes read
+ // is the difference between them
+
+ if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead))
+ {
+ // If not all bytes have been read, then return the number of bytes read
+ if((nError = GetLastError()) == ERROR_HANDLE_EOF)
+ {
+ FileStream_GetPos(hf->pStream, &FilePosition2);
+ dwBytesRead = (DWORD)(FilePosition2 - FilePosition1);
+ }
+ }
+ else
+ {
+ dwBytesRead = dwToRead;
+ }
+
+ *pdwBytesRead = dwBytesRead;
+ return nError;
+}
+
//-----------------------------------------------------------------------------
// SFileReadFile
@@ -648,58 +765,36 @@ bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD
// If the file is local file, read the data directly from the stream
if(hf->pStream != NULL)
{
- ULONGLONG FilePosition1;
- ULONGLONG FilePosition2;
-
- // Because stream I/O functions are designed to read
- // "all or nothing", we compare file position before and after,
- // and if they differ, we assume that number of bytes read
- // is the difference between them
-
- FileStream_GetPos(hf->pStream, &FilePosition1);
- if(!FileStream_Read(hf->pStream, NULL, pvBuffer, dwToRead))
- {
- // If not all bytes have been read, then return the number
- // of bytes read
- if((nError = GetLastError()) == ERROR_HANDLE_EOF)
- {
- FileStream_GetPos(hf->pStream, &FilePosition2);
- dwBytesRead = (DWORD)(FilePosition2 - FilePosition1);
- }
- else
- {
- nError = GetLastError();
- }
- }
- else
- {
- dwBytesRead = dwToRead;
- }
+ nError = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
- else
+
+ // If the file is a patch file, we have to read it special way
+ else if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
{
- // If the file is a patch file, we have to read it special way
- if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
- {
- nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
- }
+ nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
+ }
- // If the file is single unit file, redirect it to read file
- else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
- {
- nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
- }
+ // If the archive is a MPK archive, we need special way to read the file
+ else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK)
+ {
+ nError = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
+ }
- // Otherwise read it as sector based MPQ file
- else
- {
- nError = ReadMpqFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
- }
+ // If the file is single unit file, redirect it to read file
+ else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
+ {
+ nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
+ }
- // Increment the file position
- hf->dwFilePos += dwBytesRead;
+ // Otherwise read it as sector based MPQ file
+ else
+ {
+ nError = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
+ // Increment the file position
+ hf->dwFilePos += dwBytesRead;
+
// Give the caller the number of bytes read
if(pdwRead != NULL)
*pdwRead = dwBytesRead;
@@ -888,58 +983,97 @@ static TFileHeader2Ext data2ext[] =
{0, 0, 0, 0, NULL} // Terminator
};
-bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName)
+static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * szFileName)
{
- TFileEntry * pFileEntry;
TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle
- char szPseudoName[20];
- DWORD FirstBytes[2]; // The first 4 bytes of the file
+ DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file
+ DWORD dwBytesRead = 0;
DWORD dwFilePos; // Saved file position
- int nError = ERROR_SUCCESS;
- int i;
- // Pre-zero the output buffer
- if(szFileName != NULL)
- *szFileName = 0;
+ // Read the first 2 DWORDs bytes from the file
+ dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT);
+ SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead, NULL);
+ SFileSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN);
- // Check valid parameters
- if(!IsValidFileHandle(hf))
- nError = ERROR_INVALID_HANDLE;
- pFileEntry = hf->pFileEntry;
-
- // Only do something if the file name is not filled
- if(nError == ERROR_SUCCESS && pFileEntry != NULL && pFileEntry->szFileName == NULL)
+ // If we read at least 8 bytes
+ if(dwBytesRead == sizeof(FirstBytes))
{
- // Read the first 2 DWORDs bytes from the file
- FirstBytes[0] = FirstBytes[1] = 0;
- dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT);
- SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), NULL, NULL);
+ // Make sure that the array is properly BSWAP-ed
BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes));
- SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN);
// Try to guess file extension from those 2 DWORDs
- for(i = 0; data2ext[i].szExt != NULL; i++)
+ for(size_t i = 0; data2ext[i].szExt != NULL; i++)
{
if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data &&
(FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data)
{
+ char szPseudoName[20] = "";
+
+ // Format the pseudo-name
sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt);
- break;
+
+ // Save the pseudo-name in the file entry as well
+ AllocateFileName(pFileEntry, szPseudoName);
+
+ // If the caller wants to copy the file name, do it
+ if(szFileName != NULL)
+ strcpy(szFileName, szPseudoName);
+ return ERROR_SUCCESS;
}
}
+ }
- // Put the file name to the file table
- AllocateFileName(pFileEntry, szPseudoName);
- }
+ return ERROR_NOT_SUPPORTED;
+}
+
+bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName)
+{
+ TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle
+ TCHAR * szFileNameT;
+ int nError = ERROR_INVALID_HANDLE;
+
+ // Pre-zero the output buffer
+ if(szFileName != NULL)
+ *szFileName = 0;
- // Now put the file name to the file structure
- if(nError == ERROR_SUCCESS && szFileName != NULL)
+ // Check valid parameters
+ if(IsValidFileHandle(hf))
{
- if(pFileEntry != NULL && pFileEntry->szFileName != NULL)
- strcpy(szFileName, pFileEntry->szFileName);
- else if(hf->pStream != NULL)
- CopyFileName(szFileName, FileStream_GetFileName(hf->pStream));
+ TFileEntry * pFileEntry = hf->pFileEntry;
+
+ // For MPQ files, retrieve the file name from the file entry
+ if(hf->pStream == NULL)
+ {
+ if(pFileEntry != NULL)
+ {
+ // If the file name is not there yet, create a pseudo name
+ if(pFileEntry->szFileName == NULL)
+ {
+ nError = CreatePseudoFileName(hFile, pFileEntry, szFileName);
+ }
+ else
+ {
+ if(szFileName != NULL)
+ strcpy(szFileName, pFileEntry->szFileName);
+ nError = ERROR_SUCCESS;
+ }
+ }
+ }
+
+ // For local files, copy the file name from the stream
+ else
+ {
+ if(szFileName != NULL)
+ {
+ szFileNameT = FileStream_GetFileName(hf->pStream);
+ CopyFileName(szFileName, szFileNameT, _tcslen(szFileNameT));
+ }
+ nError = ERROR_SUCCESS;
+ }
}
+
+ if(nError != ERROR_SUCCESS)
+ SetLastError(nError);
return (nError == ERROR_SUCCESS);
}