server/contrib/vmap_extractor_v2/stormlib/SFileFindFile.cpp

291 lines
9.1 KiB
C++

/*****************************************************************************/
/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* A module for file searching within MPQs */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "SCommon.h"
//-----------------------------------------------------------------------------
// Defines
#define LISTFILE_CACHE_SIZE 0x1000
//-----------------------------------------------------------------------------
// Local functions
static BOOL IsValidSearchHandle(TMPQSearch * hs)
{
if(hs == NULL || IsBadReadPtr(hs, sizeof(TMPQSearch)))
return FALSE;
if(!IsValidMpqHandle(hs->ha))
return FALSE;
return TRUE;
}
// This function compares a string with a wildcard search string.
// returns TRUE, when the string matches with the wildcard.
BOOL CheckWildCard(const char * szString, const char * szWildCard)
{
char * szTemp; // Temporary helper pointer
int nResult = 0; // For memcmp return values
int nMustNotMatch = 0; // Number of following chars int szString,
// which must not match with szWildCard
int nMustMatch = 0; // Number of the following characters,
// which must match
// When the string is empty, it does not match with every wildcard
if(*szString == 0)
return FALSE;
// When the mask is empty, it matches to every wildcard
if(szWildCard == NULL || *szWildCard == 0)
return FALSE;
// Do normal test
for(;;)
{
switch(*szWildCard)
{
case '*': // Means "every number of characters"
// Skip all asterisks
while(*szWildCard == '*')
szWildCard++;
// When no more characters in wildcard, it means that the strings match
if(*szWildCard == 0)
return TRUE;
// The next N characters must not agree
nMustNotMatch |= 0x70000000;
break;
case '?': // Means "One or no character"
while(*szWildCard == '?')
{
nMustNotMatch++;
szWildCard++;
}
break;
default:
// If the two characters match
if(toupper(*szString) == toupper(*szWildCard))
{
// When end of string, they agree
if(*szString == 0)
return TRUE;
nMustNotMatch = 0;
szWildCard++;
szString++;
break;
}
// If the next character must match, the string does not match
if(nMustNotMatch == 0)
return FALSE;
// Count the characters which must match after characters
// that must not match
szTemp = (char *)szWildCard;
nMustMatch = 0;
while(*szTemp != 0 && *szTemp != '*' && *szTemp != '?')
{
nMustMatch++;
szTemp++;
}
// Now skip characters from szString up to number of chars
// that must not match
nResult = -1;
while(nMustNotMatch > 0 && *szString != 0)
{
if((nResult = _strnicmp(szString, szWildCard, nMustMatch)) == 0)
break;
szString++;
nMustNotMatch--;
}
// Make one more comparison
if(nMustNotMatch == 0)
nResult = _strnicmp(szString, szWildCard, nMustMatch);
// If a match has been found, continue the search
if(nResult == 0)
{
nMustNotMatch = 0;
szWildCard += nMustMatch;
szString += nMustMatch;
break;
}
return FALSE;
}
}
}
// Performs one MPQ search
// TODO: Test for archives > 4GB
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
TMPQArchive * ha = hs->ha;
TFileNode * pNode;
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash = ha->pHashTable + hs->dwNextIndex;
// Do until some file found or no more files
while(pHash < pHashEnd)
{
pNode = ha->pListFile[hs->dwNextIndex++];
// If this entry is free, do nothing
if(pHash->dwBlockIndex < HASH_ENTRY_FREE && (DWORD_PTR)pNode < HASH_ENTRY_FREE)
{
// Check the file name.
if(CheckWildCard(pNode->szFileName, hs->szSearchMask))
{
TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex;
lpFindFileData->lcLocale = pHash->lcLocale;
lpFindFileData->dwFileSize = pBlock->dwFSize;
lpFindFileData->dwFileFlags = pBlock->dwFlags;
lpFindFileData->dwBlockIndex = pHash->dwBlockIndex;
lpFindFileData->dwCompSize = pBlock->dwCSize;
// Fill the file name and plain file name
strcpy(lpFindFileData->cFileName, pNode->szFileName);
lpFindFileData->szPlainName = strrchr(lpFindFileData->cFileName, '\\');
if(lpFindFileData->szPlainName == NULL)
lpFindFileData->szPlainName = lpFindFileData->cFileName;
else
lpFindFileData->szPlainName++;
// Fill the next entry
return ERROR_SUCCESS;
}
}
pHash++;
}
// No more files found, return error
return ERROR_NO_MORE_FILES;
}
// TODO: Test for archives > 4GB
static void FreeMPQSearch(TMPQSearch *& hs)
{
if(hs != NULL)
{
FREEMEM(hs);
hs = NULL;
}
}
//-----------------------------------------------------------------------------
// Public functions
// TODO: Test for archives > 4GB
HANDLE WINAPI SFileFindFirstFile(HANDLE hMPQ, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile)
{
TMPQArchive * ha = (TMPQArchive *)hMPQ;
TMPQSearch * hs = NULL; // Search object handle
size_t nSize = 0;
int nError = ERROR_SUCCESS;
// Check for the valid parameters
if(nError == ERROR_SUCCESS)
{
if(!IsValidMpqHandle(ha))
nError = ERROR_INVALID_PARAMETER;
if(szMask == NULL || lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
if(szListFile == NULL && !IsValidMpqHandle(ha))
nError = ERROR_INVALID_PARAMETER;
}
// Include the listfile into the MPQ's internal listfile
// Note that if the listfile name is NULL, do nothing because the
// internal listfile is always included.
if(nError == ERROR_SUCCESS && szListFile != NULL)
nError = SFileAddListFile((HANDLE)ha, szListFile);
// Allocate the structure for MPQ search
if(nError == ERROR_SUCCESS)
{
nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
if((hs = (TMPQSearch *)ALLOCMEM(char, nSize)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform the first search
if(nError == ERROR_SUCCESS)
{
memset(hs, 0, sizeof(TMPQSearch));
hs->ha = ha;
hs->dwNextIndex = 0;
strcpy(hs->szSearchMask, szMask);
nError = DoMPQSearch(hs, lpFindFileData);
}
// Cleanup
if(nError != ERROR_SUCCESS)
{
FreeMPQSearch(hs);
SetLastError(nError);
}
// Return the result value
return (HANDLE)hs;
}
// TODO: Test for archives > 4GB
BOOL WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TMPQSearch * hs = (TMPQSearch *)hFind;
int nError = ERROR_SUCCESS;
// Check the parameters
if(nError == ERROR_SUCCESS)
{
if(!IsValidSearchHandle(hs) || lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
}
if(nError == ERROR_SUCCESS)
nError = DoMPQSearch(hs, lpFindFileData);
if(nError != ERROR_SUCCESS)
{
SetLastError(nError);
return FALSE;
}
return TRUE;
}
// TODO: Test for archives > 4GB
BOOL WINAPI SFileFindClose(HANDLE hFind)
{
TMPQSearch * hs = (TMPQSearch *)hFind;
// Check the parameters
if(!IsValidSearchHandle(hs))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FreeMPQSearch(hs);
return TRUE;
}