1 /*---------------------------------------------------------------------------*
2
3 Copyright 2010-2013 Nintendo. All rights reserved.
4
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10
11 *---------------------------------------------------------------------------*/
12
13
14 /*
15
16
17 THIS IS CROSS PLATFORM SOURCE CODE
18
19 IF YOU MODIFY THIS SOURCE YOU *MUST* ENSURE IT STILL BUILDS ON BOTH
20 WIN32 AND CAFE.
21
22
23 */
24
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <cafe/zipfile.h>
29
30 #define ZIPFILE_ENDIAN_LITTLE 0
31 #define ZIPFILE_ENDIAN_BIG 1
32
33 #ifndef ZIP_MAKEID4
34 #define ZIP_MAKEID4(endian,a,b,c,d) ((endian==ZIPFILE_ENDIAN_LITTLE) ? \
35 ((((UINT32)a)&0xFF) | ((((UINT32)b)&0xFF)<<8) | ((((UINT32)c)&0xFF)<<16) | ((((UINT32)d)&0xFF)<<24)) : \
36 ((((UINT32)d)&0xFF) | ((((UINT32)c)&0xFF)<<8) | ((((UINT32)b)&0xFF)<<16) | ((((UINT32)a)&0xFF)<<24)))
37 #endif
38
39 #ifndef ZIP_TARGETID4
40 #define ZIP_TARGETID4(a,b,c,d) ZIP_MAKEID4(ZIPFILE_ENDIAN,a,b,c,d)
41 #endif
42
43 #define ZIP_RAW_FILEENTRY_MARKER ZIP_TARGETID4('P','K',3,4)
44 #define ZIP_FILECDS_MARKER ZIP_TARGETID4('P','K',1,2)
45 #define ZIP_FILECDS_TAIL_MARKER ZIP_TARGETID4('P','K',5,6)
46
47 #define SWAP32(x) (((((UINT32)(x))&0xFF)<<24) | ((((UINT32)(x))&0xFF00)<<8) | ((((UINT32)(x))&0xFF0000)>>8) | ((((UINT32)(x))&0xFF000000)>>24))
48 #define SWAP16(x) (((((UINT16)(x))&0xFF)<<8) | ((((UINT16)(x))&0xFF00)>>8))
49
50 #ifdef _MSC_VER
51 #pragma pack (push, 1)
52 #define ZIPFILE_ENDIAN ZIPFILE_ENDIAN_LITTLE
53 #else
54 #ifdef EPPC
55 #pragma pack(1)
56 #define ZIPFILE_ENDIAN ZIPFILE_ENDIAN_BIG
57 #else
58 #error !!! Unknown compilation target
59 #endif
60 #endif
61
62 struct _ZIPFILE_RAW_FILEENTRY
63 {
64 UINT32 mMarker;
65 UINT16 mMinVer;
66 UINT16 mFlags;
67 UINT16 mCompressionMethod;
68 UINT32 mDosDateTime;
69 UINT32 mCRC;
70 UINT32 mCompSizeBytes;
71 UINT32 mUncompSizeBytes;
72 UINT16 mFileNameLen;
73 UINT16 mExtraLen;
74 /* Filename for 'mFileNameLen' */
75 /* Extra for 'mExtraLen' */
76 };
77 typedef struct _ZIPFILE_RAW_FILEENTRY ZIPFILE_RAW_FILEENTRY;
78
79 struct _ZIP_FILECDS
80 {
81 UINT32 mMarker;
82 UINT8 mMadeBy;
83 UINT8 mHostOS;
84 UINT8 mMinVer;
85 UINT8 mTargetOS;
86 UINT16 mFlags;
87 UINT16 mCompressionMethod;
88 UINT16 mModFileTime;
89 UINT16 mModFileDate;
90 UINT32 mCRC;
91 UINT32 mCompSizeBytes;
92 UINT32 mUncompSizeBytes;
93 UINT16 mFileNameLen;
94 UINT16 mExtraLen;
95 UINT16 mCommentLen;
96 UINT16 mDiskNum;
97 UINT16 mIntFileAttr;
98 UINT32 mExtFileAttr;
99 UINT32 mRelHdrOffset;
100 /* Filename for 'mFileNameLen' */
101 /* Extra for 'mExtraLen' */
102 /* Comment for 'mCommentLen' */
103 };
104 typedef struct _ZIP_FILECDS ZIP_FILECDS;
105
106 struct _ZIP_FILECDS_TAIL
107 {
108 UINT32 mMarker;
109 UINT16 mDiskNum;
110 UINT16 mDiskNumCDS;
111 UINT16 mNumEntDisk;
112 UINT16 mNumEntCDS;
113 UINT32 mSizeCDS;
114 UINT32 mDiskOffsetCDS;
115 UINT16 mCommentLen;
116 /* comment for 'mCommentLen' */
117 };
118 typedef struct _ZIP_FILECDS_TAIL ZIP_FILECDS_TAIL;
119
120 #ifdef WIN32
121 #pragma pack (pop)
122 #define STRNICMP _strnicmp
123 #endif
124 #ifdef EPPC
125 #pragma pack ()
126 #define STRNICMP _mystrnicmp
127
128 /* cafe doesn't have this function so it is provided here */
_mystrnicmp(char const * apStr1,char const * apStr2,size_t len)129 static int _mystrnicmp(char const *apStr1, char const *apStr2, size_t len)
130 {
131 int c1;
132 int c2;
133
134 if (!len)
135 return 0;
136 do {
137 c1 = (int)*apStr1; if ((c1 >='a') && (c1 <='z')) c1 -= ('a'-'A');
138 c2 = (int)*apStr2; if ((c2 >='a') && (c2 <='z')) c2 -= ('a'-'A');
139 if ((!c1) && (!c2))
140 return 0;
141 if (!c1)
142 return -1;
143 if (!c2)
144 return 1;
145 c1 -= c2;
146 if (c1)
147 return c1;
148 apStr1++;
149 apStr2++;
150 } while (--len);
151 return 0;
152 }
153 #endif
154
155 /* ---------------------------------------------------------------------- */
156
157 static const UINT32 sgCRC32_tab[] =
158 {
159 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
160 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
161 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
162 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
163 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
164 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
165 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
166 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
167 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
168 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
169 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
170 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
171 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
172 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
173 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
174 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
175 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
176 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
177 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
178 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
179 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
180 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
181 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
182 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
183 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
184 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
185 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
186 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
187 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
188 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
189 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
190 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
191 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
192 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
193 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
194 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
195 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
196 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
197 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
198 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
199 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
200 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
201 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
202 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
203 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
204 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
205 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
206 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
207 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
208 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
209 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
210 0x2D02EF8D
211 };
212
ZIPFILE_CalcCRC(UINT8 const * apData,UINT32 aDataBytes)213 UINT32 ZIPFILE_CalcCRC(UINT8 const *apData, UINT32 aDataBytes)
214 {
215 UINT32 work;
216
217 if (!aDataBytes)
218 return 0;
219
220 work = 0xFFFFFFFF;
221
222 do {
223 work = sgCRC32_tab[(work ^ (*apData)) & 0xFF] ^ (work >> 8);
224 apData++;
225 } while (--aDataBytes);
226
227 return work ^ 0xFFFFFFFF;
228 }
229
230 /* ---------------------------------------------------------------------- */
231
sZIP_EntryBytes(ZIPFILE_RAW_FILEENTRY * apEntry)232 static UINT32 sZIP_EntryBytes(ZIPFILE_RAW_FILEENTRY *apEntry)
233 {
234 return sizeof(ZIPFILE_RAW_FILEENTRY) + apEntry->mCompSizeBytes + apEntry->mFileNameLen + apEntry->mExtraLen;
235 }
236
sZIP_CDSBytes(ZIP_FILECDS * apCDS)237 static UINT32 sZIP_CDSBytes(ZIP_FILECDS *apCDS)
238 {
239 return sizeof(ZIP_FILECDS) + apCDS->mFileNameLen + apCDS->mExtraLen + apCDS->mCommentLen;
240 }
241
sZIP_SwapFILEENTRY(ZIPFILE_RAW_FILEENTRY * apEntry)242 static void sZIP_SwapFILEENTRY(ZIPFILE_RAW_FILEENTRY *apEntry)
243 {
244 apEntry->mMarker = SWAP32(apEntry->mMarker);
245 apEntry->mMinVer = SWAP16(apEntry->mMinVer);
246 apEntry->mFlags = SWAP16(apEntry->mFlags);
247 apEntry->mCompressionMethod = SWAP16(apEntry->mCompressionMethod);
248 apEntry->mDosDateTime = SWAP32(apEntry->mDosDateTime);
249 apEntry->mCRC = SWAP32(apEntry->mCRC);
250 apEntry->mCompSizeBytes = SWAP32(apEntry->mCompSizeBytes);
251 apEntry->mUncompSizeBytes = SWAP32(apEntry->mUncompSizeBytes);
252 apEntry->mFileNameLen = SWAP16(apEntry->mFileNameLen);
253 apEntry->mExtraLen = SWAP16(apEntry->mExtraLen);
254 }
255
sZIP_SwapCDS(ZIP_FILECDS * apCDS)256 static void sZIP_SwapCDS(ZIP_FILECDS *apCDS)
257 {
258 apCDS->mMarker = SWAP32(apCDS->mMarker);
259 apCDS->mFlags = SWAP16(apCDS->mFlags);
260 apCDS->mCompressionMethod = SWAP16(apCDS->mCompressionMethod);
261 apCDS->mModFileTime = SWAP16(apCDS->mModFileTime);
262 apCDS->mModFileDate = SWAP16(apCDS->mModFileDate);
263 apCDS->mCRC = SWAP32(apCDS->mCRC);
264 apCDS->mCompSizeBytes = SWAP32(apCDS->mCompSizeBytes);
265 apCDS->mUncompSizeBytes = SWAP32(apCDS->mUncompSizeBytes);
266 apCDS->mFileNameLen = SWAP16(apCDS->mFileNameLen);
267 apCDS->mExtraLen = SWAP16(apCDS->mExtraLen);
268 apCDS->mCommentLen = SWAP16(apCDS->mCommentLen);
269 apCDS->mDiskNum = SWAP16(apCDS->mDiskNum);
270 apCDS->mIntFileAttr = SWAP16(apCDS->mIntFileAttr);
271 apCDS->mExtFileAttr = SWAP32(apCDS->mExtFileAttr);
272 apCDS->mRelHdrOffset = SWAP32(apCDS->mRelHdrOffset);
273 }
274
sZIP_SwapCDS_TAIL(ZIP_FILECDS_TAIL * apTail)275 static void sZIP_SwapCDS_TAIL(ZIP_FILECDS_TAIL *apTail)
276 {
277 apTail->mMarker = SWAP32(apTail->mMarker);
278 apTail->mDiskNum = SWAP16(apTail->mDiskNum);
279 apTail->mDiskNumCDS = SWAP16(apTail->mDiskNumCDS);
280 apTail->mNumEntDisk = SWAP16(apTail->mNumEntDisk);
281 apTail->mNumEntCDS = SWAP16(apTail->mNumEntCDS);
282 apTail->mSizeCDS = SWAP32(apTail->mSizeCDS);
283 apTail->mDiskOffsetCDS = SWAP32(apTail->mDiskOffsetCDS);
284 apTail->mCommentLen = SWAP16(apTail->mCommentLen);
285 }
286
287 /* ---------------------------------------------------------------------- */
288
sEat_FileEntry(UINT8 const ** appSrc,UINT32 * apBytesLeftInZipFile)289 static int sEat_FileEntry(UINT8 const **appSrc, UINT32 *apBytesLeftInZipFile)
290 {
291 ZIPFILE_RAW_FILEENTRY entry;
292 UINT32 chk;
293
294 memcpy(&entry,*appSrc,sizeof(ZIPFILE_RAW_FILEENTRY));
295 #if (ZIPFILE_ENDIAN != ZIPFILE_ENDIAN_LITTLE)
296 sZIP_SwapFILEENTRY(&entry);
297 #endif
298 chk = sZIP_EntryBytes(&entry);
299 if (chk > *apBytesLeftInZipFile)
300 return ERRCODE_ZIPFILE_INVALID_FILEENTRY;
301 if (entry.mUncompSizeBytes < entry.mCompSizeBytes)
302 return ERRCODE_ZIPFILE_INVALID_FILEENTRY;
303 *appSrc += chk;
304 *apBytesLeftInZipFile -= chk;
305 return 0;
306 }
307
sEat_CDS(UINT8 const ** appSrc,UINT32 * apBytesLeftInZipFile)308 static int sEat_CDS(UINT8 const **appSrc, UINT32 *apBytesLeftInZipFile)
309 {
310 ZIP_FILECDS cds;
311 UINT32 chk;
312
313 memcpy(&cds, *appSrc, sizeof(ZIP_FILECDS));
314 #if (ZIPFILE_ENDIAN != ZIPFILE_ENDIAN_LITTLE)
315 sZIP_SwapCDS(&cds);
316 #endif
317 chk = sZIP_CDSBytes(&cds);
318 if (chk > *apBytesLeftInZipFile)
319 return ERRCODE_ZIPFILE_INVALID_CDS;
320
321 *appSrc += chk;
322 *apBytesLeftInZipFile -= chk;
323
324 return 0;
325 }
326
sEat_CDSTail(UINT8 const ** appSrc,UINT32 * apBytesLeftInZipFile)327 static int sEat_CDSTail(UINT8 const **appSrc, UINT32 *apBytesLeftInZipFile)
328 {
329 ZIP_FILECDS_TAIL cdsTail;
330 UINT32 chk;
331
332 memcpy(&cdsTail, *appSrc, sizeof(ZIP_FILECDS_TAIL));
333 #if (ZIPFILE_ENDIAN != ZIPFILE_ENDIAN_LITTLE)
334 sZIP_SwapCDS_TAIL(&cdsTail);
335 #endif
336 chk = sizeof(cdsTail) + cdsTail.mCommentLen;
337 if (chk > *apBytesLeftInZipFile)
338 return ERRCODE_ZIPFILE_INVALID_CDSTAIL;
339
340 *appSrc += chk;
341 *apBytesLeftInZipFile -= chk;
342
343 return 0;
344 }
345
sZIP_ValidateFileStructure(UINT8 const * apFileData,UINT32 aFileBytes,UINT32 * apRetFileCount)346 static int sZIP_ValidateFileStructure(UINT8 const *apFileData, UINT32 aFileBytes, UINT32 *apRetFileCount)
347 {
348 int err;
349 UINT8 const * pWork;
350 UINT32 left;
351 UINT32 chk;
352 UINT32 fileCount;
353 UINT32 cdsCount;
354
355 if (apRetFileCount)
356 *apRetFileCount = 0;
357 fileCount = cdsCount = 0;
358
359 if (aFileBytes < sizeof(UINT32))
360 return ERRCODE_ZIPFILE_INVALID_BAD_FILESIZE;
361
362 pWork = apFileData;
363 left = aFileBytes;
364
365 memcpy(&chk, pWork, sizeof(UINT32));
366 if (chk != ZIP_RAW_FILEENTRY_MARKER)
367 return ERRCODE_ZIPFILE_INVALID_NO_FILE_ENTRIES;
368
369 /* file entries */
370 do {
371 if (left < sizeof(ZIPFILE_RAW_FILEENTRY))
372 return ERRCODE_ZIPFILE_INVALID_UNEXPECTED_EOF;
373 fileCount++;
374 err = sEat_FileEntry(&pWork,&left);
375 if (err)
376 return err;
377 if (left < sizeof(ZIP_FILECDS))
378 return ERRCODE_ZIPFILE_INVALID_UNEXPECTED_EOF;
379 memcpy(&chk, pWork, sizeof(UINT32));
380 } while (chk == ZIP_RAW_FILEENTRY_MARKER);
381
382 /* cds headers */
383 if (chk != ZIP_FILECDS_MARKER)
384 return ERRCODE_ZIPFILE_INVALID_NO_CDS;
385 do {
386 if ((left < sizeof(ZIP_FILECDS)) && (left < sizeof(ZIP_FILECDS_TAIL)))
387 return ERRCODE_ZIPFILE_INVALID_UNEXPECTED_EOF;
388 memcpy(&chk, pWork, sizeof(UINT32));
389 if (chk != ZIP_FILECDS_MARKER)
390 break;
391 cdsCount++;
392 } while (!sEat_CDS(&pWork,&left));
393
394 if (fileCount != cdsCount)
395 return ERRCODE_ZIPFILE_INVALID_BAD_FILE_COUNT;
396
397 if (chk != ZIP_FILECDS_TAIL_MARKER)
398 return ERRCODE_ZIPFILE_INVALID_NO_CDS_TAIL;
399
400 err = sEat_CDSTail(&pWork,&left);
401 if (err)
402 return err;
403
404 if (left)
405 return ERRCODE_ZIPFILE_INVALID_JUNK_AT_EOF;
406
407 if (apRetFileCount)
408 *apRetFileCount = fileCount;
409
410 return 0;
411 }
412
413 /* ---------------------------------------------------------------------- */
414
sCompStrLens(char const * pStr1,UINT32 strLen1,char const * pStr2,UINT32 strLen2)415 static int sCompStrLens(char const *pStr1, UINT32 strLen1, char const *pStr2, UINT32 strLen2)
416 {
417 int c,v;
418
419 c = ((int)strLen2) - ((int)strLen1);
420 if (c > 0)
421 {
422 /* str2 longer than str1 */
423 c = STRNICMP(pStr1, pStr2, strLen1);
424 if (!c)
425 return -1;
426 return c;
427 }
428 v = STRNICMP(pStr1, pStr2, strLen2);
429 if (c < 0)
430 {
431 /* str2 shorter than str1 name */
432 if (!v)
433 return 1;
434 }
435 return v;
436 }
437
sComparePaths(char const * pPath1,UINT32 path1Len,char const * pPath2,UINT32 path2Len)438 static int sComparePaths(char const *pPath1, UINT32 path1Len, char const *pPath2, UINT32 path2Len)
439 {
440 char const *pSlash1;
441 char const *pSlash2;
442 UINT32 left;
443 int c;
444
445 pSlash1 = pPath1;
446 left = path1Len;
447 do {
448 if (*pSlash1 == '/')
449 break;
450 pSlash1++;
451 } while (--left);
452 if (!left)
453 pSlash1 = NULL;
454
455 pSlash2 = pPath2;
456 left = path2Len;
457 do {
458 if (*pSlash2 == '/')
459 break;
460 pSlash2++;
461 } while (--left);
462 if (!left)
463 pSlash2 = NULL;
464
465 if ((!pSlash1) && (!pSlash2))
466 return sCompStrLens(pPath1,path1Len,pPath2,path2Len);
467
468 if (pSlash1 && (!pSlash2))
469 return 1;
470 if ((!pSlash1) && (pSlash2))
471 return -1;
472
473 /* both have slashes */
474 c = sCompStrLens(pPath1, (UINT32)(pSlash1-pPath1), pPath2, (UINT32)(pSlash2-pPath2));
475 if (c)
476 return c;
477
478 /* both have slashes and are the same up to the first slash */
479 left = (UINT32)(pSlash1 - pPath1);
480
481 /* both have slashes and start with the same stuff */
482 return sComparePaths(pPath1+left+1, path1Len - (left+1), pPath2+left+1, path2Len - (left+1));
483 }
484
sCompare(void const * p1,void const * p2)485 static int sCompare(void const *p1, void const *p2)
486 {
487 ZIPPED_FILE const * pFile1;
488 ZIPPED_FILE const * pFile2;
489
490 pFile1 = (ZIPPED_FILE const *)p1;
491 pFile2 = (ZIPPED_FILE const *)p2;
492
493 if ((!pFile1->mCompBytes) && (pFile2->mCompBytes))
494 return -1;
495 if ((pFile1->mCompBytes) && (!pFile2->mCompBytes))
496 return 1;
497 return sComparePaths(pFile1->mpNameOnly, pFile1->mNameOnlyLen, pFile2->mpNameOnly, pFile2->mNameOnlyLen);
498 }
499
sCountDirsNeeded(ZIPPED_FILE * apFiles,UINT32 aNumFiles)500 static UINT32 sCountDirsNeeded(ZIPPED_FILE *apFiles, UINT32 aNumFiles)
501 {
502 char const * pLook;
503 char const * pComp;
504 UINT32 left;
505 ZIPPED_FILE * pRest;
506 UINT32 ret;
507 UINT32 subLen;
508 UINT32 numSub;
509
510 /* don't need dir for files without a slash at beginning of sorted list */
511 do {
512 pLook = apFiles->mpNameOnly;
513 left = apFiles->mNameOnlyLen;
514 do {
515 if (*pLook=='/')
516 break;
517 pLook++;
518 } while (--left);
519 if (left)
520 break;
521 apFiles++;
522 } while (--aNumFiles);
523 if (!aNumFiles)
524 return 0; /* no subdirs at this point */
525
526 pLook++;
527 subLen = (UINT32)(pLook-apFiles->mpNameOnly);
528 if (aNumFiles==1)
529 {
530 apFiles->mpNameOnly += subLen;
531 apFiles->mNameOnlyLen -= subLen;
532 return 1 + sCountDirsNeeded(apFiles, 1);
533 }
534
535 ret = 1;
536 pComp = apFiles->mpNameOnly;
537 apFiles->mpNameOnly += subLen;
538 apFiles->mNameOnlyLen -= subLen;
539 pRest = apFiles+1;
540 aNumFiles--;
541 numSub = 1;
542 do {
543 if ((pRest->mNameOnlyLen < subLen) || (STRNICMP(pRest->mpNameOnly, pComp, subLen)))
544 break;
545 pRest->mpNameOnly += subLen;
546 pRest->mNameOnlyLen -= subLen;
547 numSub++;
548 pRest++;
549 } while (--aNumFiles);
550
551 ret += sCountDirsNeeded(apFiles, numSub);
552
553 if (aNumFiles)
554 ret += sCountDirsNeeded(pRest, aNumFiles);
555
556 return ret;
557 }
558
sDirify(ZIPPED_DIR * pThisDir,ZIPPED_FILE * apFiles,UINT32 aNumFiles,ZIPPED_DIR ** ppFreeDir)559 static void sDirify(ZIPPED_DIR *pThisDir, ZIPPED_FILE *apFiles, UINT32 aNumFiles, ZIPPED_DIR **ppFreeDir)
560 {
561 char const * pLook;
562 char const * pComp;
563 UINT32 left;
564 ZIPPED_FILE * pRest;
565 UINT32 subLen;
566 UINT32 numSub;
567 ZIPPED_DIR * pNewDir;
568 ZIPPED_DIR * pPrev;
569
570 /* don't need dir for files without a slash at beginning of sorted list */
571 pRest = apFiles;
572 subLen = 0;
573 numSub = aNumFiles;
574 do {
575 pLook = pRest->mpNameOnly;
576 left = pRest->mNameOnlyLen;
577 do {
578 if (*pLook=='/')
579 break;
580 pLook++;
581 } while (--left);
582 if (left)
583 break;
584 subLen++;
585 pRest->mpParentDir = pThisDir;
586 pRest++;
587 } while (--numSub);
588
589 if (subLen)
590 {
591 pThisDir->mpFilesArray = apFiles;
592 pThisDir->mNumFiles = subLen;
593 apFiles = pRest;
594 aNumFiles -= subLen;
595 if (!numSub)
596 return;
597 }
598 else
599 {
600 pThisDir->mpFilesArray = NULL;
601 pThisDir->mNumFiles = 0;
602 }
603
604 /* subdirs found */
605
606 pNewDir = *ppFreeDir;
607 (*ppFreeDir)++;
608
609 subLen = (UINT32)(pLook-apFiles->mpNameOnly);
610
611 pNewDir->mpParentDir = pThisDir;
612 pNewDir->mpNameOnly = apFiles->mpNameOnly;
613 pNewDir->mNameOnlyLen = subLen;
614 pNewDir->mpNextSib = NULL;
615 if (!pThisDir->mpSubDirList)
616 pThisDir->mpSubDirList = pNewDir;
617 else
618 {
619 pPrev = pThisDir->mpSubDirList;
620 while (pPrev->mpNextSib)
621 pPrev = pPrev->mpNextSib;
622 pPrev->mpNextSib = pNewDir;
623 }
624
625 pLook++;
626 subLen++;
627
628 if (aNumFiles==1)
629 {
630 apFiles->mpNameOnly += subLen;
631 apFiles->mNameOnlyLen -= subLen;
632 sDirify(pNewDir, apFiles, 1, ppFreeDir);
633 return;
634 }
635
636 pComp = apFiles->mpNameOnly;
637 apFiles->mpNameOnly += subLen;
638 apFiles->mNameOnlyLen -= subLen;
639 pRest = apFiles+1;
640 aNumFiles--;
641 numSub = 1;
642 do {
643 if ((pRest->mNameOnlyLen < subLen) || (STRNICMP(pRest->mpNameOnly, pComp, subLen)))
644 break;
645 pRest->mpNameOnly += subLen;
646 pRest->mNameOnlyLen -= subLen;
647 numSub++;
648 pRest++;
649 } while (--aNumFiles);
650
651 sDirify(pNewDir, apFiles, numSub, ppFreeDir);
652
653 if (aNumFiles)
654 sDirify(pThisDir, pRest, aNumFiles, ppFreeDir);
655 }
656
ZIPFILE_CreateDir(UINT8 const * apFileData,UINT32 aFileBytes,ZIPFILE_pf_Alloc afAlloc,ZIPFILE_pf_Free afFree,ZIPPED_DIR ** appRetDir)657 int ZIPFILE_CreateDir(UINT8 const *apFileData, UINT32 aFileBytes, ZIPFILE_pf_Alloc afAlloc, ZIPFILE_pf_Free afFree, ZIPPED_DIR **appRetDir)
658 {
659 UINT8 const * pScan;
660 int err;
661 UINT32 fileCount;
662 UINT8 * pAlloc;
663 ZIPPED_FILE * pFiles;
664 ZIPPED_FILE * pWorkFile;
665 ZIPPED_DIR * pDirs;
666 ZIPFILE_RAW_FILEENTRY entry;
667 UINT32 ix;
668 UINT32 dirCount;
669
670 if (!appRetDir)
671 return ERRCODE_ZIPFILE_ARG;
672 *appRetDir = NULL;
673
674 fileCount = 0;
675 err = sZIP_ValidateFileStructure(apFileData, aFileBytes, &fileCount);
676 if (err)
677 return err;
678 if (!fileCount)
679 return ERRCODE_ZIPFILE_EMPTY;
680
681 ix = sizeof(ZIPPED_FILE) * fileCount;
682 pAlloc = (UINT8 *)afAlloc(ix);
683 if (!pAlloc)
684 return ERRCODE_ZIPFILE_OUTOFMEMORY;
685 memset(pAlloc, 0, ix);
686 pFiles = (ZIPPED_FILE *)pAlloc;
687
688 pWorkFile = pFiles;
689 pScan = apFileData;
690 for(ix=0;ix<fileCount;ix++)
691 {
692 memcpy(&entry,pScan,sizeof(ZIPFILE_RAW_FILEENTRY));
693 #if (ZIPFILE_ENDIAN != ZIPFILE_ENDIAN_LITTLE)
694 sZIP_SwapFILEENTRY(&entry);
695 #endif
696 pWorkFile->mpNameOnly = ((char const *)pScan) + sizeof(ZIPFILE_RAW_FILEENTRY);
697 pWorkFile->mNameOnlyLen = entry.mFileNameLen;
698 pWorkFile->mCompBytes = entry.mCompSizeBytes;
699 pWorkFile++;
700 pScan += sZIP_EntryBytes(&entry);
701 }
702
703 qsort(pFiles, fileCount, sizeof(ZIPPED_FILE), sCompare);
704
705 /* ignore empty files at the start of the sorted list */
706 ix = fileCount;
707 while (!pFiles->mCompBytes)
708 {
709 ix--;
710 pFiles++;
711 if (!ix)
712 break;
713 }
714 if (!ix)
715 {
716 free(pAlloc);
717 return ERRCODE_ZIPFILE_EMPTY;
718 }
719
720 dirCount = 1 + sCountDirsNeeded(pFiles,ix);
721
722 /* reset and restart with a fresh allocation including the dirs we need */
723
724 afFree(pAlloc);
725 ix = (sizeof(ZIPPED_FILE)*fileCount) + (sizeof(ZIPPED_DIR) * dirCount);
726 pAlloc = (UINT8 *)afAlloc( ix );
727 if (!pAlloc)
728 return ERRCODE_ZIPFILE_OUTOFMEMORY;
729 memset(pAlloc, 0, ix);
730
731 pDirs = (ZIPPED_DIR *)pAlloc;
732 pFiles = (ZIPPED_FILE *)(pAlloc + (sizeof(ZIPPED_DIR) * dirCount));
733 *appRetDir = pDirs;
734 pDirs->mpNameOnly = "";
735 pDirs->mNameOnlyLen = 0;
736 pDirs++;
737
738 pWorkFile = pFiles;
739 pScan = apFileData;
740 for(ix=0;ix<fileCount;ix++)
741 {
742 memcpy(&entry,pScan,sizeof(ZIPFILE_RAW_FILEENTRY));
743 #if (ZIPFILE_ENDIAN != ZIPFILE_ENDIAN_LITTLE)
744 sZIP_SwapFILEENTRY(&entry);
745 #endif
746 pWorkFile->mpNameOnly = ((char const *)pScan) + sizeof(ZIPFILE_RAW_FILEENTRY);
747 pWorkFile->mNameOnlyLen = entry.mFileNameLen;
748 pWorkFile->mpCompData = ((UINT8 const *)pWorkFile->mpNameOnly) + entry.mFileNameLen + entry.mExtraLen;
749 pWorkFile->mCompBytes = entry.mCompSizeBytes;
750 pWorkFile->mUncompBytes = entry.mUncompSizeBytes;
751 pWorkFile->mUncompCRC = entry.mCRC;
752 pWorkFile++;
753 pScan += sZIP_EntryBytes(&entry);
754 }
755
756 qsort(pFiles, fileCount, sizeof(ZIPPED_FILE), sCompare);
757
758 /* ignore empty files at the start of the sorted list */
759 while (!pFiles->mCompBytes)
760 {
761 fileCount--;
762 pFiles++;
763 if (!fileCount)
764 break;
765 }
766
767 sDirify(*appRetDir, pFiles, fileCount, &pDirs);
768
769 if (pDirs != ((*appRetDir)+dirCount))
770 {
771 afFree(pAlloc);
772 return ERRCODE_ZIPFILE_INTERNAL;
773 }
774
775 return 0;
776 }
777
sFindInDir(ZIPPED_DIR * apDir,char const * apPath,ZIPPED_FILE ** apRetEntryPtr)778 static int sFindInDir(ZIPPED_DIR *apDir, char const *apPath, ZIPPED_FILE **apRetEntryPtr)
779 {
780 char const * pSlash;
781 char c;
782 int partLen;
783 int err;
784 UINT32 ix;
785 ZIPPED_FILE * pEntry;
786 ZIPPED_DIR * pSubDir;
787
788 *apRetEntryPtr = NULL;
789
790 if (!(*apPath))
791 return ERRCODE_ZIPFILE_ARG; // argument error
792
793 pSlash = apPath;
794 do {
795 c = *pSlash;
796 if ((!c) || (c=='/'))
797 break;
798 pSlash++;
799 } while (1);
800 partLen = (int)(pSlash - apPath);
801 if (!c)
802 {
803 pEntry = apDir->mpFilesArray;
804 for(ix=0;ix<apDir->mNumFiles;ix++)
805 {
806 if (pEntry->mNameOnlyLen == partLen)
807 {
808 if (!STRNICMP(apPath, apDir->mpFilesArray[ix].mpNameOnly, partLen))
809 break;
810 }
811 pEntry++;
812 }
813 if (ix != apDir->mNumFiles)
814 {
815 *apRetEntryPtr = pEntry;
816 return 0;
817 }
818 return ERRCODE_ZIPFILE_FILENOTFOUND; // file not found
819 }
820
821 /* there is a slash, so this part is a subdirectory */
822 pSubDir = apDir->mpSubDirList;
823 if (pSubDir)
824 {
825 do {
826 err = sFindInDir(pSubDir, apPath + partLen + 1, apRetEntryPtr);
827 if (!err)
828 return 0;
829 pSubDir = pSubDir->mpNextSib;
830 } while (pSubDir);
831 }
832
833 return ERRCODE_ZIPFILE_PATHNOTFOUND; /* path not found */
834 }
ZIPFILE_Find(ZIPPED_DIR * apDir,char const * apFileName,ZIPPED_FILE ** apRetFilePtr)835 int ZIPFILE_Find(ZIPPED_DIR *apDir, char const *apFileName, ZIPPED_FILE **apRetFilePtr)
836 {
837 if ((!apDir) || (!apFileName) || (!apRetFilePtr))
838 return ERRCODE_ZIPFILE_ARG;
839 while (*apFileName=='/')
840 apFileName++;
841 return sFindInDir(apDir, apFileName, apRetFilePtr);
842 }
843
844