📄 slistfile.cpp.svn-base
字号:
/*****************************************************************************/
/* 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 "SCommon.h"
#include <assert.h>
//-----------------------------------------------------------------------------
// Listfile entry structure
#define LISTFILE_CACHE_SIZE 0x1000 // Size of one cache element
#define NO_MORE_CHARACTERS 256
#define HASH_TABLE_SIZE 31 // Initial hash table size (should be a prime number)
// TODO: Check on x64 !!!
#define LISTFILE_ENTRY_DELETED (DWORD_PTR)(-2)
#define LISTFILE_ENTRY_FREE (DWORD_PTR)(-1)
struct TListFileCache
{
HANDLE hFile; // Stormlib file handle
char * szMask; // File mask
DWORD dwFileSize; // Total size of the cached file
DWORD dwBuffSize; // File of the cache
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[1]; // Listfile cache itself
};
//-----------------------------------------------------------------------------
// Local functions (cache)
// Reloads the cache. Returns number of characters
// that has been loaded into the cache.
static int ReloadCache(TListFileCache * pCache)
{
// Check if there is enough characters in the cache
// If not, we have to reload the next block
if(pCache->pPos >= pCache->pEnd)
{
// If the cache is already at the end, do nothing more
if((pCache->dwFilePos + pCache->dwBuffSize) >= pCache->dwFileSize)
return 0;
pCache->dwFilePos += pCache->dwBuffSize;
SFileReadFile(pCache->hFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
if(pCache->dwBuffSize == 0)
return 0;
// Set the buffer pointers
pCache->pBegin =
pCache->pPos = &pCache->Buffer[0];
pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
}
return pCache->dwBuffSize;
}
static size_t ReadLine(TListFileCache * pCache, char * szLine, int nMaxChars)
{
char * szLineBegin = szLine;
char * szLineEnd = szLine + nMaxChars - 1;
__BeginLoading:
// Skip newlines, spaces, tabs and another non-printable stuff
while(pCache->pPos < pCache->pEnd && *pCache->pPos <= 0x20)
pCache->pPos++;
// Copy the remaining characters
while(pCache->pPos < pCache->pEnd && szLine < szLineEnd)
{
// If we have found a newline, stop loading
if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
break;
*szLine++ = *pCache->pPos++;
}
// If we now need to reload the cache, do it
if(pCache->pPos == pCache->pEnd)
{
if(ReloadCache(pCache) > 0)
goto __BeginLoading;
}
*szLine = 0;
return (szLine - szLineBegin);
}
//-----------------------------------------------------------------------------
// Local functions (listfile nodes)
// This function creates the name for the listfile.
// the file will be created under unique name in the temporary directory
static void GetListFileName(TMPQArchive * /* ha */, char * szListFile)
{
char szTemp[MAX_PATH];
// Create temporary file name int TEMP directory
GetTempPath(sizeof(szTemp)-1, szTemp);
GetTempFileName(szTemp, LISTFILE_NAME, 0, szListFile);
}
// Creates new listfile. The listfile is an array of TListFileNode
// structures. The size of the array is the same like the hash table size,
// the ordering is the same too (listfile item index is the same like
// the index in the MPQ hash table)
int SListFileCreateListFile(TMPQArchive * ha)
{
DWORD dwItems = ha->pHeader->dwHashTableSize;
// The listfile should be NULL now
assert(ha->pListFile == NULL);
ha->pListFile = ALLOCMEM(TFileNode *, dwItems);
if(ha->pListFile == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
memset(ha->pListFile, 0xFF, dwItems * sizeof(TFileNode *));
return ERROR_SUCCESS;
}
// Adds a filename into the listfile. If the file name is already there,
// does nothing.
int SListFileAddNode(TMPQArchive * ha, const char * szFileName)
{
TFileNode * pNode = NULL;
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash0 = GetHashEntry(ha, szFileName);
TMPQHash * pHash = pHash0;
DWORD dwHashIndex = 0;
size_t nLength; // File name lentgth
DWORD dwName1;
DWORD dwName2;
// If the file does not exist within the MPQ, do nothing
if(pHash == NULL)
return ERROR_SUCCESS;
// If the listfile entry already exists, do nothing
dwHashIndex = (DWORD)(pHash - ha->pHashTable);
dwName1 = pHash->dwName1;
dwName2 = pHash->dwName2;
if((DWORD_PTR)ha->pListFile[dwHashIndex] <= LISTFILE_ENTRY_DELETED)
return ERROR_SUCCESS;
// Create the listfile node and insert it into the listfile table
nLength = strlen(szFileName);
pNode = (TFileNode *)ALLOCMEM(char, sizeof(TFileNode) + nLength);
pNode->dwRefCount = 0;
pNode->nLength = nLength;
strcpy(pNode->szFileName, szFileName);
// Fill the nodes for all language versions
while(pHash->dwBlockIndex < LISTFILE_ENTRY_DELETED)
{
if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2)
{
pNode->dwRefCount++;
ha->pListFile[pHash - ha->pHashTable] = pNode;
}
if(++pHash >= pHashEnd)
pHash = ha->pHashTable;
if(pHash == pHash0)
break;
}
return ERROR_SUCCESS;
}
// Removes a filename from the listfile.
// If the name is not there, does nothing
int SListFileRemoveNode(TMPQArchive * ha, const char * szFileName)
{
TFileNode * pNode = NULL;
TMPQHash * pHash = GetHashEntry(ha, szFileName);
size_t nHashIndex = 0;
if(pHash != NULL)
{
nHashIndex = pHash - ha->pHashTable;
pNode = ha->pListFile[nHashIndex];
ha->pListFile[nHashIndex] = (TFileNode *)LISTFILE_ENTRY_DELETED;
// If the reference count has reached zero, do nothing
if(--pNode->dwRefCount == 0)
FREEMEM(pNode);
}
return ERROR_SUCCESS;
}
// Renames a node. We will not deal with the renaming, we'll simply
// remove the old node and insert the new one.
// TODO: Test for archives > 4GB
int SListFileRenameNode(TMPQArchive * ha, const char * szOldFileName, const char * szNewFileName)
{
SListFileRemoveNode(ha, szOldFileName);
return SListFileAddNode(ha, szNewFileName);
}
// TODO: Test for archives > 4GB
int SListFileFreeListFile(TMPQArchive * ha)
{
if(ha->pListFile != NULL)
{
for(DWORD i = 0; i < ha->pHeader->dwHashTableSize; i++)
{
TFileNode * pNode = ha->pListFile[i];
if((DWORD_PTR)pNode < LISTFILE_ENTRY_FREE)
{
if(--pNode->dwRefCount == 0)
{
FREEMEM(pNode);
ha->pListFile[i] = (TFileNode *)LISTFILE_ENTRY_FREE;
}
}
}
FREEMEM(ha->pListFile);
ha->pListFile = NULL;
}
return ERROR_SUCCESS;
}
// Saves the whole listfile into the MPQ.
// TODO: Test for archives > 4GB
int SListFileSaveToMpq(TMPQArchive * ha)
{
TFileNode * pNode = NULL;
TMPQHash * pHashEnd = NULL;
TMPQHash * pHash0 = NULL;
TMPQHash * pHash = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
char szListFile[MAX_PATH];
char szBuffer[MAX_PATH+4];
DWORD dwTransferred;
size_t nLength = 0;
DWORD dwName1 = 0;
DWORD dwName2 = 0;
LCID lcSave = lcLocale;
int nError = ERROR_SUCCESS;
// If no listfile, do nothing
if(ha->pListFile == NULL)
return ERROR_SUCCESS;
// Create the local listfile
if(nError == ERROR_SUCCESS)
{
GetListFileName(ha, szListFile);
hFile = CreateFile(szListFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
if(hFile == INVALID_HANDLE_VALUE)
nError = GetLastError();
}
// Find the hash entry corresponding to listfile
pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
pHash0 = pHash = GetHashEntry(ha, 0);
if(pHash == NULL)
pHash0 = pHash = ha->pHashTable;
// Save the file
if(nError == ERROR_SUCCESS)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -