コード例 #1
0
ファイル: SFileOpenFileEx.cpp プロジェクト: 814077430/ArkCORE
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
{
    TFileStream * pStream;
    TMPQFile * hf = NULL;

    pStream = FileStream_OpenFile(szFileName, false);
    if (pStream != NULL)
    {
        // Allocate and initialize file handle
        hf = CreateMpqFile(NULL);
        if (hf != NULL)
        {
            hf->pStream = pStream;
            *phFile = hf;
            return true;
        }
        else
        {
            FileStream_Close(pStream);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        }
    }
    *phFile = NULL;
    return false;
}
コード例 #2
0
ファイル: SFileOpenFileEx.cpp プロジェクト: Argon-/aura-bot
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
{
    TFileStream * pStream;
    TMPQFile * hf = NULL;
    TCHAR szFileNameT[MAX_PATH];

    // Convert the file name to UNICODE (if needed)
    CopyFileName(szFileNameT, szFileName, strlen(szFileName));

    // Open the file and create the TMPQFile structure
    pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);
    if(pStream != NULL)
    {
        // Allocate and initialize file handle
        hf = CreateMpqFile(NULL);
        if(hf != NULL)
        {
            hf->pStream = pStream;
            *phFile = hf;
            return true;
        }
        else
        {
            FileStream_Close(pStream);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        }
    }
    *phFile = NULL;
    return false;
}
コード例 #3
0
ファイル: ListFile.cpp プロジェクト: jnovack/CascLib
void * ListFile_OpenExternal(const TCHAR * szListFile)
{
    PLISTFILE_CACHE pCache = NULL;
    TFileStream * pStream;
    ULONGLONG FileSize = 0;

    // Open the external listfile
    pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY);
    if(pStream != NULL)
    {
        // Retrieve the size of the external listfile
        FileStream_GetSize(pStream, &FileSize);
        if(0 < FileSize && FileSize <= 0x30000000)
        {
            // Create the in-memory cache for the entire listfile
            // The listfile does not have any data loaded yet
            pCache = CreateListFileCache((DWORD)FileSize);
            if(pCache != NULL)
            {
                if(!FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
                {
                    ListFile_Free(pCache);
                    pCache = NULL;
                }
            }
        }

        // Close the file stream
        FileStream_Close(pStream);
    }

    return pCache;
}
コード例 #4
0
ファイル: ListFile.cpp プロジェクト: Deaod/CascLib
void * ListFile_OpenExternal(const TCHAR * szListFile)
{
    TListFileCache * pCache;
    TFileStream * pStream;
    ULONGLONG FileSize = 0;

    // Open the external listfile
    pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY);
    if(pStream != NULL)
    {
        // Retrieve the size of the external listfile
        FileStream_GetSize(pStream, &FileSize);
        if(0 < FileSize && FileSize <= 0xFFFFFFFF)
        {                                                               
            // Create the cache for the listfile
            pCache = CreateListFileCache(ReloadCache_ExternalFile, CloseStream_ExternalFile, pStream, (DWORD)FileSize);
            if(pCache != NULL)
                return pCache;
        }

        // Close the file stream
        FileStream_Close(pStream);
    }

    return NULL;
}
コード例 #5
0
static bool ExtractPatchPrefixFromFile(const TCHAR * szHelperFile, char * szPatchPrefix, size_t nMaxChars, size_t * PtrLength)
{
    TFileStream * pStream;
    ULONGLONG FileSize = 0;
    size_t nLength;
    char szFileData[MAX_PATH+1];
    bool bResult = false;

    pStream = FileStream_OpenFile(szHelperFile, STREAM_FLAG_READ_ONLY);
    if(pStream != NULL)
    {
        // Retrieve and check the file size
        FileStream_GetSize(pStream, &FileSize);
        if(12 <= FileSize && FileSize < MAX_PATH)
        {
            // Read the entire file to memory
            if(FileStream_Read(pStream, NULL, szFileData, (DWORD)FileSize))
            {
                // Terminate the buffer with zero
                szFileData[(DWORD)FileSize] = 0;

                // The file data must begin with the "PatchPrefix" variable
                if(!_strnicmp(szFileData, "PatchPrefix", 11))
                {
                    char * szLinePtr = szFileData + 11;
                    char * szLineEnd;
                    
                    // Skip spaces or '='
                    while(szLinePtr[0] == ' ' || szLinePtr[0] == '=')
                        szLinePtr++;
                    szLineEnd = szLinePtr;

                    // Find the end
                    while(szLineEnd[0] != 0 && szLineEnd[0] != 0x0A && szLineEnd[0] != 0x0D)
                        szLineEnd++;
                    nLength = (size_t)(szLineEnd - szLinePtr);

                    // Copy the variable
                    if(szLineEnd > szLinePtr && nLength <= nMaxChars)
                    {
                        memcpy(szPatchPrefix, szLinePtr, nLength);
                        szPatchPrefix[nLength] = 0;
                        PtrLength[0] = nLength;
                        bResult = true;
                    }
                }
            }
        }

        // Close the stream
        FileStream_Close(pStream);
    }

    return bResult;
}
コード例 #6
0
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted, DWORD dwSearchScope)
{
    TFileStream * pLocalFile = NULL;
    HANDLE hMpqFile = NULL;
    int nError = ERROR_SUCCESS;

    // Open the MPQ file
    if (nError == ERROR_SUCCESS)
    {
        if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile))
            nError = GetLastError();
    }

    // Create the local file
    if (nError == ERROR_SUCCESS)
    {
        pLocalFile = FileStream_CreateFile(szExtracted);
        if(pLocalFile == NULL)
            nError = GetLastError();
    }

    // Copy the file's content
    if (nError == ERROR_SUCCESS)
    {
        char  szBuffer[0x1000];
        DWORD dwTransferred;

        for (;;)
        {
            // dwTransferred is only set to nonzero if something has been read.
            // nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF
            if (!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL))
                nError = GetLastError();
            if (nError == ERROR_HANDLE_EOF)
                nError = ERROR_SUCCESS;
            if (dwTransferred == 0)
                break;

            // If something has been actually read, write it
            if (!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred))
                nError = GetLastError();
        }
    }

    // Close the files
    if (hMpqFile != NULL)
        SFileCloseFile(hMpqFile);
    if (pLocalFile != NULL)
        FileStream_Close(pLocalFile);
    if (nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
コード例 #7
0
ファイル: MPQ.cpp プロジェクト: Sasho/SkyFireEMU_430
int ExtractFileToHardDrive(HANDLE &MPQ_handle, const char * szArchivedFile, const char * szFileName)
{
    HANDLE hFile  = NULL;          // Archived file handle
    TFileStream* handle = NULL;    // Disk file handle
    int    nError = ERROR_SUCCESS; // Result value
    
    if (nError == ERROR_SUCCESS)            
    {
        if (!SFileOpenFileEx(MPQ_handle, szArchivedFile, SFILE_OPEN_PATCHED_FILE, &hFile))
            nError = GetLastError();
    }
    
    // Create the target file
    if (nError == ERROR_SUCCESS)
    {
        handle = FileStream_CreateFile(szFileName);
        if (handle == NULL)
            nError = GetLastError();
    }
    
    // Read the file from the archive
    if (nError == ERROR_SUCCESS)
    {
        // Get the size of the full patched file
        DWORD dwFileSize = SFileGetFileSize(hFile, NULL);
        if (dwFileSize != 0)
        {
            // Allocate space for the full file
            BYTE * pbFullFile = new BYTE[dwFileSize];
            if (!SFileReadFile(hFile, pbFullFile, dwFileSize))
            {           
                nError = GetLastError();
                printf("Failed to read full patched file data \"%s\"\n", szFileName);
                assert(false);
            }
            FileStream_Write(handle, NULL, pbFullFile, dwFileSize);
            delete [] pbFullFile;
        }
    }
    
    // Cleanup and exit
    if (handle != NULL)
        FileStream_Close(handle);
    if (hFile != NULL)
        SFileCloseFile(hFile);
    
    return nError;
}
コード例 #8
0
ファイル: CascBuildCfg.cpp プロジェクト: BuloZB/CascLib
static int LoadTextFile(const TCHAR * szFileName, PQUERY_KEY pFileBlob)
{
    TFileStream * pStream;
    ULONGLONG FileSize = 0;
    int nError = ERROR_SUCCESS;

    // Open the agent file
    pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
    if(pStream != NULL)
    {
        // Retrieve its size
        FileStream_GetSize(pStream, &FileSize);

        // Load the file to memory
        if(0 < FileSize && FileSize < 0x100000)
        {
            // Initialize the blob
            pFileBlob->cbData = (DWORD)FileSize;
            pFileBlob->pbData = CASC_ALLOC(BYTE, pFileBlob->cbData + 1);

            // Load the file data into the blob
            if(pFileBlob->pbData != NULL)
            {
                FileStream_Read(pStream, NULL, pFileBlob->pbData, (DWORD)FileSize);
                pFileBlob->pbData[pFileBlob->cbData] = 0;
            }
            else
                nError = ERROR_NOT_ENOUGH_MEMORY;
        }
        else
            nError = ERROR_INVALID_PARAMETER;

        FileStream_Close(pStream);
    }
    else
        nError = GetLastError();

    return nError;
}
コード例 #9
0
ファイル: SFileOpenFileEx.cpp プロジェクト: Hernrup/stormlib
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
{
    TFileStream * pStream;
    TMPQFile * hf = NULL;

    // We have to convert the local file name to UNICODE, if needed
#ifdef _UNICODE
    TCHAR szFileNameT[MAX_PATH];
    int i;

    for(i = 0; szFileName[i] != 0; i++)
        szFileNameT[i] = szFileName[i];
    szFileNameT[i] = 0;
    pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);

#else
    pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
#endif

    if(pStream != NULL)
    {
        // Allocate and initialize file handle
        hf = CreateMpqFile(NULL);
        if(hf != NULL)
        {
            hf->pStream = pStream;
            *phFile = hf;
            return true;
        }
        else
        {
            FileStream_Close(pStream);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        }
    }
    *phFile = NULL;
    return false;
}
コード例 #10
0
ファイル: CascFiles.cpp プロジェクト: bryanspaulding/CascLib
// Checks whether there is a ".build.info" or ".build.db".
// If yes, the function sets "szDataPath" and "szIndexPath"
// in the storage structure and returns ERROR_SUCCESS
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
    TFileStream * pStream;
    TCHAR * szBuildFile;
    int nError = ERROR_FILE_NOT_FOUND;

    // Try to find any of the root files used in the history
    for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
    {
        // Create the full name of the .agent.db file
        szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName);
        if (szBuildFile != NULL)
        {
            // Attempt to open the file
            pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY);
            if (pStream != NULL)
            {
                // Free the stream
                FileStream_Close(pStream);

                // Check for the data directory
                nError = CheckDataDirectory(hs, szDirectory);
                if (nError == ERROR_SUCCESS)
                {
                    hs->szBuildFile = szBuildFile;
                    hs->BuildFileType = BuildTypes[i].BuildFileType;
                    return ERROR_SUCCESS;
                }
            }

            CASC_FREE(szBuildFile);
        }
    }

    return nError;
}
コード例 #11
0
ファイル: System.cpp プロジェクト: milleniumcore/CactusEMU
int ExtractLocaleFile(int MPQId, const char * szArchivedFile, const char * szFileName)
{
    HANDLE hFile  = NULL;          // Archived file handle
    TFileStream* handle = NULL;          // Disk file handle
    int    nError = ERROR_SUCCESS; // Result value
    
    // Open a file in the archive, e.g. "data\global\music\Act1\tristram.wav"
    if(nError == ERROR_SUCCESS)            
    {
        if(!SFileOpenFileEx(localeMPQ[MPQId], szArchivedFile, SFILE_OPEN_PATCHED_FILE, &hFile))
            nError = GetLastError();
    }
	
    // Create the target file
    if(nError == ERROR_SUCCESS)
    {
        //handle = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
		//if(handle == INVALID_HANDLE_VALUE)
        //    nError = GetLastError();
		handle = FileStream_CreateFile(szFileName);
        if(handle == NULL)
            nError = GetLastError();
    }
	
	/*
	 // Get the size of the full patched file
	 dwFileSize = SFileGetFileSize(hFile, NULL);
	 if(dwFileSize != 0)
	 {
	 // Allocate space for the full file
	 pbFullFile = new BYTE[dwFileSize];
	 if(pbFullFile != NULL)
	 {
	 if(!SFileReadFile(hFile, pbFullFile, dwFileSize))
	 {           
	 nError = GetLastError();
	 printf("Failed to read full patched file data \"%s\"\n", szFileName);
	 }
	 
	 if(nError == ERROR_SUCCESS)
	 {
	 strcpy(szLocFileName, MAKE_PATH("Work//"));
	 strcat(szLocFileName, GetPlainName(szFileName));
	 
	 pStream = FileStream_CreateFile(szLocFileName);
	 if(pStream != NULL)
	 {
	 FileStream_Write(pStream, NULL, pbFullFile, dwFileSize);
	 FileStream_Close(pStream);
	 }
	 }
	 
	 delete [] pbFullFile;
	 }
	 }
	 */
    // Read the file from the archive
    if(nError == ERROR_SUCCESS)
    {
        // Get the size of the full patched file
        DWORD dwFileSize = SFileGetFileSize(hFile, NULL);
        if(dwFileSize != 0)
        {
            // Allocate space for the full file
            BYTE * pbFullFile = new BYTE[dwFileSize];
            if(!SFileReadFile(hFile, pbFullFile, dwFileSize))
			{           
				nError = GetLastError();
				printf("Failed to read full patched file data \"%s\"\n", szFileName);
				assert(false);
			}
			FileStream_Write(handle, NULL, pbFullFile, dwFileSize);
			delete [] pbFullFile;
        }
    }        
	
    // Cleanup and exit
    if(handle != NULL)
        FileStream_Close(handle);
    if(hFile != NULL)
        SFileCloseFile(hFile);
	
    return nError;
}
コード例 #12
0
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);
}
コード例 #13
0
ファイル: SFileOpenArchive.cpp プロジェクト: Argon-/aura-bot
bool WINAPI SFileOpenArchive(
    const TCHAR * szMpqName,
    DWORD dwPriority,
    DWORD dwFlags,
    HANDLE * phMpq)
{
    TMPQUserData * pUserData;
    TFileStream * pStream = NULL;       // Open file stream
    TMPQArchive * ha = NULL;            // Archive handle
    TFileEntry * pFileEntry;
    ULONGLONG FileSize = 0;             // Size of the file
    int nError = ERROR_SUCCESS;   

    // Verify the parameters
    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
        nError = ERROR_INVALID_PARAMETER;

    // One time initialization of MPQ cryptography
    InitializeMpqCryptography();
    dwPriority = dwPriority;

    // Open the MPQ archive file
    if(nError == ERROR_SUCCESS)
    {
        DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);

        // If not forcing MPQ v 1.0, also use file bitmap
        dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;

        // Initialize the stream
        pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
        if(pStream == NULL)
            nError = GetLastError();
    }

    // Check the file size. There must be at least 0x20 bytes
    if(nError == ERROR_SUCCESS)
    {
        FileStream_GetSize(pStream, &FileSize);
        if(FileSize < MPQ_HEADER_SIZE_V1)
            nError = ERROR_BAD_FORMAT;
    }

    // Allocate the MPQhandle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Initialize handle structure and allocate structure for MPQ header
    if(nError == ERROR_SUCCESS)
    {
        ULONGLONG SearchOffset = 0;
        DWORD dwStreamFlags = 0;
        DWORD dwHeaderSize;
        DWORD dwHeaderID;

        memset(ha, 0, sizeof(TMPQArchive));
        ha->pfnHashString = HashString;
        ha->pStream = pStream;
        pStream = NULL;

        // Set the archive read only if the stream is read-only
        FileStream_GetFlags(ha->pStream, &dwStreamFlags);
        ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;

        // Also remember if we shall check sector CRCs when reading file
        if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
            ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;

        // Find the offset of MPQ header within the file
        while(SearchOffset < FileSize)
        {
            DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;

            // Cut the bytes available, if needed
            if((FileSize - SearchOffset) < MPQ_HEADER_SIZE_V4)
                dwBytesAvailable = (DWORD)(FileSize - SearchOffset);

            // Read the eventual MPQ header
            if(!FileStream_Read(ha->pStream, &SearchOffset, ha->HeaderData, dwBytesAvailable))
            {
                nError = GetLastError();
                break;
            }

            // There are AVI files from Warcraft III with 'MPQ' extension.
            if(SearchOffset == 0 && IsAviFile(ha->HeaderData))
            {
                nError = ERROR_AVI_FILE;
                break;
            }

            // If there is the MPQ user data signature, process it
            dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
            if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
            {
                // Verify if this looks like a valid user data
                pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
                if(pUserData != NULL)
                {
                    // Fill the user data header
                    ha->UserDataPos = SearchOffset;
                    ha->pUserData = &ha->UserData;
                    memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));

                    // Continue searching from that position
                    SearchOffset += ha->pUserData->dwHeaderOffs;
                    continue;
                }
            }

            // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
            // tests the MPQ header size. It must be at least 0x20 bytes in order to load it
            // Abused by Spazzler Map protector. Note that the size check is not present
            // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
            dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
            if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
            {
                // Now convert the header to version 4
                nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
                break;
            }

            // Check for MPK archives (Longwu Online - MPQ fork)
            if(dwHeaderID == ID_MPK)
            {
                // Now convert the MPK header to MPQ Header version 4
                nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
                break;
            }

            // If searching for the MPQ header is disabled, return an error
            if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
            {
                nError = ERROR_NOT_SUPPORTED;
                break;
            }

            // Move to the next possible offset
            SearchOffset += 0x200;
        }

        // Did we identify one of the supported headers?
        if(nError == ERROR_SUCCESS)
        {
            // Set the user data position to the MPQ header, if none
            if(ha->pUserData == NULL)
                ha->UserDataPos = SearchOffset;

            // Set the position of the MPQ header
            ha->pHeader = (TMPQHeader *)ha->HeaderData;
            ha->MpqPos = SearchOffset;

            // Sector size must be nonzero.
            if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
                nError = ERROR_BAD_FORMAT;
        }
    }

    // Fix table positions according to format
    if(nError == ERROR_SUCCESS)
    {
        // Dump the header
//      DumpMpqHeader(ha->pHeader);

        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
        // and ignores the MPQ format version as well. The trick is to
        // fake MPQ format 2, with an improper hi-word position of hash table and block table
        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0
        if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
        {
            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
            ha->pUserData = NULL;
        }

        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
        if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Set the size of file sector
        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);

        // Verify if any of the tables doesn't start beyond the end of the file
        nError = VerifyMpqTablePositions(ha, FileSize);
    }

    // Read the hash table. Ignore the result, as hash table is no longer required
    // Read HET table. Ignore the result, as HET table is no longer required
    if(nError == ERROR_SUCCESS)
    {
        nError = LoadAnyHashTable(ha);
    }

    // Now, build the file table. It will be built by combining
    // the block table, BET table, hi-block table, (attributes) and (listfile).
    if(nError == ERROR_SUCCESS)
    {
        nError = BuildFileTable(ha);
    }

    // Verify the file table, if no kind of malformation was detected
    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_MALFORMED) == 0)
    {
        TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
        ULONGLONG RawFilePos;

        // Parse all file entries
        for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
        {
            // If that file entry is valid, check the file position
            if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
            {
                // Get the 64-bit file position,
                // relative to the begin of the file
                RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;

                // Begin of the file must be within range
                if(RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }

                // End of the file must be within range
                RawFilePos += pFileEntry->dwCmpSize;
                if(RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }
            }
        }
    }

    // Load the internal listfile and include it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
    {
        // Save the flags for (listfile)
        pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (listfile) is optional.
            SFileAddListFile((HANDLE)ha, NULL);
            ha->dwFileFlags1 = pFileEntry->dwFlags;
        }
    }

    // Load the "(attributes)" file and merge it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
    {
        // Save the flags for (attributes)
        pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (attributes) is optional.
            SAttrLoadAttributes(ha);
            ha->dwFileFlags2 = pFileEntry->dwFlags;
        }
    }

    // Cleanup and exit
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeMPQArchive(ha);
        SetLastError(nError);
        ha = NULL;
    }

    *phMpq = ha;
    return (nError == ERROR_SUCCESS);
}
コード例 #14
0
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
{
    TFileStream * pStream = NULL;           // File stream
    TMPQArchive * ha = NULL;                // MPQ archive handle
    ULONGLONG MpqPos = 0;                   // Position of MPQ header in the file
    HANDLE hMpq = NULL;
    DWORD dwBlockTableSize = 0;             // Initial block table size
    DWORD dwHashTableSize = 0;
    DWORD dwMaxFileCount;
    int nError = 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))            ||
       (pCreateInfo->dwMaxFileCount < 4))
    {
        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_LINEAR | 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 and attributes file
    dwMaxFileCount = pCreateInfo->dwMaxFileCount;
    if(pCreateInfo->dwAttrFlags != 0)
        dwMaxFileCount++;
    dwMaxFileCount++;

    // If file count is not zero, initialize the hash table size
    dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);

    // 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))
        nError = GetLastError();

#ifdef _DEBUG    
    // Debug code, used for testing StormLib
//  dwBlockTableSize = dwHashTableSize * 2;
#endif

    // Create the archive handle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Fill the MPQ archive handle structure
    if(nError == ERROR_SUCCESS)
    {
        memset(ha, 0, sizeof(TMPQArchive));
        ha->pStream         = pStream;
        ha->dwSectorSize    = pCreateInfo->dwSectorSize;
        ha->UserDataPos     = MpqPos;
        ha->MpqPos          = MpqPos;
        ha->pHeader         = (TMPQHeader *)ha->HeaderData;
        ha->dwMaxFileCount  = dwMaxFileCount;
        ha->dwFileTableSize = 0;
        ha->dwFileFlags1    = pCreateInfo->dwFileFlags1;
        ha->dwFileFlags2    = pCreateInfo->dwFileFlags2;
        ha->dwFlags         = 0;

        // Setup the attributes
        ha->dwAttrFlags     = pCreateInfo->dwAttrFlags;
        pStream = NULL;
    }

    // Fill the MPQ header
    if(nError == ERROR_SUCCESS)
    {
        TMPQHeader * pHeader = ha->pHeader;

        // Fill the MPQ header
        memset(pHeader, 0, sizeof(ha->HeaderData));
        pHeader->dwID             = ID_MPQ;
        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
        nError = WriteNakedMPQHeader(ha);

        // Remember that the (listfile) and (attributes) need to be saved
        ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES;
    }

    // Create initial HET table, if the caller required an MPQ format 3.0 or newer
    if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3)
    {
        ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
        if(ha->pHetTable == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Create initial hash table
    if(nError == ERROR_SUCCESS)
    {
        nError = CreateHashTable(ha, dwHashTableSize);
    }

    // Create initial file table
    if(nError == ERROR_SUCCESS)
    {
        ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
        if(ha->pFileTable != NULL)
            memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Cleanup : If an error, delete all buffers and return
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeMPQArchive(ha);
        SetLastError(nError);
        ha = NULL;
    }
    
    // Return the values
    *phMpq = (HANDLE)ha;
    return (nError == ERROR_SUCCESS);
}
コード例 #15
0
bool WINAPI SFileOpenArchive(
    const char * szMpqName, 
    DWORD dwPriority, 
    DWORD dwFlags, 
    HANDLE * phMpq)
{
    TFileStream * pStream = NULL;       // Open file stream
    TMPQArchive * ha = NULL;            // Archive handle
    ULONGLONG FileSize = 0;             // Size of the file
    int nError = ERROR_SUCCESS;

    // Verify the parameters
    if (szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
        nError = ERROR_INVALID_PARAMETER;

    // One time initialization of MPQ cryptography
    InitializeMpqCryptography();
    dwPriority = dwPriority;

    // Open the MPQ archive file
    if (nError == ERROR_SUCCESS)
    {
        if (!(dwFlags & MPQ_OPEN_ENCRYPTED))
        {
            pStream = FileStream_OpenFile(szMpqName, (dwFlags & MPQ_OPEN_READ_ONLY) ? false : true);
            if (pStream == NULL)
                nError = GetLastError();
        }
        else
        {
            pStream = FileStream_OpenEncrypted(szMpqName);
            if (pStream == NULL)
                nError = GetLastError();
        }
    }

    // Allocate the MPQhandle
    if (nError == ERROR_SUCCESS)
    {
        FileStream_GetSize(pStream, FileSize);
        if ((ha = ALLOCMEM(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Initialize handle structure and allocate structure for MPQ header
    if (nError == ERROR_SUCCESS)
    {
        memset(ha, 0, sizeof(TMPQArchive));
        ha->pStream = pStream;
        pStream = NULL;

        // Remember if the archive is open for write
        if (ha->pStream->StreamFlags & (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Also remember if we shall check sector CRCs when reading file
        if (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
            ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
    }

    // Find the offset of MPQ header within the file
    if (nError == ERROR_SUCCESS)
    {
        ULONGLONG SearchPos = 0;
        DWORD dwHeaderID;

        while (SearchPos < FileSize)
        {
            DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;

            // Cut the bytes available, if needed
            if ((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
                dwBytesAvailable = (DWORD)(FileSize - SearchPos);

            // Read the eventual MPQ header
            if (!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
            {
                nError = GetLastError();
                break;
            }

            // There are AVI files from Warcraft III with 'MPQ' extension.
            if (SearchPos == 0 && IsAviFile(ha->HeaderData))
            {
                nError = ERROR_AVI_FILE;
                break;
            }

            // If there is the MPQ user data signature, process it
            dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData);
            if (dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL)
            {
                // Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0
                if ((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
                {
                    // Fill the user data header
                    ha->pUserData = &ha->UserData;
                    memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
                    BSWAP_TMPQUSERDATA(ha->pUserData);

                    // Remember the position of the user data and continue search
                    ha->UserDataPos = SearchPos;
                    SearchPos += ha->pUserData->dwHeaderOffs;
                    continue;
                }
            }

            // There must be MPQ header signature
            if (dwHeaderID == ID_MPQ)
            {
                // Save the position where the MPQ header has been found
                if (ha->pUserData == NULL)
                    ha->UserDataPos = SearchPos;
                ha->pHeader = (TMPQHeader *)ha->HeaderData;
                ha->MpqPos = SearchPos;

                // Now convert the header to version 4
                BSWAP_TMPQHEADER(ha->pHeader);
                ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
                break;
            }

            // Move to the next possible offset
            SearchPos += 0x200;
        }

        // If we haven't found MPQ header in the file, it's an error
        if (ha->pHeader == NULL)
            nError = ERROR_BAD_FORMAT;
    }

    // Fix table positions according to format
    if (nError == ERROR_SUCCESS)
    {
        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data, 
        // and probably ignores the MPQ format version as well. The trick is to
        // fake MPQ format 2, with an improper hi-word position of hash table and block table
        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0
        if (dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
        {
            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
            ha->pUserData = NULL;
        }

        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
        if (dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Set the size of file sector
        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);

        // Verify if any of the tables doesn't start beyond the end of the file
        nError = VerifyMpqTablePositions(ha, FileSize);
    }

    // Read the hash table.
    // "interface.MPQ.part" in trial version of World of Warcraft
    // has compressed block table and hash table.
    if (nError == ERROR_SUCCESS)
    {
        //
        // Note: We will not check if the hash table is properly decrypted.
        // Some MPQ protectors corrupt the hash table by rewriting part of it.
        // Hash table, the way how it works, allows arbitrary values for unused entries.
        //

        nError = LoadHashTable(ha);
    }

    // Read Het and Bet tables, if they are present
    if (nError == ERROR_SUCCESS)
    {
        nError = LoadHetAndBetTable(ha);
        if (ha->pHetTable || ha->pBetTable)
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
    }

    // Now, build the file table. It will be built by combining
    // the block table, hi-block table, (attributes) and (listfile).
    if (nError == ERROR_SUCCESS)
    {
        nError = BuildFileTable(ha, FileSize);
    }

    // Verify the block table, if no kind of protection was detected
    if (nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
    {
        TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
        TFileEntry * pFileEntry = ha->pFileTable;
//      ULONGLONG ArchiveSize = 0;
        ULONGLONG RawFilePos;

        // Parse all file entries
        for (pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
        {
            // If that file entry is valid, check the file position
            if (pFileEntry->dwFlags & MPQ_FILE_EXISTS)
            {
                // Get the 64-bit file position, 
                // relative to the begin of the file
                RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;

                // Begin of the file must be within range
                if (RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }

                // End of the file must be within range
                RawFilePos += pFileEntry->dwCmpSize;
                if (RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }

                // Also, we remember end of the file
//              if (RawFilePos > ArchiveSize)
//                  ArchiveSize = RawFilePos;
            }
        }
    }

    // Load the "(attributes)" file and merge it to the file table
    if (nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
    {
        // Ignore result of the operation. (attributes) is optional.
        SAttrLoadAttributes(ha);
    }

    // Load the internal listfile and include it to the file table
    if (nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
    {
        // Ignore result of the operation. (listfile) is optional.
        SFileAddListFile((HANDLE)ha, NULL);
    }

    // Test the indexes from BET and BET table
#ifdef __STORMLIB_TEST__
    if (nError == ERROR_SUCCESS)
    {
        TestNewHashBlockTables(ha);
    }
#endif

    // Cleanup and exit
    if (nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeMPQArchive(ha);
        SetLastError(nError);
        ha = NULL;
    }

    *phMpq = ha;
    return (nError == ERROR_SUCCESS);
}
コード例 #16
0
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);
}
コード例 #17
0
ファイル: CascTest.cpp プロジェクト: Allowed/CascLib
static int ExtractFile(HANDLE hStorage, const char * szFileName, const TCHAR * szLocalPath, DWORD dwLocaleFlags)
{
    TFileStream * pStream = NULL;
    HANDLE hFile = NULL;
    TCHAR szLocalFileName[MAX_PATH];
    TCHAR * szNamePtr = szLocalFileName;
    BYTE Buffer[0x1000];
    DWORD dwBytesRead;
    int nError = ERROR_SUCCESS;

    // Create the file path
    _tcscpy(szNamePtr, szLocalPath);
    szNamePtr += _tcslen(szLocalPath);

    *szNamePtr++ = _T('\\');
    
    // Copy the plain file name
    CopyString(szNamePtr, szFileName, strlen(szFileName));

    // Open the CASC file
    if(nError == ERROR_SUCCESS)
    {
        // Open a file
        if(!CascOpenFile(hStorage, szFileName, dwLocaleFlags, 0, &hFile))
        {
            assert(GetLastError() != ERROR_SUCCESS);
            nError = GetLastError();
        }
    }

    // Create the local file
    if(nError == ERROR_SUCCESS)
    {
        pStream = FileStream_CreateFile(szLocalFileName, 0);
        if(pStream == NULL)
        {
            // Try to create all directories and retry
            ForceCreatePath(szLocalFileName);
            pStream = FileStream_CreateFile(szLocalFileName, 0);
            if(pStream == NULL)
                nError = GetLastError();
        }
    }

    // Read some data from the file
    if(nError == ERROR_SUCCESS)
    {
        for(;;)
        {
            // Read data from the file
            CascReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead);
            if(dwBytesRead == 0)
                break;

            // Write the local file
            FileStream_Write(pStream, NULL, Buffer, dwBytesRead);
        }
    }

    // Close handles
    if(pStream != NULL)
        FileStream_Close(pStream);
    if(hFile != NULL)
        CascCloseFile(hFile);
    return nError;
}
コード例 #18
0
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */)
{
    TFileStream * pTempStream = NULL;
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    ULONGLONG ByteOffset;
    ULONGLONG ByteCount;
    LPDWORD pFileKeys = NULL;
    char szTempFile[MAX_PATH] = "";
    char * szTemp = NULL;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if (!IsValidMpqHandle(ha))
        nError = ERROR_INVALID_HANDLE;
    if (ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;
    if (ha->pHetTable != NULL || ha->pBetTable != NULL)
        nError = ERROR_ACCESS_DENIED;

    // Create the table with file keys
    if (nError == ERROR_SUCCESS)
    {
        if ((pFileKeys = ALLOCMEM(DWORD, ha->dwFileTableSize)) != NULL)
            memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // First of all, we have to check of we are able to decrypt all files.
    // If not, sorry, but the archive cannot be compacted.
    if (nError == ERROR_SUCCESS)
    {
        // Initialize the progress variables for compact callback
        FileStream_GetSize(ha->pStream, CompactTotalBytes);
        CompactBytesProcessed = 0;
        nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
    }

    // Get the temporary file name and create it
    if (nError == ERROR_SUCCESS)
    {
        strcpy(szTempFile, ha->pStream->szFileName);
        if ((szTemp = strrchr(szTempFile, '.')) != NULL)
            strcpy(szTemp + 1, "mp_");
        else
            strcat(szTempFile, "_");

        pTempStream = FileStream_CreateFile(szTempFile);
        if (pTempStream == NULL)
            nError = GetLastError();
    }

    // Write the data before MPQ user data (if any)
    if (nError == ERROR_SUCCESS && ha->UserDataPos != 0)
    {
        // Inform the application about the progress
        if (CompactCB != NULL)
            CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);

        ByteOffset = 0;
        ByteCount = ha->UserDataPos;
        nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ user data (if any)
    if (nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
    {
        // At this point, we assume that the user data size is equal
        // to pUserData->dwHeaderOffs.
        // If this assumption doesn't work, then we have an unknown version of MPQ
        ByteOffset = ha->UserDataPos;
        ByteCount = ha->MpqPos - ha->UserDataPos;

        assert(ha->pUserData != NULL);
        assert(ha->pUserData->dwHeaderOffs == ByteCount);
        nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ header
    if (nError == ERROR_SUCCESS)
    {
        // Remember the header size before swapping
        DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize;

        BSWAP_TMPQHEADER(ha->pHeader);
        if (!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite))
            nError = GetLastError();
        BSWAP_TMPQHEADER(ha->pHeader);

        // Update the progress
        CompactBytesProcessed += ha->pHeader->dwHeaderSize;
        ha->dwFlags &= ~MPQ_FLAG_NO_HEADER;
    }

    // Now copy all files
    if (nError == ERROR_SUCCESS)
    {
        nError = CopyMpqFiles(ha, pFileKeys, pTempStream);
    }

    // If succeeded, switch the streams
    if (nError == ERROR_SUCCESS)
    {
        if (FileStream_MoveFile(ha->pStream, pTempStream))
            pTempStream = NULL;
        else
            nError = ERROR_CAN_NOT_COMPLETE;
    }

    // If all succeeded, save the MPQ tables
    if (nError == ERROR_SUCCESS)
    {
        //
        // Note: We don't recalculate position of the MPQ tables at this point.
        // SaveMPQTables does it automatically.
        //

        nError = SaveMPQTables(ha);
        if (nError == ERROR_SUCCESS && CompactCB != NULL)
        {
            CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
            CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
            CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes);
        }
    }

    // Invalidate the compact callback
    pvUserData = NULL;
    CompactCB = NULL;

    // Cleanup and return
    if (pTempStream != NULL)
        FileStream_Close(pTempStream);
    if (pFileKeys != NULL)
        FREEMEM(pFileKeys);
    if (nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
コード例 #19
0
ファイル: SFileCreateArchive.cpp プロジェクト: mewbak/sau
bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
{
    TFileStream * pStream = NULL;           // File stream
    TMPQArchive * ha = NULL;                // MPQ archive handle
    ULONGLONG MpqPos = 0;                   // Position of MPQ header in the file
    HANDLE hMpq = NULL;
    USHORT wFormatVersion = MPQ_FORMAT_VERSION_1;
    DWORD dwBlockTableSize = 0;             // Initial block table size
    DWORD dwHashTableSize = 0;
    int nError = ERROR_SUCCESS;

    // Check the parameters, if they are valid
    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
    {
        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, dwFlags, &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, true);
    if(pStream == NULL)
    {
        pStream = FileStream_CreateFile(szMpqName);
        if(pStream == NULL)
            return false;
    }

    // Decide what format to use
    wFormatVersion = (USHORT)((dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> 16);
    if(wFormatVersion > MPQ_FORMAT_VERSION_4)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    // Increment the maximum amount of files to have space
    // for listfile and attributes file
    if(dwFlags & MPQ_CREATE_ATTRIBUTES)
        dwMaxFileCount++;
    dwMaxFileCount++;

    // If file count is not zero, initialize the hash table size
    dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);

    // 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))
        nError = GetLastError();

#ifdef _DEBUG    
    // Debug code, used for testing StormLib
//  dwBlockTableSize = dwHashTableSize * 2;
#endif

    // Create the archive handle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = ALLOCMEM(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Fill the MPQ archive handle structure
    if(nError == ERROR_SUCCESS)
    {
        memset(ha, 0, sizeof(TMPQArchive));
        ha->pStream         = pStream;
        ha->dwSectorSize    = (wFormatVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
        ha->UserDataPos     = MpqPos;
        ha->MpqPos          = MpqPos;
        ha->pHeader         = (TMPQHeader *)ha->HeaderData;
        ha->dwMaxFileCount  = dwMaxFileCount;
        ha->dwFileTableSize = 0;
        ha->dwFileFlags1    = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS |  MPQ_FILE_REPLACEEXISTING;
        ha->dwFileFlags2    = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS |  MPQ_FILE_REPLACEEXISTING;
        ha->dwFlags         = 0;

        // Setup the attributes
        if(dwFlags & MPQ_CREATE_ATTRIBUTES)
            ha->dwAttrFlags = MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5;
        pStream = NULL;
    }

    // Fill the MPQ header
    if(nError == ERROR_SUCCESS)
    {
        TMPQHeader * pHeader = ha->pHeader;

        // Fill the MPQ header
        memset(pHeader, 0, sizeof(ha->HeaderData));
        pHeader->dwID             = ID_MPQ;
        pHeader->dwHeaderSize     = MpqHeaderSizes[wFormatVersion];
        pHeader->dwArchiveSize    = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
        pHeader->wFormatVersion   = wFormatVersion;
        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(wFormatVersion >= MPQ_FORMAT_VERSION_4)
            pHeader->dwRawChunkSize = 0x4000;

        // Write the naked MPQ header
        nError = WriteNakedMPQHeader(ha);

        //
        // Note: Don't recalculate position of MPQ tables at this point.
        // We merely set a flag that indicates that the MPQ tables
        // have been changed, and SaveMpqTables will do the work when closing the archive.
        //

        ha->dwFlags |= MPQ_FLAG_CHANGED;
    }

    // Create initial hash table
    if(nError == ERROR_SUCCESS)
    {
        nError = CreateHashTable(ha, dwHashTableSize);
    }

    // Create initial HET table, if the caller required an MPQ format 3.0 or newer
    if(nError == ERROR_SUCCESS && wFormatVersion >= MPQ_FORMAT_VERSION_3)
    {
        ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
        if(ha->pHetTable == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Create initial file table
    if(nError == ERROR_SUCCESS)
    {
        ha->pFileTable = ALLOCMEM(TFileEntry, dwMaxFileCount);
        if(ha->pFileTable != NULL)
            memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * dwMaxFileCount);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Cleanup : If an error, delete all buffers and return
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeMPQArchive(ha);
        SetLastError(nError);
        ha = NULL;
    }
    
    // Return the values
    *phMpq = (HANDLE)ha;
    return (nError == ERROR_SUCCESS);
}
コード例 #20
0
ファイル: SFileOpenArchive.cpp プロジェクト: dogganz/StormLib
bool WINAPI SFileOpenArchive(
    const TCHAR * szMpqName,
    DWORD dwPriority,
    DWORD dwFlags,
    HANDLE * phMpq)
{
    TMPQUserData * pUserData;
    TFileStream * pStream = NULL;       // Open file stream
    TMPQArchive * ha = NULL;            // Archive handle
    TFileEntry * pFileEntry;
    ULONGLONG FileSize = 0;             // Size of the file
    LPBYTE pbHeaderBuffer = NULL;       // Buffer for searching MPQ header
    DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);
    bool bIsWarcraft3Map = false;
    int nError = ERROR_SUCCESS;   

    // Verify the parameters
    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    // One time initialization of MPQ cryptography
    InitializeMpqCryptography();
    dwPriority = dwPriority;

    // If not forcing MPQ v 1.0, also use file bitmap
    dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;

    // Open the MPQ archive file
    pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
    if(pStream == NULL)
        return false;

    // Check the file size. There must be at least 0x20 bytes
    if(nError == ERROR_SUCCESS)
    {
        FileStream_GetSize(pStream, &FileSize);
        if(FileSize < MPQ_HEADER_SIZE_V1)
            nError = ERROR_BAD_FORMAT;
    }

    // Allocate the MPQhandle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Allocate buffer for searching MPQ header
    if(nError == ERROR_SUCCESS)
    {
        pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE);
        if(pbHeaderBuffer == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Find the position of MPQ header
    if(nError == ERROR_SUCCESS)
    {
        ULONGLONG SearchOffset = 0;
        ULONGLONG EndOfSearch = FileSize;
        DWORD dwStrmFlags = 0;
        DWORD dwHeaderSize;
        DWORD dwHeaderID;
        bool bSearchComplete = false;

        memset(ha, 0, sizeof(TMPQArchive));
        ha->pfnHashString = HashStringSlash;
        ha->pStream = pStream;
        pStream = NULL;

        // Set the archive read only if the stream is read-only
        FileStream_GetFlags(ha->pStream, &dwStrmFlags);
        ha->dwFlags |= (dwStrmFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;

        // Also remember if we shall check sector CRCs when reading file
        ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;

        // Also remember if this MPQ is a patch
        ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
       
        // Limit the header searching to about 130 MB of data
        if(EndOfSearch > 0x08000000)
            EndOfSearch = 0x08000000;

        // Find the offset of MPQ header within the file
        while(bSearchComplete == false && SearchOffset < EndOfSearch)
        {
            // Always read at least 0x1000 bytes for performance.
            // This is what Storm.dll (2002) does.
            DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE;
            DWORD dwInBufferOffset = 0;

            // Cut the bytes available, if needed
            if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE)
                dwBytesAvailable = (DWORD)(FileSize - SearchOffset);

            // Read the eventual MPQ header
            if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable))
            {
                nError = GetLastError();
                break;
            }

            // There are AVI files from Warcraft III with 'MPQ' extension.
            if(SearchOffset == 0)
            {
                if(IsAviFile((DWORD *)pbHeaderBuffer))
                {
                    nError = ERROR_AVI_FILE;
                    break;
                }

                bIsWarcraft3Map = IsWarcraft3Map((DWORD *)pbHeaderBuffer);
            }

            // Search the header buffer
            while(dwInBufferOffset < dwBytesAvailable)
            {
                // Copy the data from the potential header buffer to the MPQ header
                memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData));

                // If there is the MPQ user data, process it
                // Note that Warcraft III does not check for user data, which is abused by many map protectors
                dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
                if(bIsWarcraft3Map == false && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
                {
                    if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA)
                    {
                        // Verify if this looks like a valid user data
                        pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
                        if(pUserData != NULL)
                        {
                            // Fill the user data header
                            ha->UserDataPos = SearchOffset;
                            ha->pUserData = &ha->UserData;
                            memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));

                            // Continue searching from that position
                            SearchOffset += ha->pUserData->dwHeaderOffs;
                            break;
                        }
                    }
                }

                // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
                // tests the MPQ header size. It must be at least 0x20 bytes in order to load it
                // Abused by Spazzler Map protector. Note that the size check is not present
                // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
                dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
                if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
                {
                    // Now convert the header to version 4
                    nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
                    bSearchComplete = true;
                    break;
                }

                // Check for MPK archives (Longwu Online - MPQ fork)
                if(dwHeaderID == ID_MPK)
                {
                    // Now convert the MPK header to MPQ Header version 4
                    nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
                    bSearchComplete = true;
                    break;
                }

                // If searching for the MPQ header is disabled, return an error
                if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
                {
                    nError = ERROR_NOT_SUPPORTED;
                    bSearchComplete = true;
                    break;
                }

                // Move the pointers
                SearchOffset += 0x200;
                dwInBufferOffset += 0x200;
            }
        }

        // Did we identify one of the supported headers?
        if(nError == ERROR_SUCCESS)
        {
            // Set the user data position to the MPQ header, if none
            if(ha->pUserData == NULL)
                ha->UserDataPos = SearchOffset;

            // Set the position of the MPQ header
            ha->pHeader  = (TMPQHeader *)ha->HeaderData;
            ha->MpqPos   = SearchOffset;
            ha->FileSize = FileSize;

            // Sector size must be nonzero.
            if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
                nError = ERROR_BAD_FORMAT;
        }
    }

    // Fix table positions according to format
    if(nError == ERROR_SUCCESS)
    {
        // Dump the header
//      DumpMpqHeader(ha->pHeader);

        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
        // and ignores the MPQ format version as well. The trick is to
        // fake MPQ format 2, with an improper hi-word position of hash table and block table
        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0
        if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
        {
            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
            ha->pUserData = NULL;
        }

        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
        if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Remember whether whis is a map for Warcraft III
        if(bIsWarcraft3Map)
            ha->dwFlags |= MPQ_FLAG_WAR3_MAP;

        // Set the size of file sector
        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);

        // Verify if any of the tables doesn't start beyond the end of the file
        nError = VerifyMpqTablePositions(ha, FileSize);
    }

    // Read the hash table. Ignore the result, as hash table is no longer required
    // Read HET table. Ignore the result, as HET table is no longer required
    if(nError == ERROR_SUCCESS)
    {
        nError = LoadAnyHashTable(ha);
    }

    // Now, build the file table. It will be built by combining
    // the block table, BET table, hi-block table, (attributes) and (listfile).
    if(nError == ERROR_SUCCESS)
    {
        nError = BuildFileTable(ha);
    }

    // Load the internal listfile and include it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
    {
        // Quick check for (listfile)
        pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (listfile) is optional.
            SFileAddListFile((HANDLE)ha, NULL);
            ha->dwFileFlags1 = pFileEntry->dwFlags;
        }
    }

    // Load the "(attributes)" file and merge it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0 && (ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0)
    {
        // Quick check for (attributes)
        pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (attributes) is optional.
            SAttrLoadAttributes(ha);
            ha->dwFileFlags2 = pFileEntry->dwFlags;
        }
    }

    // Remember whether the archive has weak signature. Only for MPQs format 1.0.
    if(nError == ERROR_SUCCESS)
    {
        // Quick check for (signature)
        pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Just remember that the archive is weak-signed
            assert((pFileEntry->dwFlags & MPQ_FILE_EXISTS) != 0);
            ha->dwFileFlags3 = pFileEntry->dwFlags;
        }

        // Finally, set the MPQ_FLAG_READ_ONLY if the MPQ was found malformed
        ha->dwFlags |= (ha->dwFlags & MPQ_FLAG_MALFORMED) ? MPQ_FLAG_READ_ONLY : 0;
    }

    // Cleanup and exit
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeArchiveHandle(ha);
        SetLastError(nError);
        ha = NULL;
    }

    // Free the header buffer
    if(pbHeaderBuffer != NULL)
        STORM_FREE(pbHeaderBuffer);
    if(phMpq != NULL)
        *phMpq = ha;
    return (nError == ERROR_SUCCESS);
}
コード例 #21
0
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */)
{
    TFileStream * pTempStream = NULL;
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    ULONGLONG ByteOffset;
    ULONGLONG ByteCount;
    LPDWORD pFileKeys = NULL;
    TCHAR szTempFile[MAX_PATH] = _T("");
    TCHAR * szTemp = NULL;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(!IsValidMpqHandle(hMpq))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;

    // If the MPQ is changed at this moment, we have to flush the archive
    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED))
    {
        SFileFlushArchive(hMpq);
    }

    // Create the table with file keys
    if(nError == ERROR_SUCCESS)
    {
        if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL)
            memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // First of all, we have to check of we are able to decrypt all files.
    // If not, sorry, but the archive cannot be compacted.
    if(nError == ERROR_SUCCESS)
    {
        // Initialize the progress variables for compact callback
        FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes));
        ha->CompactBytesProcessed = 0;
        nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
    }

    // Get the temporary file name and create it
    if(nError == ERROR_SUCCESS)
    {
        _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
        if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
            _tcscpy(szTemp + 1, _T("mp_"));
        else
            _tcscat(szTempFile, _T("_"));

        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
        if(pTempStream == NULL)
            nError = GetLastError();
    }

    // Write the data before MPQ user data (if any)
    if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
    {
        // Inform the application about the progress
        if(ha->pfnCompactCB != NULL)
            ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);

        ByteOffset = 0;
        ByteCount = ha->UserDataPos;
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ user data (if any)
    if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
    {
        // At this point, we assume that the user data size is equal
        // to pUserData->dwHeaderOffs.
        // If this assumption doesn't work, then we have an unknown version of MPQ
        ByteOffset = ha->UserDataPos;
        ByteCount = ha->MpqPos - ha->UserDataPos;

        assert(ha->pUserData != NULL);
        assert(ha->pUserData->dwHeaderOffs == ByteCount);
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ header
    if(nError == ERROR_SUCCESS)
    {
        TMPQHeader SaveMpqHeader;

        // Write the MPQ header to the file
        memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4);
        if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize))
            nError = GetLastError();

        // Update the progress
        ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize;
    }

    // Now copy all files
    if(nError == ERROR_SUCCESS)
        nError = CopyMpqFiles(ha, pFileKeys, pTempStream);

    // Defragment the file table
    if(nError == ERROR_SUCCESS)
        nError = RebuildFileTable(ha, ha->pHeader->dwHashTableSize, ha->dwMaxFileCount);

    // We also need to rebuild the HET table, if any
    if(nError == ERROR_SUCCESS)
    {
        // Invalidate (listfile) and (attributes)
        InvalidateInternalFiles(ha);

        // Rebuild the HET table, if we have any
        if(ha->pHetTable != NULL)
            nError = RebuildHetTable(ha);
    }

    // If succeeded, switch the streams
    if(nError == ERROR_SUCCESS)
    {
        if(FileStream_Replace(ha->pStream, pTempStream))
            pTempStream = NULL;
        else
            nError = ERROR_CAN_NOT_COMPLETE;
    }

    // If all succeeded, save the MPQ tables
    if(nError == ERROR_SUCCESS)
    {
        //
        // Note: We don't recalculate position of the MPQ tables at this point.
        // SaveMPQTables does it automatically.
        // 

        nError = SaveMPQTables(ha);
        if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL)
        {
            ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
            ha->CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
            ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes);
        }
    }

    // Cleanup and return
    if(pTempStream != NULL)
        FileStream_Close(pTempStream);
    if(pFileKeys != NULL)
        STORM_FREE(pFileKeys);
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
コード例 #22
0
ファイル: ListFile.cpp プロジェクト: Deaod/CascLib
static void CloseStream_ExternalFile(void * pvCacheContext)
{
    TFileStream * pStream = (TFileStream *)pvCacheContext;

    return FileStream_Close(pStream);
}
コード例 #23
0
int EXPORT_SYMBOL SFileCompactArchive(void * hMpq, const char * szListFile, int bReserved)
{
    TFileStream * pTempStream = NULL;
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    uint64_t ByteOffset;
    uint64_t ByteCount;
    uint32_t * pFileKeys = NULL;
    char szTempFile[1024] = "";
    char * szTemp = NULL;
    int nError = ERROR_SUCCESS;

    /* Test the valid parameters */
    if(!IsValidMpqHandle(hMpq))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;

    /* If the MPQ is changed at this moment, we have to flush the archive */
    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED))
    {
        SFileFlushArchive(hMpq);
    }

    /* Create the table with file keys */
    if(nError == ERROR_SUCCESS)
    {
        if((pFileKeys = STORM_ALLOC(uint32_t, ha->dwFileTableSize)) != NULL)
            memset(pFileKeys, 0, sizeof(uint32_t) * ha->dwFileTableSize);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    /* First of all, we have to check of we are able to decrypt all files. */
    /* If not, sorry, but the archive cannot be compacted. */
    if(nError == ERROR_SUCCESS)
    {
        /* Initialize the progress variables for compact callback */
        FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes));
        ha->CompactBytesProcessed = 0;
        nError = CheckIfAllKeysKnown(ha, szListFile, pFileKeys);
    }

    /* Get the temporary file name and create it */
    if(nError == ERROR_SUCCESS)
    {
        strcpy(szTempFile, FileStream_GetFileName(ha->pStream));
        if((szTemp = strrchr(szTempFile, '.')) != NULL)
            strcpy(szTemp + 1, "mp_");
        else
            strcat(szTempFile, "_");

        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
        if(pTempStream == NULL)
            nError = GetLastError();
    }

    /* Write the data before MPQ user data (if any) */
    if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
    {
        /* Inform the application about the progress */
        if(ha->pfnCompactCB != NULL)
            ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);

        ByteOffset = 0;
        ByteCount = ha->UserDataPos;
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, &ByteOffset, ByteCount);
    }

    /* Write the MPQ user data (if any) */
    if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
    {
        /* At this point, we assume that the user data size is equal */
        /* to pUserData->dwHeaderOffs. */
        /* If this assumption doesn't work, then we have an unknown version of MPQ */
        ByteOffset = ha->UserDataPos;
        ByteCount = ha->MpqPos - ha->UserDataPos;

        assert(ha->pUserData != NULL);
        assert(ha->pUserData->dwHeaderOffs == ByteCount);
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, &ByteOffset, ByteCount);
    }

    /* Write the MPQ header */
    if(nError == ERROR_SUCCESS)
    {
        TMPQHeader SaveMpqHeader;

        /* Write the MPQ header to the file */
        memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4);
        if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize))
            nError = GetLastError();

        /* Update the progress */
        ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize;
    }

    /* Now copy all files */
    if(nError == ERROR_SUCCESS)
        nError = CopyMpqFiles(ha, pFileKeys, pTempStream);

    /* If succeeded, switch the streams */
    if(nError == ERROR_SUCCESS)
    {
        ha->dwFlags |= MPQ_FLAG_CHANGED;
        if(FileStream_Replace(ha->pStream, pTempStream))
            pTempStream = NULL;
        else
            nError = ERROR_CAN_NOT_COMPLETE;
    }

    /* Final user notification */
    if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL)
    {
        ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
        ha->CompactBytesProcessed += (ha->dwFileTableSize * sizeof(TMPQBlock));
        ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes);
    }

    /* Cleanup and return */
    if(pTempStream != NULL)
        FileStream_Close(pTempStream);
    if(pFileKeys != NULL)
        STORM_FREE(pFileKeys);
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}