+ Optimized loading listfile

This commit is contained in:
unknown
2015-05-04 11:42:00 +02:00
parent 9c0f0db993
commit abd17ec91e
3 changed files with 103 additions and 198 deletions

View File

@@ -21,16 +21,13 @@
struct TListFileCache
{
HANDLE hFile; // Stormlib file handle
char * szMask; // Self-relative pointer to file mask
DWORD dwFileSize; // Total size of the cached file
DWORD dwFilePos; // Position of the cache in the file
BYTE * pBegin; // The begin of the listfile cache
BYTE * pPos;
BYTE * pEnd; // The last character in the file cache
char * szWildCard; // Self-relative pointer to file mask
LPBYTE pBegin; // The begin of the listfile cache
LPBYTE pPos; // Current position in the cache
LPBYTE pEnd; // The last character in the file cache
BYTE Buffer[CACHE_BUFFER_SIZE];
// char MaskBuff[1] // Followed by the name mask (if any)
// char szWildCard[wildcard_length]; // Followed by the name mask (if any)
// char szListFile[listfile_length]; // Followed by the listfile (if any)
};
//-----------------------------------------------------------------------------
@@ -56,45 +53,46 @@ static bool FreeListFileCache(TListFileCache * pCache)
return true;
}
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask)
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szWildCard)
{
TListFileCache * pCache = NULL;
size_t nMaskLength = 0;
size_t cchWildCard = 0;
DWORD dwBytesRead = 0;
DWORD dwFileSize;
// Get the amount of bytes that need to be allocated
dwFileSize = SFileGetFileSize(hListFile, NULL);
if(dwFileSize == 0)
if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE)
return NULL;
// Append buffer for name mask, if any
if(szMask != NULL)
nMaskLength = strlen(szMask) + 1;
if(szWildCard != NULL)
cchWildCard = strlen(szWildCard) + 1;
// Allocate cache for one file block
pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + nMaskLength);
pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCard + dwFileSize + 1);
if(pCache != NULL)
{
// Clear the entire structure
memset(pCache, 0, sizeof(TListFileCache) + nMaskLength);
memset(pCache, 0, sizeof(TListFileCache) + cchWildCard);
// Shall we copy the mask?
if(szMask != NULL)
if(cchWildCard != NULL)
{
pCache->szMask = (char *)(pCache + 1);
memcpy(pCache->szMask, szMask, nMaskLength);
pCache->szWildCard = (char *)(pCache + 1);
memcpy(pCache->szWildCard, szWildCard, cchWildCard);
}
// Fill-in the rest of the cache pointers
pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCard + 1;
// Load the file cache from the file
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
if(dwBytesRead != 0)
// Load the entire listfile to the cache
SFileReadFile(hListFile, pCache->pBegin, dwFileSize, &dwBytesRead, NULL);
if(dwFileSize != 0)
{
// Allocate pointers
pCache->pBegin = pCache->pPos = &pCache->Buffer[0];
pCache->pEnd = pCache->pBegin + dwBytesRead;
pCache->dwFileSize = dwFileSize;
pCache->hFile = hListFile;
pCache->pPos = pCache->pBegin;
pCache->pEnd = pCache->pBegin + dwFileSize;
}
else
{
@@ -171,112 +169,52 @@ static void FreeNameCache(TMPQNameCache * pNameCache)
*/
#endif // _DEBUG
// Reloads the cache. Returns number of characters
// that has been loaded into the cache.
static DWORD ReloadListFileCache(TListFileCache * pCache)
static char * ReadListFileLine(TListFileCache * pCache, size_t * PtrLength)
{
DWORD dwBytesToRead;
DWORD dwBytesRead = 0;
// Only do something if the cache is empty
if(pCache->pPos >= pCache->pEnd)
{
// Move the file position forward
pCache->dwFilePos += CACHE_BUFFER_SIZE;
if(pCache->dwFilePos >= pCache->dwFileSize)
return 0;
// Get the number of bytes remaining
dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos;
if(dwBytesToRead > CACHE_BUFFER_SIZE)
dwBytesToRead = CACHE_BUFFER_SIZE;
// Load the next data chunk to the cache
SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN);
SFileReadFile(pCache->hFile, pCache->Buffer, dwBytesToRead, &dwBytesRead, NULL);
// If we didn't read anything, it might mean that the block
// of the file is not available (in case of partial MPQs).
// We stop reading the file at this point, because the rest
// of the listfile is unreliable
if(dwBytesRead == 0)
return 0;
// Set the buffer pointers
pCache->pBegin =
pCache->pPos = &pCache->Buffer[0];
pCache->pEnd = pCache->pBegin + dwBytesRead;
}
return dwBytesRead;
}
static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxChars)
{
char * szLineBegin = szLine;
char * szLineEnd = szLine + nMaxChars - 1;
char * szExtraString = NULL;
LPBYTE pbLineBegin;
LPBYTE pbLineEnd;
LPBYTE pbExtraString = NULL;
// Skip newlines, spaces, tabs and another non-printable stuff
for(;;)
while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
pCache->pPos++;
// Set the line begin and end
if(pCache->pPos >= pCache->pEnd)
return NULL;
pbLineBegin = pbLineEnd = pCache->pPos;
// Copy the remaining characters
while(pCache->pPos < pCache->pEnd && pCache->pPos[0] != 0x0A && pCache->pPos[0] != 0x0D)
{
// If we need to reload the cache, do it
if(pCache->pPos == pCache->pEnd)
{
if(ReloadListFileCache(pCache) == 0)
break;
}
// Blizzard listfiles can also contain information about patch:
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
if(pCache->pPos[0] == '~')
pbExtraString = pCache->pPos;
// If we found a non-whitespace character, stop
if(*pCache->pPos > 0x20)
break;
// Skip the character
// Copy the character
pCache->pPos++;
}
// Copy the remaining characters
while(szLine < szLineEnd)
{
// If we need to reload the cache, do it now and resume copying
if(pCache->pPos == pCache->pEnd)
{
if(ReloadListFileCache(pCache) == 0)
break;
}
// If we have found a newline, stop loading
if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
break;
// Blizzard listfiles can also contain information about patch:
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
if(*pCache->pPos == '~')
szExtraString = szLine;
// Remember that last occurence of a slash or backslash
// if(*pCache->pPos == '\\' || *pCache->pPos == '/')
// szPlainName = szLine + 1;
// Copy the character
*szLine++ = *pCache->pPos++;
}
// Terminate line with zero
*szLine = 0;
// If there was extra string after the file name, clear it
if(szExtraString != NULL)
if(pbExtraString != NULL)
{
if(szExtraString[0] == '~' && szExtraString[1] == 'P')
if(pbExtraString[0] == '~' && pbExtraString[1] == 'P')
{
szLine = szExtraString;
*szExtraString = 0;
pbLineEnd = pbExtraString;
pbLineEnd[0] = 0;
}
}
else
{
pbLineEnd = pCache->pPos++;
pbLineEnd[0] = 0;
}
// Return the length of the line
return (szLine - szLineBegin);
// Give the line to the caller
if(PtrLength != NULL)
PtrLength[0] = (size_t)(pbLineEnd - pbLineBegin);
return (char *)pbLineBegin;
}
static int CompareFileNodes(const void * p1, const void * p2)
@@ -486,16 +424,21 @@ static int SFileAddArbitraryListFile(
HANDLE hListFile)
{
TListFileCache * pCache = NULL;
size_t nLength;
char szFileName[MAX_PATH];
// Create the listfile cache for that file
pCache = CreateListFileCache(hListFile, NULL);
if(pCache != NULL)
{
// Load the node list. Add the node for every locale in the archive
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
SListFileCreateNodeForAllLocales(ha, szFileName);
char * szFileName;
size_t nLength = 0;
// Get the next line
while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
{
// Add the line to the MPQ
if(nLength != 0)
SListFileCreateNodeForAllLocales(ha, szFileName);
}
// Delete the cache
FreeListFileCache(pCache);
@@ -572,6 +515,36 @@ static int SFileAddInternalListFile(
return nError;
}
static bool DoListFileSearch(TListFileCache * pCache, SFILE_FIND_DATA * lpFindFileData)
{
// Check for the valid search handle
if(pCache != NULL)
{
char * szFileName;
size_t nLength = 0;
// Get the next line
while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
{
// Check search mask
if(nLength != 0 && CheckWildCard(szFileName, pCache->szWildCard))
{
if(nLength >= sizeof(lpFindFileData->cFileName))
nLength = sizeof(lpFindFileData->cFileName);
memcpy(lpFindFileData->cFileName, szFileName, nLength);
lpFindFileData->cFileName[nLength] = 0;
return true;
}
}
}
// No more files
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
SetLastError(ERROR_NO_MORE_FILES);
return false;
}
//-----------------------------------------------------------------------------
// File functions
@@ -609,9 +582,7 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
{
TListFileCache * pCache = NULL;
HANDLE hListFile = NULL;
size_t nLength = 0;
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
int nError = ERROR_SUCCESS;
// Initialize the structure with zeros
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
@@ -628,48 +599,15 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
// Open the local/internal listfile
if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
{
#ifdef _DEBUG
// TMPQNameCache * pNameCache = CreateNameCache(hListFile, szMask);
// FreeNameCache(pNameCache);
#endif
// Load the listfile to cache
pCache = CreateListFileCache(hListFile, szMask);
if(pCache != NULL)
{
// Iterate through the listfile
for(;;)
{
// Read the (next) line
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
if(nLength == 0)
{
nError = ERROR_NO_MORE_FILES;
break;
}
// If some mask entered, check it
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
break;
}
}
else
{
SFileCloseFile(hListFile);
nError = ERROR_FILE_CORRUPT;
}
}
else
{
nError = GetLastError();
SFileCloseFile(hListFile);
}
// Cleanup & exit
if(nError != ERROR_SUCCESS)
if(!DoListFileSearch(pCache, lpFindFileData))
{
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
SetLastError(ERROR_NO_MORE_FILES);
FreeListFileCache(pCache);
SetLastError(nError);
pCache = NULL;
}
@@ -679,46 +617,13 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TListFileCache * pCache = (TListFileCache *)hFind;
size_t nLength;
int nError = ERROR_INVALID_PARAMETER;
// Check for parameters
if(pCache != NULL)
{
for(;;)
{
// Read the (next) line
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
if(nLength == 0)
{
nError = ERROR_NO_MORE_FILES;
break;
}
// If some mask entered, check it
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
{
nError = ERROR_SUCCESS;
break;
}
}
}
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
return DoListFileSearch((TListFileCache *)hFind, lpFindFileData);
}
bool WINAPI SListFileFindClose(HANDLE hFind)
{
TListFileCache * pCache = (TListFileCache *)hFind;
if(pCache == NULL)
return false;
if(pCache->hFile != NULL)
SFileCloseFile(pCache->hFile);
return FreeListFileCache(pCache);
}