1 /*****************************************************************************/
2 /* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */
3 /*---------------------------------------------------------------------------*/
5 /*---------------------------------------------------------------------------*/
6 /* Date Ver Who Comment */
7 /* -------- ---- --- ------- */
8 /* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */
9 /*****************************************************************************/
11 #define __STORMLIB_SELF__
15 /*****************************************************************************/
17 /*****************************************************************************/
19 // TODO: Test for archives > 4GB
20 static BOOL
OpenLocalFile(const char * szFileName
, HANDLE
* phFile
)
23 HANDLE hFile
= CreateFile(szFileName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
25 if(hFile
!= INVALID_HANDLE_VALUE
)
27 // Allocate and initialize file handle
28 size_t nHandleSize
= sizeof(TMPQFile
) + strlen(szFileName
);
29 if((hf
= (TMPQFile
*)ALLOCMEM(char, nHandleSize
)) != NULL
)
31 memset(hf
, 0, nHandleSize
);
32 strcpy(hf
->szFileName
, szFileName
);
38 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
44 // TODO: Test for archives > 4GB
45 static void FreeMPQFile(TMPQFile
*& hf
)
49 if(hf
->hFile
!= INVALID_HANDLE_VALUE
)
50 CloseHandle(hf
->hFile
);
51 if(hf
->pdwBlockPos
!= NULL
)
52 FREEMEM(hf
->pdwBlockPos
);
53 if(hf
->pbFileBuffer
!= NULL
)
54 FREEMEM(hf
->pbFileBuffer
);
60 /*****************************************************************************/
61 /* Public functions */
62 /*****************************************************************************/
64 //-----------------------------------------------------------------------------
65 // SFileEnumLocales enums all locale versions within MPQ.
66 // Functions fills all available language identifiers on a file into the buffer
67 // pointed by plcLocales. There must be enough entries to copy the localed,
68 // otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
70 // TODO: Test for archives > 4GB
71 int WINAPI
SFileEnumLocales(
73 const char * szFileName
,
75 DWORD
* pdwMaxLocales
,
78 TMPQArchive
* ha
= (TMPQArchive
*)hMPQ
;
79 TMPQHash
* pHash
= NULL
;
80 TMPQHash
* pHashEnd
= NULL
;
82 int nError
= ERROR_SUCCESS
;
84 // Test the parameters
85 if(nError
== ERROR_SUCCESS
)
87 if(!IsValidMpqHandle(ha
) || pdwMaxLocales
== NULL
)
88 nError
= ERROR_INVALID_PARAMETER
;
89 if(dwSearchScope
== SFILE_OPEN_BY_INDEX
&& (DWORD_PTR
)szFileName
> ha
->pHeader
->dwBlockTableSize
)
90 nError
= ERROR_INVALID_PARAMETER
;
91 if(dwSearchScope
!= SFILE_OPEN_BY_INDEX
&& *szFileName
== 0)
92 nError
= ERROR_INVALID_PARAMETER
;
95 // Retrieve the hash entry for the required file
96 if(nError
== ERROR_SUCCESS
)
98 pHashEnd
= ha
->pHashTable
+ ha
->pHeader
->dwHashTableSize
;
100 if(dwSearchScope
== SFILE_OPEN_BY_INDEX
)
102 for(pHash
= ha
->pHashTable
; pHash
< pHashEnd
; pHash
++)
104 if(pHash
->dwBlockIndex
== (DWORD_PTR
)szFileName
)
107 if(pHash
== pHashEnd
)
111 pHash
= GetHashEntry(ha
, szFileName
);
114 // If the file was not found, sorry
115 if(nError
== ERROR_SUCCESS
)
118 nError
= ERROR_FILE_NOT_FOUND
;
121 // Count the entries which correspond to the same file name
122 if(nError
== ERROR_SUCCESS
)
124 TMPQHash
* pSaveHash
= pHash
;
125 DWORD dwName1
= pHash
->dwName1
;
126 DWORD dwName2
= pHash
->dwName2
;
128 // For nameless access, return 1 locale always
129 if(dwSearchScope
== SFILE_OPEN_BY_INDEX
)
133 while(pHash
< pHashEnd
&& pHash
->dwName1
== dwName1
&& pHash
->dwName2
== dwName2
)
143 // Test if there is enough space to copy the locales
144 if(nError
== ERROR_SUCCESS
)
146 DWORD dwMaxLocales
= *pdwMaxLocales
;
148 *pdwMaxLocales
= dwLocales
;
149 if(dwMaxLocales
< dwLocales
)
150 nError
= ERROR_INSUFFICIENT_BUFFER
;
153 // Fill all the locales
154 if(nError
== ERROR_SUCCESS
)
156 for(DWORD i
= 0; i
< dwLocales
; i
++, pHash
++)
157 *plcLocales
++ = (LCID
)pHash
->lcLocale
;
162 //-----------------------------------------------------------------------------
165 // hMPQ - Handle of opened MPQ archive
166 // szFileName - Name of file to look for
168 // TODO: Test for archives > 4GB
169 BOOL WINAPI
SFileHasFile(HANDLE hMPQ
, char * szFileName
)
171 TMPQArchive
* ha
= (TMPQArchive
*)hMPQ
;
172 int nError
= ERROR_SUCCESS
;
174 if(nError
== ERROR_SUCCESS
)
177 nError
= ERROR_INVALID_PARAMETER
;
179 nError
= ERROR_INVALID_PARAMETER
;
182 // Prepare the file opening
183 if(nError
== ERROR_SUCCESS
)
185 if(GetHashEntryEx(ha
, szFileName
, lcLocale
) == NULL
)
187 nError
= ERROR_FILE_NOT_FOUND
;
192 if(nError
!= ERROR_SUCCESS
)
194 SetLastError(nError
);
197 return (nError
== ERROR_SUCCESS
);
201 //-----------------------------------------------------------------------------
204 // hMPQ - Handle of opened MPQ archive
205 // szFileName - Name of file to open
206 // dwSearchScope - Where to search
207 // phFile - Pointer to store opened file handle
209 // TODO: Test for archives > 4GB
210 BOOL WINAPI
SFileOpenFileEx(HANDLE hMPQ
, const char * szFileName
, DWORD dwSearchScope
, HANDLE
* phFile
)
212 LARGE_INTEGER FilePos
;
213 TMPQArchive
* ha
= (TMPQArchive
*)hMPQ
;
214 TMPQFile
* hf
= NULL
;
215 TMPQHash
* pHash
= NULL
; // Hash table index
216 TMPQBlock
* pBlock
= NULL
; // File block
217 TMPQBlockEx
* pBlockEx
= NULL
;
218 DWORD dwHashIndex
= 0; // Hash table index
219 DWORD dwBlockIndex
= (DWORD
)-1; // Found table index
220 size_t nHandleSize
= 0; // Memory space necessary to allocate TMPQHandle
221 int nError
= ERROR_SUCCESS
;
224 // Due to increasing numbers of files in MPQs, I had to change the behavior
225 // of opening by file index. Now, the SFILE_OPEN_BY_INDEX value of dwSearchScope
226 // must be entered. This check will allow to find code places that are incompatible
227 // with the new behavior.
228 if(dwSearchScope
!= SFILE_OPEN_BY_INDEX
&& szFileName
!= NULL
)
230 assert((DWORD_PTR
)szFileName
> 0x10000);
234 if(nError
== ERROR_SUCCESS
)
236 if(ha
== NULL
&& dwSearchScope
== SFILE_OPEN_FROM_MPQ
)
237 nError
= ERROR_INVALID_PARAMETER
;
239 nError
= ERROR_INVALID_PARAMETER
;
240 if(dwSearchScope
== SFILE_OPEN_BY_INDEX
&& (DWORD_PTR
)szFileName
> ha
->pHeader
->dwBlockTableSize
)
241 nError
= ERROR_INVALID_PARAMETER
;
242 if(dwSearchScope
!= SFILE_OPEN_BY_INDEX
&& (szFileName
== NULL
|| *szFileName
== 0))
243 nError
= ERROR_INVALID_PARAMETER
;
246 // Prepare the file opening
247 if(nError
== ERROR_SUCCESS
)
249 // When the file is given by number, ...
250 if(dwSearchScope
== SFILE_OPEN_BY_INDEX
)
252 TMPQHash
* pHashEnd
= ha
->pHashTable
+ ha
->pHeader
->dwHashTableSize
;
254 // Set handle size to be sizeof(TMPQFile) + length of FileXXXXXXXX.xxx
255 nHandleSize
= sizeof(TMPQFile
) + 20;
256 for(pHash
= ha
->pHashTable
; pHash
< pHashEnd
; pHash
++)
258 if((DWORD_PTR
)szFileName
== pHash
->dwBlockIndex
)
260 dwHashIndex
= (DWORD
)(pHash
- ha
->pHashTable
);
261 dwBlockIndex
= pHash
->dwBlockIndex
;
268 // If we have to open a disk file
269 if(dwSearchScope
== SFILE_OPEN_LOCAL_FILE
)
270 return OpenLocalFile(szFileName
, phFile
);
272 nHandleSize
= sizeof(TMPQFile
) + strlen(szFileName
);
273 if((pHash
= GetHashEntryEx(ha
, szFileName
, lcLocale
)) != NULL
)
275 dwHashIndex
= (DWORD
)(pHash
- ha
->pHashTable
);
276 dwBlockIndex
= pHash
->dwBlockIndex
;
281 // Get block index from file name and test it
282 if(nError
== ERROR_SUCCESS
)
284 // If index was not found, or is greater than number of files, exit.
285 if(dwBlockIndex
== (DWORD
)-1 || dwBlockIndex
> ha
->pHeader
->dwBlockTableSize
)
286 nError
= ERROR_FILE_NOT_FOUND
;
289 // Get block and test if the file was not already deleted.
290 if(nError
== ERROR_SUCCESS
)
292 // Get both block tables and file position
293 pBlockEx
= ha
->pExtBlockTable
+ dwBlockIndex
;
294 pBlock
= ha
->pBlockTable
+ dwBlockIndex
;
295 FilePos
.HighPart
= pBlockEx
->wFilePosHigh
;
296 FilePos
.LowPart
= pBlock
->dwFilePos
;
298 if(FilePos
.QuadPart
> ha
->MpqSize
.QuadPart
||
299 pBlock
->dwCSize
> ha
->MpqSize
.QuadPart
)
300 nError
= ERROR_FILE_CORRUPT
;
301 if((pBlock
->dwFlags
& MPQ_FILE_EXISTS
) == 0)
302 nError
= ERROR_FILE_NOT_FOUND
;
303 if(pBlock
->dwFlags
& ~MPQ_FILE_VALID_FLAGS
)
304 nError
= ERROR_NOT_SUPPORTED
;
307 // Allocate file handle
308 if(nError
== ERROR_SUCCESS
)
310 if((hf
= (TMPQFile
*)ALLOCMEM(char, nHandleSize
)) == NULL
)
311 nError
= ERROR_NOT_ENOUGH_MEMORY
;
314 // Initialize file handle
315 if(nError
== ERROR_SUCCESS
)
317 memset(hf
, 0, nHandleSize
);
318 hf
->hFile
= INVALID_HANDLE_VALUE
;
320 hf
->pBlockEx
= pBlockEx
;
322 hf
->nBlocks
= (hf
->pBlock
->dwFSize
+ ha
->dwBlockSize
- 1) / ha
->dwBlockSize
;
325 hf
->MpqFilePos
.HighPart
= pBlockEx
->wFilePosHigh
;
326 hf
->MpqFilePos
.LowPart
= pBlock
->dwFilePos
;
327 hf
->MpqFilePos
.QuadPart
+= ha
->MpqPos
.QuadPart
;
329 hf
->dwHashIndex
= dwHashIndex
;
330 hf
->dwFileIndex
= dwBlockIndex
;
332 // Allocate buffers for decompression.
333 if(hf
->pBlock
->dwFlags
& MPQ_FILE_COMPRESSED
)
335 // Allocate buffer for block positions. At the begin of file are stored
336 // DWORDs holding positions of each block relative from begin of file in the archive
337 // As for newer MPQs, there may be one additional entry in the block table
338 // (if the MPQ_FILE_HAS_EXTRA flag is set).
339 // Allocate the buffer to include this DWORD as well
341 if((hf
->pdwBlockPos
= ALLOCMEM(DWORD
, hf
->nBlocks
+ 2)) == NULL
)
342 nError
= ERROR_NOT_ENOUGH_MEMORY
;
345 // Decrypt file seed. Cannot be used if the file is given by index
346 if(dwSearchScope
!= SFILE_OPEN_BY_INDEX
)
348 if(hf
->pBlock
->dwFlags
& MPQ_FILE_ENCRYPTED
)
350 const char * szTemp
= strrchr(szFileName
, '\\');
352 strcpy(hf
->szFileName
, szFileName
);
354 szFileName
= szTemp
+ 1;
355 hf
->dwSeed1
= DecryptFileSeed((char *)szFileName
);
357 if(hf
->pBlock
->dwFlags
& MPQ_FILE_FIXSEED
)
359 hf
->dwSeed1
= (hf
->dwSeed1
+ hf
->pBlock
->dwFilePos
) ^ hf
->pBlock
->dwFSize
;
365 // If the file is encrypted and not compressed, we cannot detect the file seed
366 if(SFileGetFileName(hf
, hf
->szFileName
) == FALSE
)
367 nError
= GetLastError();
372 if(nError
!= ERROR_SUCCESS
)
375 SetLastError(nError
);
379 return (nError
== ERROR_SUCCESS
);
382 //-----------------------------------------------------------------------------
383 // BOOL SFileCloseFile(HANDLE hFile);
385 // TODO: Test for archives > 4GB
386 BOOL WINAPI
SFileCloseFile(HANDLE hFile
)
388 TMPQFile
* hf
= (TMPQFile
*)hFile
;
390 if(!IsValidFileHandle(hf
))
392 SetLastError(ERROR_INVALID_PARAMETER
);
396 // Set the last accessed file in the archive
398 hf
->ha
->pLastFile
= NULL
;
400 // Free the structure