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 | |
92 | void * CWadOpenArchive(const TCHAR *pszFileName) |
93 | { |
94 | if (!pszFileName) |
95 | return 0; |
96 | |
97 | unsigned long flen; |
98 | FILE *pFile; |
99 | pFile = _tfopen(pszFileName, _T("rb")); |
100 | if (pFile) |
101 | { |
102 | unsigned long nCwadStart = CWadFindHeaderAndSize(pFile, &flen); |
103 | if (nCwadStart != -1) { |
104 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)malloc(sizeof(CWADARCHIVE)); |
105 | if (cOpenArc) { |
106 | cOpenArc->pFile = pFile; |
107 | cOpenArc->nStart = nCwadStart; |
108 | cOpenArc->nEnd = flen; |
109 | cOpenArc->pmszFileList = 0; |
110 | cOpenArc->bHasFileNameBlank = false; |
111 | cOpenArc->pFiles = 0; |
112 | CWadListFilesInternal(cOpenArc); |
113 | if (cOpenArc->pmszFileList) { |
114 | cOpenArc->pszFileName = _tcsdup(pszFileName); |
115 | cOpenArc->nReferences = 0; |
116 | cOpenArc->bOpen = true; |
117 | return cOpenArc; |
118 | } |
119 | |
120 | free(cOpenArc); |
121 | } |
122 | } |
123 | |
124 | fclose(pFile); |
125 | } |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | bool CWadCloseArchive(void *hCWAD) |
131 | { |
132 | if (!hCWAD) |
133 | return false; |
134 | |
135 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
136 | if (cOpenArc->nReferences > 0) { |
137 | cOpenArc->bOpen = false; |
138 | return false; |
139 | } |
140 | fclose(cOpenArc->pFile); |
141 | if (cOpenArc->pFiles) free(cOpenArc->pFiles); |
142 | if (cOpenArc->pmszFileList) free(cOpenArc->pmszFileList); |
143 | if (cOpenArc->pszFileName) free(cOpenArc->pszFileName); |
144 | free(cOpenArc); |
145 | return true; |
146 | } |
147 | |
148 | unsigned long CWadGetArchiveInfo(void *hCWAD, int nInfoType) |
149 | { |
150 | if (!hCWAD) |
151 | return -1; |
152 | |
153 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
154 | switch (nInfoType) { |
155 | case CWAD_INFO_NUM_FILES: |
156 | return cOpenArc->nFiles; |
157 | case CWAD_INFO_SIZE: |
158 | return cOpenArc->nEnd - cOpenArc->nStart; |
159 | default: |
160 | return -1; |
161 | } |
162 | } |
163 | |
164 | unsigned long CWadListFiles(void *hCWAD, char *pmszBuffer, unsigned long nBufferLength) |
165 | { |
166 | if (!hCWAD) |
167 | return 0; |
168 | |
169 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
170 | if (pmszBuffer && nBufferLength >= cOpenArc->nFileListSize) |
171 | memcpy(pmszBuffer, cOpenArc->pmszFileList, cOpenArc->nFileListSize); |
172 | |
173 | return cOpenArc->nFileListSize; |
174 | } |
175 | |
176 | void CWadListFilesInternal(void *hCWAD) |
177 | { |
178 | if (!hCWAD) |
179 | return; |
180 | |
181 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
182 | CWADFILEHEADER CwadFile; |
183 | unsigned long nReqSize = 0; |
184 | cOpenArc->nFiles = 0; |
185 | if (cOpenArc->bHasFileNameBlank && cOpenArc->pmszFileList) { |
186 | *cOpenArc->pmszFileList = 0; |
187 | nReqSize++; |
188 | } |
189 | fseek(cOpenArc->pFile, cOpenArc->nStart + 4, SEEK_SET); |
190 | for (long fpos = ftell(cOpenArc->pFile); fpos != -1 && fpos + sizeof(CWADFILEHEADER) <= cOpenArc->nEnd; fpos = ftell(cOpenArc->pFile)) { |
191 | if (fread(&CwadFile, sizeof(CWADFILEHEADER), 1, cOpenArc->pFile) == 1) { |
192 | if (cOpenArc->pmszFileList) |
193 | { |
194 | if (CwadFile.dwNameLength > 0) { |
195 | if (fread(cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength, 1, cOpenArc->pFile) == 1) |
196 | CWadDecryptData((UInt8 *)cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength); |
197 | cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList + nReqSize; |
198 | } |
199 | else |
200 | cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList; |
201 | cOpenArc->pFiles[cOpenArc->nFiles].pParentArc = cOpenArc; |
202 | cOpenArc->pFiles[cOpenArc->nFiles].dwPackedSize = CwadFile.dwPackedSize; |
203 | cOpenArc->pFiles[cOpenArc->nFiles].dwFullSize = CwadFile.dwFullSize; |
204 | cOpenArc->pFiles[cOpenArc->nFiles].dwFlags = CwadFile.dwFlags; |
205 | cOpenArc->pFiles[cOpenArc->nFiles].nOffset = fpos + sizeof(CWADFILEHEADER) + CwadFile.dwNameLength; |
206 | cOpenArc->pFiles[cOpenArc->nFiles].pBuffer = 0; |
207 | cOpenArc->pFiles[cOpenArc->nFiles].nReferences = 0; |
208 | } |
209 | else { |
210 | fseek(cOpenArc->pFile, CwadFile.dwNameLength, SEEK_CUR); |
211 | } |
212 | nReqSize += CwadFile.dwNameLength; |
213 | if (CwadFile.dwNameLength > 0) { |
214 | if (cOpenArc->pmszFileList) |
215 | cOpenArc->pmszFileList[nReqSize] = 0; |
216 | nReqSize++; |
217 | } |
218 | else if (!cOpenArc->bHasFileNameBlank) { |
219 | cOpenArc->bHasFileNameBlank = true; |
220 | nReqSize++; |
221 | } |
222 | fseek(cOpenArc->pFile, CwadFile.dwPackedSize, SEEK_CUR); |
223 | cOpenArc->nFiles++; |
224 | } |
225 | else break; |
226 | } |
227 | if (cOpenArc->pmszFileList) |
228 | cOpenArc->pmszFileList[nReqSize] = 0; |
229 | nReqSize++; |
230 | if (nReqSize == 1) |
231 | { |
232 | if (cOpenArc->pmszFileList) |
233 | cOpenArc->pmszFileList[nReqSize] = 0; |
234 | nReqSize++; |
235 | } |
236 | if (!cOpenArc->pmszFileList) { |
237 | cOpenArc->pmszFileList = (char *)malloc(nReqSize); |
238 | cOpenArc->nFileListSize = nReqSize; |
239 | if (cOpenArc->nFiles > 0) |
240 | cOpenArc->pFiles = (CWADFILE *)calloc(cOpenArc->nFiles, sizeof(CWADFILE)); |
241 | if (cOpenArc->pmszFileList && (cOpenArc->pFiles || cOpenArc->nFiles == 0)) |
242 | CWadListFilesInternal(hCWAD); |
243 | else { |
244 | if (cOpenArc->pmszFileList) { |
245 | free(cOpenArc->pmszFileList); |
246 | cOpenArc->pmszFileList = 0; |
247 | } |
248 | if (cOpenArc->pFiles) |
249 | free(cOpenArc->pFiles); |
250 | } |
251 | } |
252 | } |
253 | |
254 | void * CWadOpenFile(void *hCWAD, const char *pszFileName) |
255 | { |
256 | if (!hCWAD || !pszFileName) |
257 | return 0; |
258 | |
259 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; |
260 | unsigned long nFile; |
261 | for (nFile = 0; nFile < cOpenArc->nFiles; nFile++) { |
262 | if (strcmp(pszFileName, cOpenArc->pFiles[nFile].pszFileName) == 0) { |
263 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)malloc(sizeof(CWADFILEHANDLE)); |
264 | if (cOpenFile) { |
265 | cOpenFile->f = &cOpenArc->pFiles[nFile]; |
266 | cOpenFile->dwFilePointer = 0; |
267 | cOpenFile->f->nReferences++; |
268 | cOpenArc->nReferences++; |
269 | return cOpenFile; |
270 | } |
271 | } |
272 | } |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | bool CWadCloseFile(void *hFile) |
278 | { |
279 | if (!hFile) |
280 | return false; |
281 | |
282 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
283 | cOpenFile->f->nReferences--; |
284 | cOpenFile->f->pParentArc->nReferences--; |
285 | if (cOpenFile->f->nReferences < 1 && cOpenFile->f->pBuffer) { |
286 | free(cOpenFile->f->pBuffer); |
287 | cOpenFile->f->pBuffer = 0; |
288 | } |
289 | if (cOpenFile->f->pParentArc->nReferences < 1 && !cOpenFile->f->pParentArc->bOpen) |
290 | CWadCloseArchive(cOpenFile->f->pParentArc); |
291 | free(cOpenFile); |
292 | |
293 | return true; |
294 | } |
295 | |
296 | unsigned long CWadGetFileSize(void *hFile) |
297 | { |
298 | if (!hFile) |
299 | return -1; |
300 | |
301 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
302 | return cOpenFile->f->dwFullSize; |
303 | } |
304 | |
305 | unsigned long CWadGetFileInfo(void *hFile, int nInfoType) |
306 | { |
307 | if (!hFile) |
308 | return -1; |
309 | |
310 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
311 | switch (nInfoType) { |
312 | case CWAD_INFO_NUM_FILES: |
313 | return cOpenFile->f->pParentArc->nFiles; |
314 | case CWAD_INFO_SIZE: |
315 | return cOpenFile->f->dwFullSize; |
316 | case CWAD_INFO_COMPRESSED_SIZE: |
317 | return cOpenFile->f->dwPackedSize; |
318 | case CWAD_INFO_FLAGS: |
319 | return cOpenFile->f->dwFlags; |
320 | case CWAD_INFO_PARENT: |
321 | return (unsigned long)cOpenFile->f->pParentArc; |
322 | case CWAD_INFO_POSITION: |
323 | return cOpenFile->dwFilePointer; |
324 | default: |
325 | return -1; |
326 | } |
327 | } |
328 | |
329 | unsigned long CWadSetFilePointer(void *hFile, long nDistanceToMove, int nMoveMethod) |
330 | { |
331 | if (!hFile) |
332 | return -1; |
333 | |
334 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
335 | long fsz = cOpenFile->f->dwFullSize; |
336 | long cpos = cOpenFile->dwFilePointer; |
337 | switch (nMoveMethod) { |
338 | case CWAD_FILE_CURRENT: |
339 | if (cpos + nDistanceToMove < 0 || cpos + nDistanceToMove > fsz) return -1; |
340 | cOpenFile->dwFilePointer += nDistanceToMove; |
341 | break; |
342 | case CWAD_FILE_END: |
343 | if (fsz + nDistanceToMove < 0 || nDistanceToMove > 0) return -1; |
344 | cOpenFile->dwFilePointer = fsz + nDistanceToMove; |
345 | break; |
346 | case CWAD_FILE_BEGIN: |
347 | default: |
348 | if (nDistanceToMove < 0 || nDistanceToMove > fsz) return -1; |
349 | cOpenFile->dwFilePointer = nDistanceToMove; |
350 | } |
351 | |
352 | return cOpenFile->dwFilePointer; |
353 | } |
354 | |
355 | unsigned long CWadReadFile(void *hFile, void *pBuffer, unsigned long nNumberOfBytesToRead) |
356 | { |
357 | if (!hFile || !pBuffer || nNumberOfBytesToRead == 0) |
358 | return 0; |
359 | |
360 | CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; |
361 | CWADARCHIVE *cOpenArc = (CWADARCHIVE *)cOpenFile->f->pParentArc; |
362 | if (cOpenFile->dwFilePointer >= cOpenFile->f->dwFullSize) |
363 | return 0; |
364 | if (cOpenFile->dwFilePointer + nNumberOfBytesToRead > cOpenFile->f->dwFullSize) |
365 | nNumberOfBytesToRead = cOpenFile->f->dwFullSize - cOpenFile->dwFilePointer; |
366 | unsigned long nBytesRead = nNumberOfBytesToRead; |
367 | if (cOpenFile->f->dwFlags & 1) { |
368 | if (!cOpenFile->f->pBuffer) { |
369 | cOpenFile->f->pBuffer = (UInt8 *)malloc(cOpenFile->f->dwFullSize); |
370 | UInt8 *compbuffer = (UInt8 *)malloc(cOpenFile->f->dwPackedSize); |
371 | bool bReadOK = false; |
372 | if (cOpenFile->f->pBuffer && compbuffer) { |
373 | fseek(cOpenArc->pFile, cOpenFile->f->nOffset, SEEK_SET); |
374 | if (fread(compbuffer, 1, cOpenFile->f->dwPackedSize, cOpenArc->pFile) == cOpenFile->f->dwPackedSize) |
375 | if (uncompress(cOpenFile->f->pBuffer, &nBytesRead, compbuffer, cOpenFile->f->dwPackedSize) == Z_OK) |
376 | bReadOK = true; |
377 | } |
378 | if (!bReadOK && cOpenFile->f->pBuffer) { |
379 | free(cOpenFile->f->pBuffer); |
380 | cOpenFile->f->pBuffer = 0; |
381 | nBytesRead = 0; |
382 | } |
383 | if (compbuffer) |
384 | free(compbuffer); |
385 | } |
386 | if (cOpenFile->f->pBuffer) |
387 | memcpy(pBuffer, cOpenFile->f->pBuffer + cOpenFile->dwFilePointer, nNumberOfBytesToRead); |
388 | } |
389 | else { |
390 | fseek(cOpenArc->pFile, cOpenFile->f->nOffset + cOpenFile->dwFilePointer, SEEK_SET); |
391 | nBytesRead = fread(pBuffer, 1, nNumberOfBytesToRead, cOpenArc->pFile); |
392 | } |
393 | |
394 | cOpenFile->dwFilePointer += nBytesRead; |
395 | return nBytesRead; |
396 | } |
397 | |
398 | unsigned long CWadFindHeader(FILE *hFile) |
399 | { |
400 | return CWadFindHeaderAndSize(hFile, 0); |
401 | } |
402 | |
403 | unsigned long CWadFindHeaderAndSize(FILE *pFile, unsigned long *pnCwadEnd) |
404 | { |
405 | if (!pFile) return -1; |
406 | |
407 | if (fseek(pFile, 0, SEEK_END)) return -1; |
408 | long fsz = ftell(pFile); |
409 | UInt32 buffer[2]; |
410 | long sep, i, offset; |
411 | |
412 | if (pnCwadEnd) *pnCwadEnd = fsz; |
413 | |
414 | fseek(pFile, 0, SEEK_SET); |
415 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
416 | if (buffer[0] == ID_CWAD) |
417 | return 0; |
418 | |
419 | if (fsz < 12) |
420 | return -1; |
421 | |
422 | if (pnCwadEnd) *pnCwadEnd = fsz - 8; |
423 | |
424 | fseek(pFile, -8, SEEK_END); |
425 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
426 | if (buffer[0] == ID_CWAD) { |
427 | fseek(pFile, buffer[1], SEEK_SET); |
428 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
429 | if (buffer[0] == ID_CWAD) |
430 | return buffer[1]; |
431 | } |
432 | |
433 | if (fsz < 132) |
434 | return -1; |
435 | |
436 | for (sep = fsz - 12; sep >= fsz - 132; sep -= 8) { |
437 | fseek(pFile, sep, SEEK_SET); |
438 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
439 | |
440 | if (buffer[0] == ID_SEP) { |
441 | for (i = sep + 4; i < fsz; i += 8) { |
442 | fseek(pFile, i, SEEK_SET); |
443 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
444 | |
445 | offset = buffer[0]; |
446 | if (pnCwadEnd) *pnCwadEnd = offset + buffer[1] - 8; |
447 | fseek(pFile, offset + buffer[1] - 8, SEEK_SET); |
448 | if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; |
449 | if (buffer[0] == ID_CWAD) { |
450 | fseek(pFile, offset + buffer[1], SEEK_SET); |
451 | if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; |
452 | if (buffer[0] == ID_CWAD) |
453 | return offset + buffer[1]; |
454 | } |
455 | } |
456 | |
457 | break; |
458 | } |
459 | } |
460 | |
461 | return -1; |
462 | } |
463 | |
464 | void CWadDecryptData(UInt8 *pBuffer, unsigned long nBufferLength) |
465 | { |
466 | if (!pBuffer || nBufferLength == 0) return; |
467 | pBuffer += nBufferLength - 1; |
468 | UInt8 byCWadKey; |
469 | byCWadKey = (UInt8)(66 - (nBufferLength << 1)); |
470 | for (unsigned long i = 0; i < nBufferLength; i++) { |
471 | pBuffer[0] ^= (UInt8)(byCWadKey + (i << 1)); |
472 | pBuffer--; |
473 | } |
474 | } |