/*---------------------------------------------------------------------------* Copyright (C) 2009-2013 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ #include #include #include "7z.h" #include "7zCrc.h" static int sgCrcInit = 0; /* --------------------------------------------------------------------------------- */ struct _MemStream { ILookInStream iFace; /* must be first thing in structure */ UINT8 const * mpSrcData; UINT32 mSrcDataBytes; UINT32 mCurPos; }; typedef struct _MemStream MemStream; static SRes memStreamLook(void *p, const void **buf, size_t *size) { UINT32 left; SRes ret; if ((!(*size)) || (!buf)) return SZ_ERROR_PARAM; left = ((MemStream *)p)->mSrcDataBytes - ((MemStream *)p)->mCurPos; if (left < (*size)) { ret = SZ_ERROR_INPUT_EOF; *size = left; } else ret = 0; *buf = ((MemStream *)p)->mpSrcData + ((MemStream *)p)->mCurPos; return ret; } static SRes memStreamSkip(void *p, size_t offset) { UINT32 left; SRes ret; if (!offset) return SZ_ERROR_PARAM; left = ((MemStream *)p)->mSrcDataBytes - ((MemStream *)p)->mCurPos; if (!left) return SZ_ERROR_INPUT_EOF; if (left < offset) { offset = left; ret = SZ_ERROR_INPUT_EOF; } else ret = 0; ((MemStream *)p)->mCurPos += offset; return ret; } static SRes memStreamRead(void *p, void *buf, size_t *size) { UINT32 left; UINT32 act; SRes ret; act = *size; if (!act) return SZ_ERROR_PARAM; left = ((MemStream *)p)->mSrcDataBytes - ((MemStream *)p)->mCurPos; if (!left) return SZ_ERROR_INPUT_EOF; if (left < act) { act = left; ret = SZ_ERROR_INPUT_EOF; *size = left; } else ret = 0; memcpy(buf, ((MemStream *)p)->mpSrcData + ((MemStream *)p)->mCurPos, act); ((MemStream *)p)->mCurPos += act; return ret; } static SRes memStreamSeek(void *p, Int64 *pos, ESzSeek origin) { UINT32 pos32; if (origin != SZ_SEEK_CUR) { /* trying to move file pointer */ if (origin == SZ_SEEK_END) ((MemStream *)p)->mCurPos = ((MemStream *)p)->mSrcDataBytes; else if (origin == SZ_SEEK_SET) { if ((*pos)&0xFFFFFFFF80000000ull) return SZ_ERROR_PARAM; pos32 = (UINT32)((*pos)&0x7FFFFFFF); if (pos32 > ((MemStream *)p)->mSrcDataBytes) return SZ_ERROR_PARAM; ((MemStream *)p)->mCurPos = pos32; } else return SZ_ERROR_PARAM; } *pos = (Int64)((MemStream *)p)->mCurPos; return 0; } static void Init_MemStream(MemStream *apStream, UINT8 const *apData, UINT32 aDataBytes) { apStream->iFace.Look = memStreamLook; apStream->iFace.Read = memStreamRead; apStream->iFace.Seek = memStreamSeek; apStream->iFace.Skip = memStreamSkip; apStream->mpSrcData = apData; apStream->mSrcDataBytes = aDataBytes; apStream->mCurPos = 0; } /* --------------------------------------------------------------------------------- */ #ifdef WIN32 #define STRNICMP _strnicmp #endif #ifdef EPPC #define STRNICMP _mystrnicmp /* cafe doesn't have this function so it is provided here */ static int _mystrnicmp(char const *apStr1, char const *apStr2, size_t len) { int c1; int c2; if (!len) return 0; do { c1 = (int)*apStr1; if ((c1 >='a') && (c1 <='z')) c1 -= ('a'-'A'); c2 = (int)*apStr2; if ((c2 >='a') && (c2 <='z')) c2 -= ('a'-'A'); if ((!c1) && (!c2)) return 0; if (!c1) return -1; if (!c2) return 1; c1 -= c2; if (c1) return c1; apStr1++; apStr2++; } while (--len); return 0; } #endif static int sCompStrLens(char const *pStr1, UINT32 strLen1, char const *pStr2, UINT32 strLen2) { int c,v; c = ((int)strLen2) - ((int)strLen1); if (c > 0) { /* str2 longer than str1 */ c = STRNICMP(pStr1, pStr2, strLen1); if (!c) return -1; return c; } v = STRNICMP(pStr1, pStr2, strLen2); if (c < 0) { /* str2 shorter than str1 name */ if (!v) return 1; } return v; } static int sComparePaths(char const *pPath1, UINT32 path1Len, char const *pPath2, UINT32 path2Len) { char const *pSlash1; char const *pSlash2; UINT32 left; int c; pSlash1 = pPath1; left = path1Len; do { if (*pSlash1 == '/') break; pSlash1++; } while (--left); if (!left) pSlash1 = NULL; pSlash2 = pPath2; left = path2Len; do { if (*pSlash2 == '/') break; pSlash2++; } while (--left); if (!left) pSlash2 = NULL; if ((!pSlash1) && (!pSlash2)) return sCompStrLens(pPath1,path1Len,pPath2,path2Len); if (pSlash1 && (!pSlash2)) return 1; if ((!pSlash1) && (pSlash2)) return -1; /* both have slashes */ c = sCompStrLens(pPath1, (UINT32)(pSlash1-pPath1), pPath2, (UINT32)(pSlash2-pPath2)); if (c) return c; /* both have slashes and are the same up to the first slash */ left = (UINT32)(pSlash1 - pPath1); /* both have slashes and start with the same stuff */ return sComparePaths(pPath1+left+1, path1Len - (left+1), pPath2+left+1, path2Len - (left+1)); } static int sCompare(void const *p1, void const *p2) { SZFILE_ENTRY const * pFile1; SZFILE_ENTRY const * pFile2; pFile1 = (SZFILE_ENTRY const *)p1; pFile2 = (SZFILE_ENTRY const *)p2; if ((!pFile1->mUncompBytes) && (pFile2->mUncompBytes)) return -1; if ((pFile1->mUncompBytes) && (!pFile2->mUncompBytes)) return 1; return sComparePaths(pFile1->mpNameOnly, pFile1->mNameOnlyLen, pFile2->mpNameOnly, pFile2->mNameOnlyLen); } /* --------------------------------------------------------------------------------- */ struct _SZFILE_IMP { ISzAlloc mAllocator; MemStream mMemStream; SZFILE_ARC * mpArc; CSzArEx mDB; char * mpNames; SZFILE_ENTRY * mpFiles; }; typedef struct _SZFILE_IMP SZFILE_IMP; static void * sAlloc(void *p, size_t bytes) { if (!bytes) return NULL; return ((SZFILE_IMP *)p)->mpArc->mfAlloc(bytes); } static void sFree(void *p, void *address) { if (!address) return; ((SZFILE_IMP *)p)->mpArc->mfFree(address); } /* --------------------------------------------------------------------------------- */ static UINT32 sCountDirsNeeded(SZFILE_ENTRY *apFiles, UINT32 aNumFiles) { char const * pLook; char const * pComp; UINT32 left; SZFILE_ENTRY * pRest; UINT32 ret; UINT32 subLen; UINT32 numSub; /* don't need dir for files without a slash at beginning of sorted list */ do { pLook = apFiles->mpNameOnly; left = apFiles->mNameOnlyLen; do { if (*pLook=='/') break; pLook++; } while (--left); if (left) break; apFiles++; } while (--aNumFiles); if (!aNumFiles) return 0; /* no subdirs at this point */ pLook++; subLen = (UINT32)(pLook-apFiles->mpNameOnly); if (aNumFiles==1) { apFiles->mpNameOnly += subLen; apFiles->mNameOnlyLen -= subLen; return 1 + sCountDirsNeeded(apFiles, 1); } ret = 1; pComp = apFiles->mpNameOnly; apFiles->mpNameOnly += subLen; apFiles->mNameOnlyLen -= subLen; pRest = apFiles+1; aNumFiles--; numSub = 1; do { if ((pRest->mNameOnlyLen < subLen) || (STRNICMP(pRest->mpNameOnly, pComp, subLen))) break; pRest->mpNameOnly += subLen; pRest->mNameOnlyLen -= subLen; numSub++; pRest++; } while (--aNumFiles); ret += sCountDirsNeeded(apFiles, numSub); if (aNumFiles) ret += sCountDirsNeeded(pRest, aNumFiles); return ret; } static void sDirify(SZFILE_DIR *pThisDir, SZFILE_ENTRY *apFiles, UINT32 aNumFiles, SZFILE_DIR **ppFreeDir) { char const * pLook; char const * pComp; UINT32 left; SZFILE_ENTRY * pRest; UINT32 subLen; UINT32 numSub; SZFILE_DIR * pNewDir; SZFILE_DIR * pPrev; /* don't need dir for files without a slash at beginning of sorted list */ pRest = apFiles; subLen = 0; numSub = aNumFiles; do { pLook = pRest->mpNameOnly; left = pRest->mNameOnlyLen; do { if (*pLook=='/') break; pLook++; } while (--left); if (left) break; subLen++; pRest->mpParentDir = pThisDir; pRest++; } while (--numSub); if (subLen) { pThisDir->mpFilesArray = apFiles; pThisDir->mNumFiles = subLen; apFiles = pRest; aNumFiles -= subLen; if (!numSub) return; } else { pThisDir->mpFilesArray = NULL; pThisDir->mNumFiles = 0; } /* subdirs found */ pNewDir = *ppFreeDir; (*ppFreeDir)++; subLen = (UINT32)(pLook-apFiles->mpNameOnly); pNewDir->mpArc = pThisDir->mpArc; pNewDir->mpParentDir = pThisDir; pNewDir->mpNameOnly = apFiles->mpNameOnly; pNewDir->mNameOnlyLen = subLen; pNewDir->mpNextSib = NULL; if (!pThisDir->mpSubDirList) pThisDir->mpSubDirList = pNewDir; else { pPrev = pThisDir->mpSubDirList; while (pPrev->mpNextSib) pPrev = pPrev->mpNextSib; pPrev->mpNextSib = pNewDir; } pLook++; subLen++; if (aNumFiles==1) { apFiles->mpNameOnly += subLen; apFiles->mNameOnlyLen -= subLen; sDirify(pNewDir, apFiles, 1, ppFreeDir); return; } pComp = apFiles->mpNameOnly; apFiles->mpNameOnly += subLen; apFiles->mNameOnlyLen -= subLen; pRest = apFiles+1; aNumFiles--; numSub = 1; do { if ((pRest->mNameOnlyLen < subLen) || (STRNICMP(pRest->mpNameOnly, pComp, subLen))) break; pRest->mpNameOnly += subLen; pRest->mNameOnlyLen -= subLen; numSub++; pRest++; } while (--aNumFiles); sDirify(pNewDir, apFiles, numSub, ppFreeDir); if (aNumFiles) sDirify(pThisDir, pRest, aNumFiles, ppFreeDir); } int SZFILE_CreateArc(SZFILE_ARC *apArc, UINT8 const *apFileData, UINT32 aFileBytes, SZFILE_pf_Alloc afAlloc, SZFILE_pf_Free afFree) { SRes res; SZFILE_IMP * pImp; UINT32 fileNameSpace; UINT16 const * pUnic; char * pAscii; SZFILE_ENTRY * pEntry; UINT32 ix, ixe; UINT32 jx; UINT32 nzFileCount; SZFILE_ENTRY * pnzFiles; SZFILE_DIR * pDirs; UINT32 ixFolder; UINT64 folderTotalUnpackSize; UINT32 folderUnpackedOffset; if ((!apArc) || (!apFileData) || (!aFileBytes) || (!afAlloc) || (!afFree)) return ERRCODE_SZFILE_BADARG; // bad argument pImp = (SZFILE_IMP *)afAlloc(sizeof(SZFILE_IMP)); if (!pImp) return ERRCODE_SZFILE_OUTOFMEMORY; // out of memory memset(pImp, 0, sizeof(SZFILE_IMP)); pImp->mpArc = apArc; apArc->mpImp = pImp; apArc->mfAlloc = afAlloc; apArc->mfFree = afFree; apArc->mpSrcFile = apFileData; apArc->mSrcFileBytes = aFileBytes; if (!sgCrcInit) { CrcGenerateTable(); sgCrcInit = 1; } SzArEx_Init(&pImp->mDB); pImp->mAllocator.Alloc = sAlloc; pImp->mAllocator.Free = sFree; Init_MemStream(&pImp->mMemStream, apFileData, aFileBytes); res = SzArEx_Open(&pImp->mDB, &pImp->mMemStream.iFace, &pImp->mAllocator, &pImp->mAllocator); if (res != SZ_OK) { afFree(pImp); return res; } apArc->mNumStreams = pImp->mDB.db.NumPackStreams; apArc->mppUnpackedStreams = (UINT8 **)afAlloc(sizeof(UINT8 *)*apArc->mNumStreams); if (!apArc->mppUnpackedStreams) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_OUTOFMEMORY; // out of memory } memset(apArc->mppUnpackedStreams, 0, sizeof(UINT8 *) * apArc->mNumStreams); pUnic = ((UINT16 const*)pImp->mDB.FileNames.data) + pImp->mDB.FileNameOffsets[pImp->mDB.db.NumFiles-1]; while (*pUnic) pUnic++; pUnic++; fileNameSpace = (UINT32)(pUnic - (UINT16 const *)pImp->mDB.FileNames.data); pImp->mpNames = (char *)afAlloc(fileNameSpace); if (!pImp->mpNames) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_OUTOFMEMORY; // out of memory } pAscii = pImp->mpNames; pUnic = (UINT16 const *)pImp->mDB.FileNames.data; do { #ifdef WIN32 *pAscii = (UINT8)(*pUnic); #endif #ifdef EPPC *pAscii = (UINT8)((*pUnic)>>8); #endif pAscii++; pUnic++; } while (--fileNameSpace); pImp->mpFiles = (SZFILE_ENTRY *)afAlloc(sizeof(SZFILE_ENTRY) * pImp->mDB.db.NumFiles); if (!pImp->mpFiles) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_OUTOFMEMORY; // out of memory } memset(pImp->mpFiles, 0, sizeof(SZFILE_ENTRY) * pImp->mDB.db.NumFiles); pEntry = pImp->mpFiles; ixFolder = 0; ix = pImp->mDB.FolderStartFileIndex[ixFolder]; if (ixFolder==pImp->mDB.db.NumFolders-1) ixe = pImp->mDB.db.NumFiles; else ixe = pImp->mDB.FolderStartFileIndex[ixFolder+1]; do { folderUnpackedOffset = 0; folderTotalUnpackSize = 0; for(jx=0;jxmDB.db.Folders[ixFolder].NumPackStreams;jx++) folderTotalUnpackSize += pImp->mDB.db.Folders[ixFolder].UnpackSizes[jx]; while (ix < ixe) { pEntry->mArcIndex = ix; pEntry->mpNameOnly = pImp->mpNames + pImp->mDB.FileNameOffsets[ix]; pEntry->mNameOnlyLen = strlen(pEntry->mpNameOnly); pEntry->mStreamIx = ixFolder; pEntry->mStreamUnpackedOffset = folderUnpackedOffset; pEntry->mUncompBytes = (UINT32)pImp->mDB.db.Files[ix].Size; folderUnpackedOffset += pEntry->mUncompBytes; pEntry++; ix++; } if (folderUnpackedOffset != folderTotalUnpackSize) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_CORRUPT; } ixFolder++; if (ixFolder == pImp->mDB.db.NumFolders) break; ixe = pImp->mDB.FolderStartFileIndex[ixFolder+1]; } while (1); qsort(pImp->mpFiles, pImp->mDB.db.NumFiles, sizeof(SZFILE_ENTRY), sCompare); /* ignore empty files at the start of the sorted list */ nzFileCount = pImp->mDB.db.NumFiles; pnzFiles = pImp->mpFiles; while (!pnzFiles->mUncompBytes) { nzFileCount--; pnzFiles++; if (!nzFileCount) break; } if (!nzFileCount) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_EMPTY; // empty archive } jx = 1 + sCountDirsNeeded(pnzFiles,nzFileCount); /* must reset name pointers and sizes so dir construction can follow same as what counting did */ pEntry = pnzFiles; for(ix=0;ixmpNameOnly = pImp->mpNames + pImp->mDB.FileNameOffsets[pEntry->mArcIndex]; pEntry->mNameOnlyLen = strlen(pEntry->mpNameOnly); pEntry++; } apArc->mpRootDir = (SZFILE_DIR *)afAlloc(sizeof(SZFILE_DIR) * jx); if (!apArc->mpRootDir) { SZFILE_PurgeArc(apArc); // out of memory return ERRCODE_SZFILE_OUTOFMEMORY; } memset(apArc->mpRootDir, 0, sizeof(SZFILE_DIR) * jx); pDirs = apArc->mpRootDir; pDirs->mNameOnlyLen = 0; pDirs->mpArc = apArc; pDirs->mpNameOnly = ""; pDirs++; sDirify(apArc->mpRootDir, pnzFiles, nzFileCount, &pDirs); if (pDirs != (apArc->mpRootDir+jx)) { SZFILE_PurgeArc(apArc); return ERRCODE_SZFILE_INTERNAL; // internal error } return 0; } static int sFindInDir(SZFILE_DIR *apDir, char const *apPath, SZFILE_ENTRY **apRetEntryPtr) { char const * pSlash; char c; int partLen; int err; UINT32 ix; SZFILE_ENTRY * pEntry; SZFILE_DIR * pSubDir; *apRetEntryPtr = NULL; if (!(*apPath)) return ERRCODE_SZFILE_BADARG; // argument error pSlash = apPath; do { c = *pSlash; if ((!c) || (c=='/')) break; pSlash++; } while (1); partLen = (int)(pSlash - apPath); if (!c) { pEntry = apDir->mpFilesArray; for(ix=0;ixmNumFiles;ix++) { if (pEntry->mNameOnlyLen == partLen) { if (!STRNICMP(apPath, apDir->mpFilesArray[ix].mpNameOnly, partLen)) break; } pEntry++; } if (ix != apDir->mNumFiles) { *apRetEntryPtr = pEntry; return 0; } return ERRCODE_SZFILE_FILENOTFOUND; // file not found } /* there is a slash, so this part is a subdirectory */ pSubDir = apDir->mpSubDirList; if (pSubDir) { do { err = sFindInDir(pSubDir, apPath + partLen + 1, apRetEntryPtr); if (!err) return 0; pSubDir = pSubDir->mpNextSib; } while (pSubDir); } return ERRCODE_SZFILE_PATHNOTFOUND; /* path not found */ } int SZFILE_FindInArc(SZFILE_ARC *apArc, char const *apFileName, SZFILE_ENTRY **apRetEntryPtr) { if ((!apArc) || (!apFileName) || (!apRetEntryPtr)) return ERRCODE_SZFILE_BADARG; while (*apFileName=='/') apFileName++; return sFindInDir(apArc->mpRootDir, apFileName, apRetEntryPtr); } int SZFILE_UnpackStream(SZFILE_ARC *apArc, UINT32 aStreamIx) { SRes res; SZFILE_IMP * pImp; UINT32 decBytes; if ((!apArc) || (aStreamIx >= apArc->mNumStreams)) return ERRCODE_SZFILE_BADARG; if (apArc->mppUnpackedStreams[aStreamIx]) return 0; pImp = (SZFILE_IMP *)apArc->mpImp; decBytes = 0; res = SzArEx_ExtractFolder(&pImp->mDB, &pImp->mMemStream.iFace, aStreamIx, &apArc->mppUnpackedStreams[aStreamIx], &decBytes, &pImp->mAllocator, &pImp->mAllocator); if (res != SZ_OK) return res; return 0; } int SZFILE_GetUnpackedFile(SZFILE_ENTRY *apEntry, UINT8 **apRetDataPtr, UINT32 *apRetDataBytes) { SZFILE_ARC *pArc; int err; if (apRetDataPtr) *apRetDataPtr = NULL; if (apRetDataBytes) *apRetDataBytes = 0; if ((!apEntry) || (!apRetDataPtr) || (!apRetDataBytes)) return ERRCODE_SZFILE_BADARG; pArc = apEntry->mpParentDir->mpArc; if (!pArc->mppUnpackedStreams[apEntry->mStreamIx]) { err = SZFILE_UnpackStream(pArc,apEntry->mStreamIx); if (err) return err; } *apRetDataPtr = pArc->mppUnpackedStreams[apEntry->mStreamIx] + apEntry->mStreamUnpackedOffset; *apRetDataBytes = apEntry->mUncompBytes; return 0; } UINT8 * SZFILE_Get(SZFILE_ARC *apArc, char const *apFileName, UINT32 *apRetFileBytes) { UINT8 * pRet; SZFILE_ENTRY * pEntry; int err; if ((!apArc) || (!apFileName) || (!apRetFileBytes)) return NULL; err = SZFILE_FindInArc(apArc, apFileName, &pEntry); if (err) return NULL; err = SZFILE_GetUnpackedFile(pEntry, &pRet, apRetFileBytes); if (err) return NULL; return pRet; } void SZFILE_PurgeArc(SZFILE_ARC *apArc) { SZFILE_IMP * pImp; UINT32 ix; if (!apArc) return; pImp = (SZFILE_IMP *)apArc->mpImp; if (pImp) { if (apArc->mpRootDir) { apArc->mfFree(apArc->mpRootDir); apArc->mpRootDir = NULL; } if (pImp->mpFiles) { apArc->mfFree(pImp->mpFiles); pImp->mpFiles = NULL; } if (pImp->mpNames) { apArc->mfFree(pImp->mpNames); pImp->mpNames = NULL; } if (apArc->mppUnpackedStreams) { for(ix=0;ixmNumStreams;ix++) { if (apArc->mppUnpackedStreams[ix]) pImp->mAllocator.Free(pImp, apArc->mppUnpackedStreams[ix]); } apArc->mfFree(apArc->mppUnpackedStreams); apArc->mppUnpackedStreams = NULL; } SzArEx_Free(&pImp->mDB, &pImp->mAllocator); apArc->mfFree(pImp); apArc->mpImp = NULL; } apArc->mfAlloc = NULL; apArc->mfFree = NULL; apArc->mNumStreams = 0; apArc->mpSrcFile = NULL; apArc->mSrcFileBytes = 0; }