1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) 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