Commit | Line | Data |
c3242ac4 |
1 | /* |
2 | Copyright (c) 2002-2013, ShadowFlare <blakflare@hotmail.com> |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions |
7 | are met: |
8 | |
9 | 1. Redistributions of source code must retain the above copyright |
10 | notice, this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND |
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 | SUCH DAMAGE. |
26 | */ |
27 | |
cfca19c6 |
28 | #include "CwadLib.h" |
29 | #include "SFTypes.h" |
30 | #include <stdlib.h> |
31 | #include <stdio.h> |
32 | #include <string.h> |
33 | #ifndef __SYS_ZLIB |
34 | #include "zlib/zlib.h" |
35 | #else |
36 | #include <zlib.h> |
37 | #endif |
38 | |
39 | const UInt32 ID_CWAD = *(UInt32 *)"CWAD"; |
40 | const UInt32 ID_SEP = *(UInt32 *)"SEP\0"; |
41 | |
42 | typedef struct _CWADFILEHEADER { |
43 | UInt32 dwPackedSize; |
44 | UInt32 dwNameLength; |
45 | UInt32 dwFullSize; |
46 | UInt32 dwFlags; |
47 | // char szFileName[dwNameLength]; |
48 | // UInt8 byData[dwPackedSize]; |
49 | } CWADFILEHEADER; |
50 | |
51 | /* struct CWAD { |
52 | char IDTag[4] = "CWAD"; |
53 | CWADFILEHEADER Files[]; |
54 | }; */ |
55 | |
56 | typedef struct _CWADFILE CWADFILE; |
57 | |
58 | typedef struct _CWADARCHIVE { |
59 | TCHAR *pszFileName; |
60 | FILE *pFile; |
61 | unsigned long nStart; |
62 | unsigned long nEnd; |
63 | unsigned long nFiles; |
64 | CWADFILE *pFiles; |
65 | char *pmszFileList; |
66 | unsigned long nFileListSize; |
67 | bool bHasFileNameBlank; |
68 | long nReferences; |
69 | bool bOpen; |
70 | } CWADARCHIVE; |
71 | |
72 | typedef struct _CWADFILE { |
73 | char *pszFileName; |
74 | CWADARCHIVE *pParentArc; |
75 | UInt32 dwPackedSize; |
76 | UInt32 dwFullSize; |
77 | UInt32 dwFlags; |
78 | unsigned long nOffset; |
79 | UInt8 *pBuffer; |
80 | long nReferences; |
81 | } CWADFILE; |
82 | |
83 | typedef struct _CWADFILEHANDLE { |
84 | CWADFILE *f; |
85 | UInt32 dwFilePointer; |
86 | } CWADFILEHANDLE; |
87 | |
88 | void CWadListFilesInternal(void *hCWAD); |
89 | unsigned long CWadFindHeaderAndSize(FILE *hFile, unsigned long *pnCwadEnd); |
90 | void CWadDecryptData(UInt8 *pBuffer, unsigned long nBufferLength); |
91 | |
7b6f1c5a |
92 | #ifndef _WIN32 |
93 | #define _T(x) x |
94 | #define _tfopen fopen |
95 | #define _tcsdup strdup |
96 | #endif |
97 | |
cfca19c6 |
98 | void * CWadOpenArchive(const TCHAR *pszFileName) |
99 | { |
100 | if (!pszFileName) |
101 | return 0; |
102 | |
103 | unsigned long flen; |
104 | FILE *pFile; |
105 | pFile = _tfopen(pszFileName, _T("rb")); |
106 | if (pFile) |
107 | { |
108 | unsigned long nCwadStart = CWadFindHeaderAndSize(pFile, &flen); |
109 | if (nCwadStart != -1) { |
110 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)malloc(sizeof(CWADARCHIVE)); |
111 | if (cOpenArc) { |
112 | cOpenArc->pFile = pFile; |
113 | cOpenArc->nStart = nCwadStart; |
114 | cOpenArc->nEnd = flen; |
115 | cOpenArc->pmszFileList = 0; |
116 | cOpenArc->bHasFileNameBlank = false; |
117 | cOpenArc->pFiles = 0; |
118 | CWadListFilesInternal(cOpenArc); |
119 | if (cOpenArc->pmszFileList) { |
120 | cOpenArc->pszFileName = _tcsdup(pszFileName); |
121 | cOpenArc->nReferences = 0; |
122 | cOpenArc->bOpen = true; |
123 | return cOpenArc; |
124 | } |
125 | |
126 | free(cOpenArc); |
127 | } |
128 | } |
129 | |
130 | fclose(pFile); |
131 | } |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | bool CWadCloseArchive(void *hCWAD) |
137 | { |
138 | if (!hCWAD) |
139 | return false; |
140 | |
141 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
142 | if (cOpenArc->nReferences > 0) { |
143 | cOpenArc->bOpen = false; |
144 | return false; |
145 | } |
146 | fclose(cOpenArc->pFile); |
147 | if (cOpenArc->pFiles) free(cOpenArc->pFiles); |
148 | if (cOpenArc->pmszFileList) free(cOpenArc->pmszFileList); |
149 | if (cOpenArc->pszFileName) free(cOpenArc->pszFileName); |
150 | free(cOpenArc); |
151 | return true; |
152 | } |
153 | |
154 | unsigned long CWadGetArchiveInfo(void *hCWAD, int nInfoType) |
155 | { |
156 | if (!hCWAD) |
157 | return -1; |
158 | |
159 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
160 | switch (nInfoType) { |
161 | case CWAD_INFO_NUM_FILES: |
162 | return cOpenArc->nFiles; |
163 | case CWAD_INFO_SIZE: |
164 | return cOpenArc->nEnd - cOpenArc->nStart; |
165 | default: |
166 | return -1; |
167 | } |
168 | } |
169 | |
170 | unsigned long CWadListFiles(void *hCWAD, char *pmszBuffer, unsigned long nBufferLength) |
171 | { |
172 | if (!hCWAD) |
173 | return 0; |
174 | |
175 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
176 | if (pmszBuffer && nBufferLength >= cOpenArc->nFileListSize) |
177 | memcpy(pmszBuffer, cOpenArc->pmszFileList, cOpenArc->nFileListSize); |
178 | |
179 | return cOpenArc->nFileListSize; |
180 | } |
181 | |
182 | void CWadListFilesInternal(void *hCWAD) |
183 | { |
184 | if (!hCWAD) |
185 | return; |
186 | |
187 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
188 | CWADFILEHEADER CwadFile; |
189 | unsigned long nReqSize = 0; |
190 | cOpenArc->nFiles = 0; |
191 | if (cOpenArc->bHasFileNameBlank && cOpenArc->pmszFileList) { |
192 | *cOpenArc->pmszFileList = 0; |
193 | nReqSize++; |
194 | } |
195 | fseek(cOpenArc->pFile, cOpenArc->nStart + 4, SEEK_SET); |
196 | for (long fpos = ftell(cOpenArc->pFile); fpos != -1 && fpos + sizeof(CWADFILEHEADER) <= cOpenArc->nEnd; fpos = ftell(cOpenArc->pFile)) { |
197 | if (fread(&CwadFile, sizeof(CWADFILEHEADER), 1, cOpenArc->pFile) == 1) { |
198 | if (cOpenArc->pmszFileList) |
199 | { |
200 | if (CwadFile.dwNameLength > 0) { |
201 | if (fread(cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength, 1, cOpenArc->pFile) == 1) |
202 | CWadDecryptData((UInt8 *)cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength); |
203 | cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList + nReqSize; |
204 | } |
205 | else |
206 | cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList; |
207 | cOpenArc->pFiles[cOpenArc->nFiles].pParentArc = cOpenArc; |
208 | cOpenArc->pFiles[cOpenArc->nFiles].dwPackedSize = CwadFile.dwPackedSize; |
209 | cOpenArc->pFiles[cOpenArc->nFiles].dwFullSize = CwadFile.dwFullSize; |
210 | cOpenArc->pFiles[cOpenArc->nFiles].dwFlags = CwadFile.dwFlags; |
211 | cOpenArc->pFiles[cOpenArc->nFiles].nOffset = fpos + sizeof(CWADFILEHEADER) + CwadFile.dwNameLength; |
212 | cOpenArc->pFiles[cOpenArc->nFiles].pBuffer = 0; |
213 | cOpenArc->pFiles[cOpenArc->nFiles].nReferences = 0; |
214 | } |
215 | else { |
216 | fseek(cOpenArc->pFile, CwadFile.dwNameLength, SEEK_CUR); |
217 | } |
218 | nReqSize += CwadFile.dwNameLength; |
219 | if (CwadFile.dwNameLength > 0) { |
220 | if (cOpenArc->pmszFileList) |
221 | cOpenArc->pmszFileList[nReqSize] = 0; |
222 | nReqSize++; |
223 | } |
224 | else if (!cOpenArc->bHasFileNameBlank) { |
225 | cOpenArc->bHasFileNameBlank = true; |
226 | nReqSize++; |
227 | } |
228 | fseek(cOpenArc->pFile, CwadFile.dwPackedSize, SEEK_CUR); |
229 | cOpenArc->nFiles++; |
230 | } |
231 | else break; |
232 | } |
233 | if (cOpenArc->pmszFileList) |
234 | cOpenArc->pmszFileList[nReqSize] = 0; |
235 | nReqSize++; |
236 | if (nReqSize == 1) |
237 | { |
238 | if (cOpenArc->pmszFileList) |
239 | cOpenArc->pmszFileList[nReqSize] = 0; |
240 | nReqSize++; |
241 | } |
242 | if (!cOpenArc->pmszFileList) { |
243 | cOpenArc->pmszFileList = (char *)malloc(nReqSize); |
244 | cOpenArc->nFileListSize = nReqSize; |
245 | if (cOpenArc->nFiles > 0) |
246 | cOpenArc->pFiles = (CWADFILE *)calloc(cOpenArc->nFiles, sizeof(CWADFILE)); |
247 | if (cOpenArc->pmszFileList && (cOpenArc->pFiles || cOpenArc->nFiles == 0)) |
248 | CWadListFilesInternal(hCWAD); |
249 | else { |
250 | if (cOpenArc->pmszFileList) { |
251 | free(cOpenArc->pmszFileList); |
252 | cOpenArc->pmszFileList = 0; |
253 | } |
254 | if (cOpenArc->pFiles) |
255 | free(cOpenArc->pFiles); |
256 | } |
257 | } |
258 | } |
259 | |
260 | void * CWadOpenFile(void *hCWAD, const char *pszFileName) |
261 | { |
262 | if (!hCWAD || !pszFileName) |
263 | return 0; |
264 | |
265 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
266 | unsigned long nFile; |
267 | for (nFile = 0; nFile < cOpenArc->nFiles; nFile++) { |
268 | if (strcmp(pszFileName, cOpenArc->pFiles[nFile].pszFileName) == 0) { |
269 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)malloc(sizeof(CWADFILEHANDLE)); |
270 | if (cOpenFile) { |
271 | cOpenFile->f = &cOpenArc->pFiles[nFile]; |
272 | cOpenFile->dwFilePointer = 0; |
273 | cOpenFile->f->nReferences++; |
274 | cOpenArc->nReferences++; |
275 | return cOpenFile; |
276 | } |
277 | } |
278 | } |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | bool CWadCloseFile(void *hFile) |
284 | { |
285 | if (!hFile) |
286 | return false; |
287 | |
288 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
289 | cOpenFile->f->nReferences--; |
290 | cOpenFile->f->pParentArc->nReferences--; |
291 | if (cOpenFile->f->nReferences < 1 && cOpenFile->f->pBuffer) { |
292 | free(cOpenFile->f->pBuffer); |
293 | cOpenFile->f->pBuffer = 0; |
294 | } |
295 | if (cOpenFile->f->pParentArc->nReferences < 1 && !cOpenFile->f->pParentArc->bOpen) |
296 | CWadCloseArchive(cOpenFile->f->pParentArc); |
297 | free(cOpenFile); |
298 | |
299 | return true; |
300 | } |
301 | |
302 | unsigned long CWadGetFileSize(void *hFile) |
303 | { |
304 | if (!hFile) |
305 | return -1; |
306 | |
307 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
308 | return cOpenFile->f->dwFullSize; |
309 | } |
310 | |
311 | unsigned long CWadGetFileInfo(void *hFile, int nInfoType) |
312 | { |
313 | if (!hFile) |
314 | return -1; |
315 | |
316 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
317 | switch (nInfoType) { |
318 | case CWAD_INFO_NUM_FILES: |
319 | return cOpenFile->f->pParentArc->nFiles; |
320 | case CWAD_INFO_SIZE: |
321 | return cOpenFile->f->dwFullSize; |
322 | case CWAD_INFO_COMPRESSED_SIZE: |
323 | return cOpenFile->f->dwPackedSize; |
324 | case CWAD_INFO_FLAGS: |
325 | return cOpenFile->f->dwFlags; |
326 | case CWAD_INFO_PARENT: |
327 | return (unsigned long)cOpenFile->f->pParentArc; |
328 | case CWAD_INFO_POSITION: |
329 | return cOpenFile->dwFilePointer; |
330 | default: |
331 | return -1; |
332 | } |
333 | } |
334 | |
335 | unsigned long CWadSetFilePointer(void *hFile, long nDistanceToMove, int nMoveMethod) |
336 | { |
337 | if (!hFile) |
338 | return -1; |
339 | |
340 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
341 | long fsz = cOpenFile->f->dwFullSize; |
342 | long cpos = cOpenFile->dwFilePointer; |
343 | switch (nMoveMethod) { |
344 | case CWAD_FILE_CURRENT: |
345 | if (cpos + nDistanceToMove < 0 || cpos + nDistanceToMove > fsz) return -1; |
346 | cOpenFile->dwFilePointer += nDistanceToMove; |
347 | break; |
348 | case CWAD_FILE_END: |
349 | if (fsz + nDistanceToMove < 0 || nDistanceToMove > 0) return -1; |
350 | cOpenFile->dwFilePointer = fsz + nDistanceToMove; |
351 | break; |
352 | case CWAD_FILE_BEGIN: |
353 | default: |
354 | if (nDistanceToMove < 0 || nDistanceToMove > fsz) return -1; |
355 | cOpenFile->dwFilePointer = nDistanceToMove; |
356 | } |
357 | |
358 | return cOpenFile->dwFilePointer; |
359 | } |
360 | |
361 | unsigned long CWadReadFile(void *hFile, void *pBuffer, unsigned long nNumberOfBytesToRead) |
362 | { |
363 | if (!hFile || !pBuffer || nNumberOfBytesToRead == 0) |
364 | return 0; |
365 | |
366 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
367 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)cOpenFile->f->pParentArc; |
368 | if (cOpenFile->dwFilePointer >= cOpenFile->f->dwFullSize) |
369 | return 0; |
370 | if (cOpenFile->dwFilePointer + nNumberOfBytesToRead > cOpenFile->f->dwFullSize) |
371 | nNumberOfBytesToRead = cOpenFile->f->dwFullSize - cOpenFile->dwFilePointer; |
372 | unsigned long nBytesRead = nNumberOfBytesToRead; |
373 | if (cOpenFile->f->dwFlags & 1) { |
374 | if (!cOpenFile->f->pBuffer) { |
375 | cOpenFile->f->pBuffer = (UInt8 *)malloc(cOpenFile->f->dwFullSize); |
376 | UInt8 *compbuffer = (UInt8 *)malloc(cOpenFile->f->dwPackedSize); |
377 | bool bReadOK = false; |
378 | if (cOpenFile->f->pBuffer && compbuffer) { |
379 | fseek(cOpenArc->pFile, cOpenFile->f->nOffset, SEEK_SET); |
380 | if (fread(compbuffer, 1, cOpenFile->f->dwPackedSize, cOpenArc->pFile) == cOpenFile->f->dwPackedSize) |
381 | if (uncompress(cOpenFile->f->pBuffer, &nBytesRead, compbuffer, cOpenFile->f->dwPackedSize) == Z_OK) |
382 | bReadOK = true; |
383 | } |
384 | if (!bReadOK && cOpenFile->f->pBuffer) { |
385 | free(cOpenFile->f->pBuffer); |
386 | cOpenFile->f->pBuffer = 0; |
387 | nBytesRead = 0; |
388 | } |
389 | if (compbuffer) |
390 | free(compbuffer); |
391 | } |
392 | if (cOpenFile->f->pBuffer) |
393 | memcpy(pBuffer, cOpenFile->f->pBuffer + cOpenFile->dwFilePointer, nNumberOfBytesToRead); |
394 | } |
395 | else { |
396 | fseek(cOpenArc->pFile, cOpenFile->f->nOffset + cOpenFile->dwFilePointer, SEEK_SET); |
397 | nBytesRead = fread(pBuffer, 1, nNumberOfBytesToRead, cOpenArc->pFile); |
398 | } |
399 | |
400 | cOpenFile->dwFilePointer += nBytesRead; |
401 | return nBytesRead; |
402 | } |
403 | |
404 | unsigned long CWadFindHeader(FILE *hFile) |
405 | { |
406 | return CWadFindHeaderAndSize(hFile, 0); |
407 | } |
408 | |
409 | unsigned long CWadFindHeaderAndSize(FILE *pFile, unsigned long *pnCwadEnd) |
410 | { |
411 | if (!pFile) return -1; |
412 | |
413 | if (fseek(pFile, 0, SEEK_END)) return -1; |
414 | long fsz = ftell(pFile); |
415 | UInt32 buffer[2]; |
416 | long sep, i, offset; |
417 | |
418 | if (pnCwadEnd) *pnCwadEnd = fsz; |
419 | |
420 | fseek(pFile, 0, SEEK_SET); |
421 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
422 | if (buffer[0] == ID_CWAD) |
423 | return 0; |
424 | |
425 | if (fsz < 12) |
426 | return -1; |
427 | |
428 | if (pnCwadEnd) *pnCwadEnd = fsz - 8; |
429 | |
430 | fseek(pFile, -8, SEEK_END); |
431 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
432 | if (buffer[0] == ID_CWAD) { |
433 | fseek(pFile, buffer[1], SEEK_SET); |
434 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
435 | if (buffer[0] == ID_CWAD) |
436 | return buffer[1]; |
437 | } |
438 | |
439 | if (fsz < 132) |
440 | return -1; |
441 | |
442 | for (sep = fsz - 12; sep >= fsz - 132; sep -= 8) { |
443 | fseek(pFile, sep, SEEK_SET); |
444 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
445 | |
446 | if (buffer[0] == ID_SEP) { |
447 | for (i = sep + 4; i < fsz; i += 8) { |
448 | fseek(pFile, i, SEEK_SET); |
449 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
450 | |
451 | offset = buffer[0]; |
452 | if (pnCwadEnd) *pnCwadEnd = offset + buffer[1] - 8; |
453 | fseek(pFile, offset + buffer[1] - 8, SEEK_SET); |
454 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
455 | if (buffer[0] == ID_CWAD) { |
456 | fseek(pFile, offset + buffer[1], SEEK_SET); |
457 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
458 | if (buffer[0] == ID_CWAD) |
459 | return offset + buffer[1]; |
460 | } |
461 | } |
462 | |
463 | break; |
464 | } |
465 | } |
466 | |
467 | return -1; |
468 | } |
469 | |
470 | void CWadDecryptData(UInt8 *pBuffer, unsigned long nBufferLength) |
471 | { |
472 | if (!pBuffer || nBufferLength == 0) return; |
473 | pBuffer += nBufferLength - 1; |
474 | UInt8 byCWadKey; |
475 | byCWadKey = (UInt8)(66 - (nBufferLength << 1)); |
476 | for (unsigned long i = 0; i < nBufferLength; i++) { |
477 | pBuffer[0] ^= (UInt8)(byCWadKey + (i << 1)); |
478 | pBuffer--; |
479 | } |
480 | } |