Updated item stuff
[getmangos.git] / contrib / vmap_extractor_v2 / stormlib / SListFile.cpp
blobc3723d17dcd898f2fc9a1baac68cf65d59c61461
1 /*****************************************************************************/
2 /* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
3 /*---------------------------------------------------------------------------*/
4 /* Description: */
5 /*---------------------------------------------------------------------------*/
6 /* Date Ver Who Comment */
7 /* -------- ---- --- ------- */
8 /* 12.06.04 1.00 Lad The first version of SListFile.cpp */
9 /*****************************************************************************/
11 #define __STORMLIB_SELF__
12 #include "StormLib.h"
13 #include "SCommon.h"
14 #include <assert.h>
16 //-----------------------------------------------------------------------------
17 // Listfile entry structure
19 #define LISTFILE_CACHE_SIZE 0x1000 // Size of one cache element
20 #define NO_MORE_CHARACTERS 256
21 #define HASH_TABLE_SIZE 31 // Initial hash table size (should be a prime number)
23 // TODO: Check on x64 !!!
24 #define LISTFILE_ENTRY_DELETED (DWORD_PTR)(-2)
25 #define LISTFILE_ENTRY_FREE (DWORD_PTR)(-1)
27 struct TListFileCache
29 HANDLE hFile; // Stormlib file handle
30 char * szMask; // File mask
31 DWORD dwFileSize; // Total size of the cached file
32 DWORD dwBuffSize; // File of the cache
33 DWORD dwFilePos; // Position of the cache in the file
34 BYTE * pBegin; // The begin of the listfile cache
35 BYTE * pPos;
36 BYTE * pEnd; // The last character in the file cache
38 BYTE Buffer[1]; // Listfile cache itself
41 //-----------------------------------------------------------------------------
42 // Local functions (cache)
44 // Reloads the cache. Returns number of characters
45 // that has been loaded into the cache.
46 static int ReloadCache(TListFileCache * pCache)
48 // Check if there is enough characters in the cache
49 // If not, we have to reload the next block
50 if(pCache->pPos >= pCache->pEnd)
52 // If the cache is already at the end, do nothing more
53 if((pCache->dwFilePos + pCache->dwBuffSize) >= pCache->dwFileSize)
54 return 0;
56 pCache->dwFilePos += pCache->dwBuffSize;
57 SFileReadFile(pCache->hFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
58 if(pCache->dwBuffSize == 0)
59 return 0;
61 // Set the buffer pointers
62 pCache->pBegin =
63 pCache->pPos = &pCache->Buffer[0];
64 pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
67 return pCache->dwBuffSize;
70 static size_t ReadLine(TListFileCache * pCache, char * szLine, int nMaxChars)
72 char * szLineBegin = szLine;
73 char * szLineEnd = szLine + nMaxChars - 1;
75 __BeginLoading:
77 // Skip newlines, spaces, tabs and another non-printable stuff
78 while(pCache->pPos < pCache->pEnd && *pCache->pPos <= 0x20)
79 pCache->pPos++;
81 // Copy the remaining characters
82 while(pCache->pPos < pCache->pEnd && szLine < szLineEnd)
84 // If we have found a newline, stop loading
85 if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
86 break;
88 *szLine++ = *pCache->pPos++;
91 // If we now need to reload the cache, do it
92 if(pCache->pPos == pCache->pEnd)
94 if(ReloadCache(pCache) > 0)
95 goto __BeginLoading;
98 *szLine = 0;
99 return (szLine - szLineBegin);
102 //-----------------------------------------------------------------------------
103 // Local functions (listfile nodes)
105 // This function creates the name for the listfile.
106 // the file will be created under unique name in the temporary directory
107 static void GetListFileName(TMPQArchive * /* ha */, char * szListFile)
109 char szTemp[MAX_PATH];
111 // Create temporary file name int TEMP directory
112 GetTempPath(sizeof(szTemp)-1, szTemp);
113 GetTempFileName(szTemp, LISTFILE_NAME, 0, szListFile);
116 // Creates new listfile. The listfile is an array of TListFileNode
117 // structures. The size of the array is the same like the hash table size,
118 // the ordering is the same too (listfile item index is the same like
119 // the index in the MPQ hash table)
121 int SListFileCreateListFile(TMPQArchive * ha)
123 DWORD dwItems = ha->pHeader->dwHashTableSize;
125 // The listfile should be NULL now
126 assert(ha->pListFile == NULL);
128 ha->pListFile = ALLOCMEM(TFileNode *, dwItems);
129 if(ha->pListFile == NULL)
130 return ERROR_NOT_ENOUGH_MEMORY;
132 memset(ha->pListFile, 0xFF, dwItems * sizeof(TFileNode *));
133 return ERROR_SUCCESS;
136 // Adds a filename into the listfile. If the file name is already there,
137 // does nothing.
138 int SListFileAddNode(TMPQArchive * ha, const char * szFileName)
140 TFileNode * pNode = NULL;
141 TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
142 TMPQHash * pHash0 = GetHashEntry(ha, szFileName);
143 TMPQHash * pHash = pHash0;
144 DWORD dwHashIndex = 0;
145 size_t nLength; // File name lentgth
146 DWORD dwName1;
147 DWORD dwName2;
149 // If the file does not exist within the MPQ, do nothing
150 if(pHash == NULL)
151 return ERROR_SUCCESS;
153 // If the listfile entry already exists, do nothing
154 dwHashIndex = (DWORD)(pHash - ha->pHashTable);
155 dwName1 = pHash->dwName1;
156 dwName2 = pHash->dwName2;
157 if((DWORD_PTR)ha->pListFile[dwHashIndex] <= LISTFILE_ENTRY_DELETED)
158 return ERROR_SUCCESS;
160 // Create the listfile node and insert it into the listfile table
161 nLength = strlen(szFileName);
162 pNode = (TFileNode *)ALLOCMEM(char, sizeof(TFileNode) + nLength);
163 pNode->dwRefCount = 0;
164 pNode->nLength = nLength;
165 strcpy(pNode->szFileName, szFileName);
167 // Fill the nodes for all language versions
168 while(pHash->dwBlockIndex < LISTFILE_ENTRY_DELETED)
170 if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2)
172 pNode->dwRefCount++;
173 ha->pListFile[pHash - ha->pHashTable] = pNode;
176 if(++pHash >= pHashEnd)
177 pHash = ha->pHashTable;
178 if(pHash == pHash0)
179 break;
182 return ERROR_SUCCESS;
185 // Removes a filename from the listfile.
186 // If the name is not there, does nothing
187 int SListFileRemoveNode(TMPQArchive * ha, const char * szFileName)
189 TFileNode * pNode = NULL;
190 TMPQHash * pHash = GetHashEntry(ha, szFileName);
191 size_t nHashIndex = 0;
193 if(pHash != NULL)
195 nHashIndex = pHash - ha->pHashTable;
196 pNode = ha->pListFile[nHashIndex];
197 ha->pListFile[nHashIndex] = (TFileNode *)LISTFILE_ENTRY_DELETED;
199 // If the reference count has reached zero, do nothing
200 if(--pNode->dwRefCount == 0)
201 FREEMEM(pNode);
203 return ERROR_SUCCESS;
207 // Renames a node. We will not deal with the renaming, we'll simply
208 // remove the old node and insert the new one.
209 // TODO: Test for archives > 4GB
210 int SListFileRenameNode(TMPQArchive * ha, const char * szOldFileName, const char * szNewFileName)
212 SListFileRemoveNode(ha, szOldFileName);
213 return SListFileAddNode(ha, szNewFileName);
216 // TODO: Test for archives > 4GB
217 int SListFileFreeListFile(TMPQArchive * ha)
219 if(ha->pListFile != NULL)
221 for(DWORD i = 0; i < ha->pHeader->dwHashTableSize; i++)
223 TFileNode * pNode = ha->pListFile[i];
225 if((DWORD_PTR)pNode < LISTFILE_ENTRY_FREE)
227 if(--pNode->dwRefCount == 0)
229 FREEMEM(pNode);
230 ha->pListFile[i] = (TFileNode *)LISTFILE_ENTRY_FREE;
235 FREEMEM(ha->pListFile);
236 ha->pListFile = NULL;
239 return ERROR_SUCCESS;
242 // Saves the whole listfile into the MPQ.
243 // TODO: Test for archives > 4GB
244 int SListFileSaveToMpq(TMPQArchive * ha)
246 TFileNode * pNode = NULL;
247 TMPQHash * pHashEnd = NULL;
248 TMPQHash * pHash0 = NULL;
249 TMPQHash * pHash = NULL;
250 HANDLE hFile = INVALID_HANDLE_VALUE;
251 char szListFile[MAX_PATH];
252 char szBuffer[MAX_PATH+4];
253 DWORD dwTransferred;
254 size_t nLength = 0;
255 DWORD dwName1 = 0;
256 DWORD dwName2 = 0;
257 LCID lcSave = lcLocale;
258 int nError = ERROR_SUCCESS;
260 // If no listfile, do nothing
261 if(ha->pListFile == NULL)
262 return ERROR_SUCCESS;
264 // Create the local listfile
265 if(nError == ERROR_SUCCESS)
267 GetListFileName(ha, szListFile);
268 hFile = CreateFile(szListFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
269 if(hFile == INVALID_HANDLE_VALUE)
270 nError = GetLastError();
273 // Find the hash entry corresponding to listfile
274 pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
275 pHash0 = pHash = GetHashEntry(ha, 0);
276 if(pHash == NULL)
277 pHash0 = pHash = ha->pHashTable;
279 // Save the file
280 if(nError == ERROR_SUCCESS)
282 for(;;)
284 if(pHash->dwName1 != dwName1 && pHash->dwName2 != dwName2 && pHash->dwBlockIndex < LISTFILE_ENTRY_DELETED)
286 dwName1 = pHash->dwName1;
287 dwName2 = pHash->dwName2;
288 pNode = ha->pListFile[pHash - ha->pHashTable];
290 if((DWORD_PTR)pNode < LISTFILE_ENTRY_DELETED)
292 memcpy(szBuffer, pNode->szFileName, pNode->nLength);
293 szBuffer[pNode->nLength + 0] = 0x0D;
294 szBuffer[pNode->nLength + 1] = 0x0A;
295 WriteFile(hFile, szBuffer, (DWORD)(pNode->nLength + 2), &dwTransferred, NULL);
299 if(++pHash >= pHashEnd)
300 pHash = ha->pHashTable;
301 if(pHash == pHash0)
302 break;
305 // Write the listfile name (if not already there)
306 if(GetHashEntry(ha, LISTFILE_NAME) == NULL)
308 nLength = strlen(LISTFILE_NAME);
309 memcpy(szBuffer, LISTFILE_NAME, nLength);
310 szBuffer[nLength + 0] = 0x0D;
311 szBuffer[nLength + 1] = 0x0A;
312 WriteFile(hFile, szBuffer, (DWORD)(nLength + 2), &dwTransferred, NULL);
315 // Add the listfile into the archive.
316 SFileSetLocale(LANG_NEUTRAL);
317 nError = AddFileToArchive(ha, hFile, LISTFILE_NAME, MPQ_FILE_COMPRESS_PKWARE | MPQ_FILE_ENCRYPTED | MPQ_FILE_REPLACEEXISTING, 0, SFILE_TYPE_DATA, NULL);
320 // Close the temporary file. This will delete it too.
321 if(hFile != INVALID_HANDLE_VALUE)
322 CloseHandle(hFile);
324 lcLocale = lcSave;
325 return nError;
328 //-----------------------------------------------------------------------------
329 // File functions
331 // Adds a listfile into the MPQ archive.
332 // Note that the function does not remove the
333 // TODO: Test for archives > 4GB
334 int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
336 TListFileCache * pCache = NULL;
337 TMPQArchive * ha = (TMPQArchive *)hMpq;
338 HANDLE hListFile = NULL;
339 char szFileName[MAX_PATH + 1];
340 DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
341 DWORD dwCacheSize = 0;
342 DWORD dwFileSize = 0;
343 size_t nLength = 0;
344 int nError = ERROR_SUCCESS;
346 // If the szListFile is NULL, it means we have to open internal listfile
347 if(szListFile == NULL)
349 szListFile = LISTFILE_NAME;
350 dwSearchScope = SFILE_OPEN_FROM_MPQ;
353 // Open the local/internal listfile
354 if(nError == ERROR_SUCCESS)
356 if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile))
357 nError = GetLastError();
360 if(nError == ERROR_SUCCESS)
362 dwCacheSize =
363 dwFileSize = SFileGetFileSize(hListFile, NULL);
365 // Try to allocate memory for the complete file. If it fails,
366 // load the part of the file
367 pCache = (TListFileCache *)ALLOCMEM(char, (sizeof(TListFileCache) + dwCacheSize));
368 if(pCache == NULL)
370 dwCacheSize = LISTFILE_CACHE_SIZE;
371 pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
374 if(pCache == NULL)
375 nError = ERROR_NOT_ENOUGH_MEMORY;
378 if(nError == ERROR_SUCCESS)
380 // Initialize the file cache
381 memset(pCache, 0, sizeof(TListFileCache));
382 pCache->hFile = hListFile;
383 pCache->dwFileSize = dwFileSize;
384 pCache->dwBuffSize = dwCacheSize;
385 pCache->dwFilePos = 0;
387 // Fill the cache
388 SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
390 // Initialize the pointers
391 pCache->pBegin =
392 pCache->pPos = &pCache->Buffer[0];
393 pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
395 // Load the node tree
396 while((nLength = ReadLine(pCache, szFileName, sizeof(szFileName) - 1)) > 0)
397 SListFileAddNode(ha, szFileName);
399 // Add well-known names
400 // Sometimes, they are not in listfile, but they exist in the archive
401 SListFileAddNode(ha, LISTFILE_NAME);
402 SListFileAddNode(ha, SIGNATURE_NAME);
403 SListFileAddNode(ha, ATTRIBUTES_NAME);
406 // Cleanup & exit
407 if(pCache != NULL)
408 SListFileFindClose((HANDLE)pCache);
409 return nError;
412 //-----------------------------------------------------------------------------
413 // Passing through the listfile
415 // TODO: Test for archives > 4GB
416 HANDLE SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
418 TListFileCache * pCache = NULL;
419 TMPQArchive * ha = (TMPQArchive *)hMpq;
420 HANDLE hListFile = NULL;
421 DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
422 DWORD dwCacheSize = 0;
423 DWORD dwFileSize = 0;
424 size_t nLength = 0;
425 int nError = ERROR_SUCCESS;
427 // Initialize the structure with zeros
428 memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
430 // If the szListFile is NULL, it means we have to open internal listfile
431 if(szListFile == NULL)
433 szListFile = LISTFILE_NAME;
434 dwSearchScope = SFILE_OPEN_FROM_MPQ;
437 // Open the local/internal listfile
438 if(nError == ERROR_SUCCESS)
440 if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile))
441 nError = GetLastError();
444 if(nError == ERROR_SUCCESS)
446 dwCacheSize =
447 dwFileSize = SFileGetFileSize(hListFile, NULL);
449 // Try to allocate memory for the complete file. If it fails,
450 // load the part of the file
451 pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
452 if(pCache == NULL)
454 dwCacheSize = LISTFILE_CACHE_SIZE;
455 pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
458 if(pCache == NULL)
459 nError = ERROR_NOT_ENOUGH_MEMORY;
462 if(nError == ERROR_SUCCESS)
464 // Initialize the file cache
465 memset(pCache, 0, sizeof(TListFileCache));
466 pCache->hFile = hListFile;
467 pCache->dwFileSize = dwFileSize;
468 pCache->dwBuffSize = dwCacheSize;
469 pCache->dwFilePos = 0;
470 if(szMask != NULL)
472 pCache->szMask = ALLOCMEM(char, strlen(szMask) + 1);
473 strcpy(pCache->szMask, szMask);
476 // Fill the cache
477 SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
479 // Initialize the pointers
480 pCache->pBegin =
481 pCache->pPos = &pCache->Buffer[0];
482 pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
484 for(;;)
486 // Read the (next) line
487 nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
488 if(nLength == 0)
490 nError = ERROR_NO_MORE_FILES;
491 break;
494 // If some mask entered, check it
495 if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
496 break;
500 // Cleanup & exit
501 if(nError != ERROR_SUCCESS)
503 memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
504 SListFileFindClose((HANDLE)pCache);
505 pCache = NULL;
507 SetLastError(nError);
509 return (HANDLE)pCache;
512 // TODO: Test for archives > 4GB
513 BOOL SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
515 TListFileCache * pCache = (TListFileCache *)hFind;
516 size_t nLength;
517 BOOL bResult = FALSE;
518 int nError = ERROR_SUCCESS;
520 for(;;)
522 // Read the (next) line
523 nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
524 if(nLength == 0)
526 nError = ERROR_NO_MORE_FILES;
527 break;
530 // If some mask entered, check it
531 if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
533 bResult = TRUE;
534 break;
538 if(nError != ERROR_SUCCESS)
539 SetLastError(nError);
540 return bResult;
543 // TODO: Test for archives > 4GB
544 BOOL SListFileFindClose(HANDLE hFind)
546 TListFileCache * pCache = (TListFileCache *)hFind;
548 if(pCache != NULL)
550 if(pCache->hFile != NULL)
551 SFileCloseFile(pCache->hFile);
552 if(pCache->szMask != NULL)
553 FREEMEM(pCache->szMask);
555 FREEMEM(pCache);
556 return TRUE;
559 return FALSE;