/StormLib/stormlib/SListFile.cpp

http://ghostcb.googlecode.com/ · C++ · 599 lines · 417 code · 91 blank · 91 comment · 126 complexity · 8e3e36028e8bb17aba6628fc966b16e3 MD5 · raw file

  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. /*****************************************************************************/
  10. #define __STORMLIB_SELF__
  11. #include "StormLib.h"
  12. #include "SCommon.h"
  13. #include <assert.h>
  14. //-----------------------------------------------------------------------------
  15. // Listfile entry structure
  16. #define LISTFILE_CACHE_SIZE 0x1000 // Size of one cache element
  17. #define NO_MORE_CHARACTERS 256
  18. #define HASH_TABLE_SIZE 31 // Initial hash table size (should be a prime number)
  19. struct TListFileCache
  20. {
  21. HANDLE hFile; // Stormlib file handle
  22. char * szMask; // File mask
  23. DWORD dwFileSize; // Total size of the cached file
  24. DWORD dwBuffSize; // File of the cache
  25. DWORD dwFilePos; // Position of the cache in the file
  26. BYTE * pBegin; // The begin of the listfile cache
  27. BYTE * pPos;
  28. BYTE * pEnd; // The last character in the file cache
  29. BYTE Buffer[1]; // Listfile cache itself
  30. };
  31. //-----------------------------------------------------------------------------
  32. // Local functions (cache)
  33. // Reloads the cache. Returns number of characters
  34. // that has been loaded into the cache.
  35. static int ReloadCache(TListFileCache * pCache)
  36. {
  37. // Check if there is enough characters in the cache
  38. // If not, we have to reload the next block
  39. if(pCache->pPos >= pCache->pEnd)
  40. {
  41. // If the cache is already at the end, do nothing more
  42. if((pCache->dwFilePos + pCache->dwBuffSize) >= pCache->dwFileSize)
  43. return 0;
  44. pCache->dwFilePos += pCache->dwBuffSize;
  45. SFileReadFile(pCache->hFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
  46. if(pCache->dwBuffSize == 0)
  47. return 0;
  48. // Set the buffer pointers
  49. pCache->pBegin =
  50. pCache->pPos = &pCache->Buffer[0];
  51. pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
  52. }
  53. return pCache->dwBuffSize;
  54. }
  55. static size_t ReadLine(TListFileCache * pCache, char * szLine, int nMaxChars)
  56. {
  57. char * szLineBegin = szLine;
  58. char * szLineEnd = szLine + nMaxChars - 1;
  59. __BeginLoading:
  60. // Skip newlines, spaces, tabs and another non-printable stuff
  61. while(pCache->pPos < pCache->pEnd && *pCache->pPos <= 0x20)
  62. pCache->pPos++;
  63. // Copy the remaining characters
  64. while(pCache->pPos < pCache->pEnd && szLine < szLineEnd)
  65. {
  66. // If we have found a newline, stop loading
  67. if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
  68. break;
  69. *szLine++ = *pCache->pPos++;
  70. }
  71. // If we now need to reload the cache, do it
  72. if(pCache->pPos == pCache->pEnd)
  73. {
  74. if(ReloadCache(pCache) > 0)
  75. goto __BeginLoading;
  76. }
  77. *szLine = 0;
  78. return (szLine - szLineBegin);
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Local functions (listfile nodes)
  82. // This function creates the name for the listfile.
  83. // the file will be created under unique name in the temporary directory
  84. static void GetListFileName(TMPQArchive * /* ha */, char * szListFile)
  85. {
  86. char szTemp[MAX_PATH];
  87. // Create temporary file name int TEMP directory
  88. GetTempPath(sizeof(szTemp)-1, szTemp);
  89. GetTempFileName(szTemp, LISTFILE_NAME, 0, szListFile);
  90. }
  91. // Creates new listfile. The listfile is an array of TListFileNode
  92. // structures. The size of the array is the same like the hash table size,
  93. // the ordering is the same too (listfile item index is the same like
  94. // the index in the MPQ hash table)
  95. int SListFileCreateListFile(TMPQArchive * ha)
  96. {
  97. DWORD dwItems = ha->pHeader->dwHashTableSize;
  98. // The listfile should be NULL now
  99. assert(ha->pListFile == NULL);
  100. ha->pListFile = ALLOCMEM(TFileNode *, dwItems);
  101. if(ha->pListFile == NULL)
  102. return ERROR_NOT_ENOUGH_MEMORY;
  103. memset(ha->pListFile, 0xFF, dwItems * sizeof(TFileNode *));
  104. return ERROR_SUCCESS;
  105. }
  106. // Adds a name into the list of all names. For each locale in the MPQ,
  107. // one entry will be created
  108. // If the file name is already there, does nothing.
  109. int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
  110. {
  111. TFileNode * pNode = NULL;
  112. TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
  113. TMPQHash * pHash0 = GetHashEntry(ha, szFileName);
  114. TMPQHash * pHash = pHash0;
  115. DWORD dwHashIndex = 0;
  116. size_t nLength; // File name lentgth
  117. DWORD dwName1;
  118. DWORD dwName2;
  119. // If the file does not exist within the MPQ, do nothing
  120. if(pHash == NULL)
  121. return ERROR_SUCCESS;
  122. // Remember the name
  123. dwName1 = pHash->dwName1;
  124. dwName2 = pHash->dwName2;
  125. // Pass all entries in the hash table
  126. while(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex != HASH_ENTRY_FREE)
  127. {
  128. // There may be an entry deleted amongst various language versions
  129. if(pHash->dwBlockIndex != HASH_ENTRY_DELETED)
  130. {
  131. // Create the lang version, if none
  132. dwHashIndex = (DWORD)(pHash - ha->pHashTable);
  133. if((DWORD_PTR)ha->pListFile[dwHashIndex] >= LISTFILE_ENTRY_DELETED)
  134. {
  135. // Create the listfile node and insert it into the listfile table
  136. // If the name node already exists there, we just increment number of references
  137. if(pNode == NULL)
  138. {
  139. nLength = strlen(szFileName);
  140. pNode = (TFileNode *)ALLOCMEM(char, sizeof(TFileNode) + nLength);
  141. pNode->dwRefCount = 1;
  142. pNode->nLength = nLength;
  143. strcpy(pNode->szFileName, szFileName);
  144. ha->pListFile[dwHashIndex] = pNode;
  145. }
  146. else
  147. {
  148. pNode->dwRefCount++;
  149. ha->pListFile[dwHashIndex] = pNode;
  150. }
  151. }
  152. }
  153. // Move to the next hash entry
  154. if(++pHash >= pHashEnd)
  155. pHash = ha->pHashTable;
  156. if(pHash == pHash0)
  157. break;
  158. }
  159. return ERROR_SUCCESS;
  160. }
  161. // Adds a filename into the listfile. If the file name is already there,
  162. // does nothing.
  163. int SListFileCreateNode(TMPQArchive * ha, const char * szFileName, LCID lcLocale)
  164. {
  165. TFileNode * pNode = NULL;
  166. TMPQHash * pHash0 = GetHashEntry(ha, szFileName);
  167. TMPQHash * pHash1 = GetHashEntryEx(ha, szFileName, lcLocale);
  168. DWORD dwHashIndex0 = 0;
  169. DWORD dwHashIndex1 = 0;
  170. size_t nLength; // File name lentgth
  171. // If the file does not exist within the MPQ, do nothing
  172. if(pHash1 == NULL || pHash1->dwBlockIndex >= HASH_ENTRY_DELETED)
  173. return ERROR_SUCCESS;
  174. // If the locale-cpecific listfile entry already exists, do nothing
  175. dwHashIndex0 = (DWORD)(pHash0 - ha->pHashTable);
  176. dwHashIndex1 = (DWORD)(pHash1 - ha->pHashTable);
  177. if((DWORD_PTR)ha->pListFile[dwHashIndex1] < LISTFILE_ENTRY_DELETED)
  178. return ERROR_SUCCESS;
  179. // Does the neutral table entry exist ?
  180. if((DWORD_PTR)ha->pListFile[dwHashIndex0] < LISTFILE_ENTRY_DELETED)
  181. pNode = ha->pListFile[dwHashIndex0];
  182. // If no node yet, we have to create new one
  183. if(pNode == NULL)
  184. {
  185. nLength = strlen(szFileName);
  186. pNode = (TFileNode *)ALLOCMEM(char, sizeof(TFileNode) + nLength);
  187. pNode->dwRefCount = 1;
  188. pNode->nLength = nLength;
  189. strcpy(pNode->szFileName, szFileName);
  190. ha->pListFile[dwHashIndex0] = pNode;
  191. }
  192. // Also insert the node in the locale-specific entry
  193. if(dwHashIndex1 != dwHashIndex0)
  194. {
  195. pNode->dwRefCount++;
  196. ha->pListFile[dwHashIndex1] = pNode;
  197. }
  198. return ERROR_SUCCESS;
  199. }
  200. // Removes a filename from the listfile.
  201. // If the name is not there, does nothing
  202. int SListFileRemoveNode(TMPQArchive * ha, const char * szFileName, LCID lcLocale)
  203. {
  204. TFileNode * pNode = NULL;
  205. TMPQHash * pHash = GetHashEntryEx(ha, szFileName, lcLocale);
  206. size_t nHashIndex = 0;
  207. if(pHash != NULL)
  208. {
  209. nHashIndex = pHash - ha->pHashTable;
  210. pNode = ha->pListFile[nHashIndex];
  211. ha->pListFile[nHashIndex] = (TFileNode *)LISTFILE_ENTRY_DELETED;
  212. // Free the node
  213. pNode->dwRefCount--;
  214. if(pNode->dwRefCount == 0)
  215. FREEMEM(pNode);
  216. }
  217. return ERROR_SUCCESS;
  218. }
  219. void SListFileFreeListFile(TMPQArchive * ha)
  220. {
  221. if(ha->pListFile != NULL)
  222. {
  223. for(DWORD i = 0; i < ha->pHeader->dwHashTableSize; i++)
  224. {
  225. TFileNode * pNode = ha->pListFile[i];
  226. if((DWORD_PTR)pNode < LISTFILE_ENTRY_DELETED)
  227. {
  228. ha->pListFile[i] = (TFileNode *)LISTFILE_ENTRY_FREE;
  229. pNode->dwRefCount--;
  230. if(pNode->dwRefCount == 0)
  231. FREEMEM(pNode);
  232. }
  233. }
  234. FREEMEM(ha->pListFile);
  235. ha->pListFile = NULL;
  236. }
  237. }
  238. // Saves the whole listfile into the MPQ.
  239. int SListFileSaveToMpq(TMPQArchive * ha)
  240. {
  241. TFileNode * pNode = NULL;
  242. TMPQHash * pHashEnd = NULL;
  243. TMPQHash * pHash0 = NULL;
  244. TMPQHash * pHash = NULL;
  245. HANDLE hFile = INVALID_HANDLE_VALUE;
  246. char szListFile[MAX_PATH];
  247. char szBuffer[MAX_PATH+4];
  248. DWORD dwTransferred;
  249. size_t nLength = 0;
  250. DWORD dwName1 = 0;
  251. DWORD dwName2 = 0;
  252. LCID lcSave = lcLocale;
  253. int nError = ERROR_SUCCESS;
  254. // If no listfile, do nothing
  255. if(ha->pListFile == NULL)
  256. return ERROR_SUCCESS;
  257. // Create the local listfile
  258. if(nError == ERROR_SUCCESS)
  259. {
  260. GetListFileName(ha, szListFile);
  261. hFile = CreateFile(szListFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  262. if(hFile == INVALID_HANDLE_VALUE)
  263. nError = GetLastError();
  264. }
  265. // Find the hash entry corresponding to listfile
  266. pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
  267. pHash0 = pHash = GetHashEntry(ha, 0);
  268. if(pHash == NULL)
  269. pHash0 = pHash = ha->pHashTable;
  270. // Save the file
  271. if(nError == ERROR_SUCCESS)
  272. {
  273. for(;;)
  274. {
  275. if(pHash->dwName1 != dwName1 && pHash->dwName2 != dwName2 && pHash->dwBlockIndex < HASH_ENTRY_DELETED)
  276. {
  277. dwName1 = pHash->dwName1;
  278. dwName2 = pHash->dwName2;
  279. pNode = ha->pListFile[pHash - ha->pHashTable];
  280. if((DWORD_PTR)pNode < LISTFILE_ENTRY_DELETED)
  281. {
  282. memcpy(szBuffer, pNode->szFileName, pNode->nLength);
  283. szBuffer[pNode->nLength + 0] = 0x0D;
  284. szBuffer[pNode->nLength + 1] = 0x0A;
  285. WriteFile(hFile, szBuffer, (DWORD)(pNode->nLength + 2), &dwTransferred, NULL);
  286. }
  287. }
  288. if(++pHash >= pHashEnd)
  289. pHash = ha->pHashTable;
  290. if(pHash == pHash0)
  291. break;
  292. }
  293. // Write the listfile name (if not already there)
  294. if(GetHashEntry(ha, LISTFILE_NAME) == NULL)
  295. {
  296. nLength = strlen(LISTFILE_NAME);
  297. memcpy(szBuffer, LISTFILE_NAME, nLength);
  298. szBuffer[nLength + 0] = 0x0D;
  299. szBuffer[nLength + 1] = 0x0A;
  300. WriteFile(hFile, szBuffer, (DWORD)(nLength + 2), &dwTransferred, NULL);
  301. }
  302. // Add the listfile into the archive.
  303. SFileSetLocale(LANG_NEUTRAL);
  304. nError = AddFileToArchive(ha,
  305. hFile,
  306. LISTFILE_NAME,
  307. MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING,
  308. 0,
  309. SFILE_TYPE_DATA,
  310. NULL);
  311. lcLocale = lcSave;
  312. }
  313. // Close the temporary file and delete it.
  314. // There is no FILE_FLAG_DELETE_ON_CLOSE on LINUX.
  315. if(hFile != INVALID_HANDLE_VALUE)
  316. CloseHandle(hFile);
  317. DeleteFile(szListFile);
  318. return nError;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // File functions
  322. // Adds a listfile into the MPQ archive.
  323. // Note that the function does not remove the
  324. int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
  325. {
  326. TListFileCache * pCache = NULL;
  327. TMPQArchive * ha = (TMPQArchive *)hMpq;
  328. HANDLE hListFile = NULL;
  329. char szFileName[MAX_PATH + 1];
  330. DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
  331. DWORD dwCacheSize = 0;
  332. DWORD dwFileSize = 0;
  333. size_t nLength = 0;
  334. int nError = ERROR_SUCCESS;
  335. // If the szListFile is NULL, it means we have to open internal listfile
  336. if(szListFile == NULL)
  337. {
  338. szListFile = LISTFILE_NAME;
  339. dwSearchScope = SFILE_OPEN_FROM_MPQ;
  340. }
  341. // Open the local/internal listfile
  342. if(nError == ERROR_SUCCESS)
  343. {
  344. if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile))
  345. nError = GetLastError();
  346. }
  347. if(nError == ERROR_SUCCESS)
  348. {
  349. dwCacheSize =
  350. dwFileSize = SFileGetFileSize(hListFile, NULL);
  351. // Try to allocate memory for the complete file. If it fails,
  352. // load the part of the file
  353. pCache = (TListFileCache *)ALLOCMEM(char, (sizeof(TListFileCache) + dwCacheSize));
  354. if(pCache == NULL)
  355. {
  356. dwCacheSize = LISTFILE_CACHE_SIZE;
  357. pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
  358. }
  359. if(pCache == NULL)
  360. nError = ERROR_NOT_ENOUGH_MEMORY;
  361. }
  362. if(nError == ERROR_SUCCESS)
  363. {
  364. // Initialize the file cache
  365. memset(pCache, 0, sizeof(TListFileCache));
  366. pCache->hFile = hListFile;
  367. pCache->dwFileSize = dwFileSize;
  368. pCache->dwBuffSize = dwCacheSize;
  369. pCache->dwFilePos = 0;
  370. // Fill the cache
  371. SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
  372. // Initialize the pointers
  373. pCache->pBegin =
  374. pCache->pPos = &pCache->Buffer[0];
  375. pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
  376. // Load the node list. Add the node for every locale in the archive
  377. while((nLength = ReadLine(pCache, szFileName, sizeof(szFileName) - 1)) > 0)
  378. SListFileCreateNodeForAllLocales(ha, szFileName);
  379. }
  380. // Cleanup & exit
  381. if(pCache != NULL)
  382. SListFileFindClose((HANDLE)pCache);
  383. return nError;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Passing through the listfile
  387. HANDLE SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
  388. {
  389. TListFileCache * pCache = NULL;
  390. TMPQArchive * ha = (TMPQArchive *)hMpq;
  391. HANDLE hListFile = NULL;
  392. DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
  393. DWORD dwCacheSize = 0;
  394. DWORD dwFileSize = 0;
  395. size_t nLength = 0;
  396. int nError = ERROR_SUCCESS;
  397. // Initialize the structure with zeros
  398. memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
  399. // If the szListFile is NULL, it means we have to open internal listfile
  400. if(szListFile == NULL)
  401. {
  402. szListFile = LISTFILE_NAME;
  403. dwSearchScope = SFILE_OPEN_FROM_MPQ;
  404. }
  405. // Open the local/internal listfile
  406. if(nError == ERROR_SUCCESS)
  407. {
  408. if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile))
  409. nError = GetLastError();
  410. }
  411. if(nError == ERROR_SUCCESS)
  412. {
  413. dwCacheSize =
  414. dwFileSize = SFileGetFileSize(hListFile, NULL);
  415. // Try to allocate memory for the complete file. If it fails,
  416. // load the part of the file
  417. pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
  418. if(pCache == NULL)
  419. {
  420. dwCacheSize = LISTFILE_CACHE_SIZE;
  421. pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize);
  422. }
  423. if(pCache == NULL)
  424. nError = ERROR_NOT_ENOUGH_MEMORY;
  425. }
  426. if(nError == ERROR_SUCCESS)
  427. {
  428. // Initialize the file cache
  429. memset(pCache, 0, sizeof(TListFileCache));
  430. pCache->hFile = hListFile;
  431. pCache->dwFileSize = dwFileSize;
  432. pCache->dwBuffSize = dwCacheSize;
  433. pCache->dwFilePos = 0;
  434. if(szMask != NULL)
  435. {
  436. pCache->szMask = ALLOCMEM(char, strlen(szMask) + 1);
  437. strcpy(pCache->szMask, szMask);
  438. }
  439. // Fill the cache
  440. SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL);
  441. // Initialize the pointers
  442. pCache->pBegin =
  443. pCache->pPos = &pCache->Buffer[0];
  444. pCache->pEnd = pCache->pBegin + pCache->dwBuffSize;
  445. for(;;)
  446. {
  447. // Read the (next) line
  448. nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
  449. if(nLength == 0)
  450. {
  451. nError = ERROR_NO_MORE_FILES;
  452. break;
  453. }
  454. // If some mask entered, check it
  455. if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
  456. break;
  457. }
  458. }
  459. // Cleanup & exit
  460. if(nError != ERROR_SUCCESS)
  461. {
  462. memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
  463. SListFileFindClose((HANDLE)pCache);
  464. pCache = NULL;
  465. SetLastError(nError);
  466. }
  467. return (HANDLE)pCache;
  468. }
  469. BOOL SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
  470. {
  471. TListFileCache * pCache = (TListFileCache *)hFind;
  472. size_t nLength;
  473. BOOL bResult = FALSE;
  474. int nError = ERROR_SUCCESS;
  475. for(;;)
  476. {
  477. // Read the (next) line
  478. nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
  479. if(nLength == 0)
  480. {
  481. nError = ERROR_NO_MORE_FILES;
  482. break;
  483. }
  484. // If some mask entered, check it
  485. if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
  486. {
  487. bResult = TRUE;
  488. break;
  489. }
  490. }
  491. if(nError != ERROR_SUCCESS)
  492. SetLastError(nError);
  493. return bResult;
  494. }
  495. BOOL SListFileFindClose(HANDLE hFind)
  496. {
  497. TListFileCache * pCache = (TListFileCache *)hFind;
  498. if(pCache != NULL)
  499. {
  500. if(pCache->hFile != NULL)
  501. SFileCloseFile(pCache->hFile);
  502. if(pCache->szMask != NULL)
  503. FREEMEM(pCache->szMask);
  504. FREEMEM(pCache);
  505. return TRUE;
  506. }
  507. return FALSE;
  508. }