mirror of
https://github.com/alexkulya/pandaria_5.4.8.git
synced 2025-12-13 22:37:04 +00:00
Init
This commit is contained in:
commit
068b923299
4319 changed files with 2080299 additions and 0 deletions
557
dep/StormLib/src/SFileListFile.cpp
Normal file
557
dep/StormLib/src/SFileListFile.cpp
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
/*****************************************************************************/
|
||||
/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 12.06.04 1.00 Lad The first version of SListFile.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
#include <assert.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile entry structure
|
||||
|
||||
#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
|
||||
|
||||
struct TListFileCache
|
||||
{
|
||||
HANDLE hFile; // Stormlib file handle
|
||||
char * szMask; // 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
|
||||
|
||||
BYTE Buffer[CACHE_BUFFER_SIZE]; // Listfile cache itself
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions (cache)
|
||||
|
||||
static TListFileCache * CreateListFileCache(HANDLE hMpq, const char * szListFile)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
HANDLE hListFile = NULL;
|
||||
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
|
||||
DWORD dwBytesRead = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// If the szListFile is NULL, it means we have to open internal listfile
|
||||
if(szListFile == NULL)
|
||||
{
|
||||
// Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load
|
||||
// the listfile even if there is only non-neutral version of the listfile in the MPQ
|
||||
dwSearchScope = SFILE_OPEN_ANY_LOCALE;
|
||||
szListFile = LISTFILE_NAME;
|
||||
}
|
||||
|
||||
// Open the local/internal listfile
|
||||
if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TMPQFile * hf = (TMPQFile *)hListFile;
|
||||
|
||||
// Remember flags for (listfile)
|
||||
if(hf->pFileEntry != NULL)
|
||||
ha->dwFileFlags1 = hf->pFileEntry->dwFlags;
|
||||
}
|
||||
else
|
||||
nError = GetLastError();
|
||||
|
||||
// Allocate cache for one file block
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1);
|
||||
if(pCache == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the file cache
|
||||
memset(pCache, 0, sizeof(TListFileCache));
|
||||
pCache->dwFileSize = SFileGetFileSize(hListFile, NULL);
|
||||
pCache->hFile = hListFile;
|
||||
|
||||
// Fill the cache
|
||||
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == 0)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Initialize the pointers
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
SetLastError(nError);
|
||||
pCache = NULL;
|
||||
}
|
||||
|
||||
// Return the cache
|
||||
return pCache;
|
||||
}
|
||||
|
||||
// Reloads the cache. Returns number of characters
|
||||
// that has been loaded into the cache.
|
||||
static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||
{
|
||||
DWORD dwBytesToRead;
|
||||
DWORD dwBytesRead = 0;
|
||||
|
||||
// Only do something if the cache is empty
|
||||
if(pCache->pPos >= pCache->pEnd)
|
||||
{
|
||||
// __TryReadBlock:
|
||||
|
||||
// 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, CACHE_BUFFER_SIZE, &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;
|
||||
|
||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||
for(;;)
|
||||
{
|
||||
// If we need to reload the cache, do it
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a non-whitespace character, stop
|
||||
if(*pCache->pPos > 0x20)
|
||||
break;
|
||||
|
||||
// Skip 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;
|
||||
|
||||
// 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(szExtraString[0] == '~' && szExtraString[1] == 'P')
|
||||
{
|
||||
szLine = szExtraString;
|
||||
*szExtraString = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the length of the line
|
||||
return (szLine - szLineBegin);
|
||||
}
|
||||
|
||||
static int CompareFileNodes(const void * p1, const void * p2)
|
||||
{
|
||||
char * szFileName1 = *(char **)p1;
|
||||
char * szFileName2 = *(char **)p2;
|
||||
|
||||
return _stricmp(szFileName1, szFileName2);
|
||||
}
|
||||
|
||||
static int WriteListFileLine(
|
||||
TMPQFile * hf,
|
||||
const char * szLine)
|
||||
{
|
||||
char szNewLine[2] = {0x0D, 0x0A};
|
||||
size_t nLength = strlen(szLine);
|
||||
int nError;
|
||||
|
||||
nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions (listfile nodes)
|
||||
|
||||
// Adds a name into the list of all names. For each locale in the MPQ,
|
||||
// one entry will be created
|
||||
// If the file name is already there, does nothing.
|
||||
int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHash * pFirstHash;
|
||||
TMPQHash * pHash;
|
||||
bool bNameEntryCreated = false;
|
||||
|
||||
// If we have HET table, use that one
|
||||
if(ha->pHetTable != NULL)
|
||||
{
|
||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(pFileEntry, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// If we have hash table, we use it
|
||||
if(bNameEntryCreated == false && ha->pHashTable != NULL)
|
||||
{
|
||||
// Look for the first hash table entry for the file
|
||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||
|
||||
// Go while we found something
|
||||
while(pHash != NULL)
|
||||
{
|
||||
// Is it a valid file table index ?
|
||||
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
}
|
||||
|
||||
// Now find the next language version of the file
|
||||
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_CAN_NOT_COMPLETE;
|
||||
}
|
||||
|
||||
// Saves the whole listfile into the MPQ.
|
||||
int SListFileSaveToMpq(TMPQArchive * ha)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
char * szPrevItem;
|
||||
char ** SortTable = NULL;
|
||||
DWORD dwFileSize = 0;
|
||||
size_t nFileNodes = 0;
|
||||
size_t i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate the table for sorting listfile
|
||||
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
||||
if(SortTable == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Construct the sort table
|
||||
// Note: in MPQs with multiple locale versions of the same file,
|
||||
// this code causes adding multiple listfile entries.
|
||||
// Since those MPQs were last time used in Starcraft,
|
||||
// we leave it as it is.
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
{
|
||||
// Only take existing items
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
|
||||
{
|
||||
// Ignore pseudo-names
|
||||
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
|
||||
{
|
||||
SortTable[nFileNodes++] = pFileEntry->szFileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the table
|
||||
qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
|
||||
|
||||
// Now parse the table of file names again - remove duplicates
|
||||
// and count file size.
|
||||
if(nFileNodes != 0)
|
||||
{
|
||||
// Count the 0-th item
|
||||
dwFileSize += (DWORD)strlen(SortTable[0]) + 2;
|
||||
szPrevItem = SortTable[0];
|
||||
|
||||
// Count all next items
|
||||
for(i = 1; i < nFileNodes; i++)
|
||||
{
|
||||
// If the item is the same like the last one, skip it
|
||||
if(_stricmp(SortTable[i], szPrevItem))
|
||||
{
|
||||
dwFileSize += (DWORD)strlen(SortTable[i]) + 2;
|
||||
szPrevItem = SortTable[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the flags for (listfile)
|
||||
if(ha->dwFileFlags1 == 0)
|
||||
ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize);
|
||||
|
||||
// Create the listfile in the MPQ
|
||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
||||
0,
|
||||
dwFileSize,
|
||||
LANG_NEUTRAL,
|
||||
ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
|
||||
&hf);
|
||||
// Add all file names
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Each name is followed by newline ("\x0D\x0A")
|
||||
szPrevItem = SortTable[0];
|
||||
nError = WriteListFileLine(hf, SortTable[0]);
|
||||
|
||||
// Count all next items
|
||||
for(i = 1; i < nFileNodes; i++)
|
||||
{
|
||||
// If the item is the same like the last one, skip it
|
||||
if(_stricmp(SortTable[i], szPrevItem))
|
||||
{
|
||||
WriteListFileLine(hf, SortTable[i]);
|
||||
szPrevItem = SortTable[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create the listfile in the MPQ
|
||||
dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2;
|
||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
||||
0,
|
||||
dwFileSize,
|
||||
LANG_NEUTRAL,
|
||||
MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING,
|
||||
&hf);
|
||||
|
||||
// Just add "(listfile)" there
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
WriteListFileLine(hf, LISTFILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the file in the MPQ
|
||||
if(hf != NULL)
|
||||
{
|
||||
SFileAddFile_Finish(hf);
|
||||
}
|
||||
|
||||
// Free buffers
|
||||
if(nError == ERROR_SUCCESS)
|
||||
ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE;
|
||||
if(SortTable != NULL)
|
||||
STORM_FREE(SortTable);
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File functions
|
||||
|
||||
// Adds a listfile into the MPQ archive.
|
||||
// Note that the function does not remove the
|
||||
int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
char szFileName[MAX_PATH];
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Add the listfile for each MPQ in the patch chain
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Load the listfile to cache
|
||||
pCache = CreateListFileCache(hMpq, szListFile);
|
||||
if(pCache == NULL)
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the node list. Add the node for every locale in the archive
|
||||
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
|
||||
SListFileCreateNodeForAllLocales(ha, szFileName);
|
||||
|
||||
// Also, add three special files to the listfile:
|
||||
// (listfile) itself, (attributes) and (signature)
|
||||
SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME);
|
||||
SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME);
|
||||
SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME);
|
||||
|
||||
// Delete the cache
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
|
||||
// Move to the next archive in the chain
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Passing through the listfile
|
||||
|
||||
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Initialize the structure with zeros
|
||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||
|
||||
// Load the listfile to cache
|
||||
pCache = CreateListFileCache(hMpq, szListFile);
|
||||
if(pCache == NULL)
|
||||
nError = GetLastError();
|
||||
|
||||
// Allocate file mask
|
||||
if(nError == ERROR_SUCCESS && szMask != NULL)
|
||||
{
|
||||
pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1);
|
||||
if(pCache->szMask != NULL)
|
||||
strcpy(pCache->szMask, szMask);
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Perform file search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup & exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||
SListFileFindClose((HANDLE)pCache);
|
||||
pCache = NULL;
|
||||
|
||||
SetLastError(nError);
|
||||
}
|
||||
return (HANDLE)pCache;
|
||||
}
|
||||
|
||||
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||
size_t nLength;
|
||||
bool bResult = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
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))
|
||||
{
|
||||
bResult = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool WINAPI SListFileFindClose(HANDLE hFind)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||
|
||||
if(pCache != NULL)
|
||||
{
|
||||
if(pCache->hFile != NULL)
|
||||
SFileCloseFile(pCache->hFile);
|
||||
if(pCache->szMask != NULL)
|
||||
STORM_FREE(pCache->szMask);
|
||||
|
||||
STORM_FREE(pCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue