Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#include "PakInterface.h"
#include <windows.h>
#include <direct.h>

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;

enum
{
        FILEFLAGS_END = 0x80
};

PakInterface* gPakInterface = new PakInterface();

static std::string StringToUpper(const std::string& theString)
{
        std::string aString;

        for (unsigned i = 0; i < theString.length(); i++)
                aString += toupper(theString[i]);

        return aString;
}

PakInterface::PakInterface()
{
        if (GetPakPtr() == NULL)
                *gPakInterfaceP = this;
}

PakInterface::~PakInterface()
{
}

bool PakInterface::AddPakFile(const std::string& theFileName)
{
        HANDLE aFileHandle = CreateFile(theFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

        if (aFileHandle == INVALID_HANDLE_VALUE)
                return false;

        int aFileSize = GetFileSize(aFileHandle, 0);

        HANDLE aFileMapping = CreateFileMapping(aFileHandle, NULL, PAGE_READONLY, 0, aFileSize, NULL);
        if (aFileMapping == NULL)
        {
                CloseHandle(aFileHandle);
                return false;
        }

        void* aPtr = MapViewOfFile(aFileMapping, FILE_MAP_READ, 0, 0, aFileSize);
        if (aPtr == NULL)
        {
                CloseHandle(aFileMapping);
                CloseHandle(aFileHandle);
                return false;
        }

        mPakCollectionList.push_back(PakCollection());
        PakCollection* aPakCollection = &mPakCollectionList.back();

        aPakCollection->mFileHandle = aFileHandle;
        aPakCollection->mMappingHandle = aFileMapping;
        aPakCollection->mDataPtr = aPtr;
       
        PakRecordMap::iterator aRecordItr = mPakRecordMap.insert(PakRecordMap::value_type(StringToUpper(theFileName), PakRecord())).first;
        PakRecord* aPakRecord = &(aRecordItr->second);
        aPakRecord->mCollection = aPakCollection;
        aPakRecord->mFileName = theFileName;
        aPakRecord->mStartPos = 0;
        aPakRecord->mSize = aFileSize;
       
        PFILE* aFP = FOpen(theFileName.c_str(), "rb");
        if (aFP == NULL)
                return false;

        ulong aMagic = 0;
        FRead(&aMagic, sizeof(ulong), 1, aFP);
        if (aMagic != 0xBAC04AC0)
        {
                FClose(aFP);
                return false;
        }

        ulong aVersion = 0;
        FRead(&aVersion, sizeof(ulong), 1, aFP);
        if (aVersion > 0)
        {
                FClose(aFP);
                return false;
        }

        int aPos = 0;

        for (;;)
        {
                uchar aFlags = 0;
                int aCount = FRead(&aFlags, 1, 1, aFP);
                if ((aFlags & FILEFLAGS_END) || (aCount == 0))
                        break;

                uchar aNameWidth = 0;
                char aName[256];
                FRead(&aNameWidth, 1, 1, aFP);
                FRead(aName, 1, aNameWidth, aFP);
                aName[aNameWidth] = 0;

                int aSrcSize = 0;
                FRead(&aSrcSize, sizeof(int), 1, aFP);
                FILETIME aFileTime;
                FRead(&aFileTime, sizeof(FILETIME), 1, aFP);

                PakRecordMap::iterator aRecordItr = mPakRecordMap.insert(PakRecordMap::value_type(StringToUpper(aName), PakRecord())).first;
                PakRecord* aPakRecord = &(aRecordItr->second);
                aPakRecord->mCollection = aPakCollection;
                aPakRecord->mFileName = aName;
                aPakRecord->mStartPos = aPos;
                aPakRecord->mSize = aSrcSize;
                aPakRecord->mFileTime = aFileTime;

                aPos += aSrcSize;
        }

        int anOffset = FTell(aFP);

        // Now fix file starts
        aRecordItr = mPakRecordMap.begin();
        while (aRecordItr != mPakRecordMap.end())
        {
                PakRecord* aPakRecord = &(aRecordItr->second);
                if (aPakRecord->mCollection == aPakCollection)
                        aPakRecord->mStartPos += anOffset;
                ++aRecordItr;
        }

        FClose(aFP);

        return true;
}

static void FixFileName(const char* theFileName, char* theUpperName)
{
        if ((theFileName[0] != 0) && (theFileName[1] == ':'))
        {
                char aDir[256];
                getcwd(aDir, 256);
                int aLen = strlen(aDir);
                aDir[aLen++] = '\\';
                aDir[aLen] = 0;

                if (strnicmp(aDir, theFileName, aLen) == 0)
                        theFileName += aLen;
        }

        bool lastSlash = false;
        const char* aSrc = theFileName;
        char* aDest = theUpperName;

        for (;;)
        {
                char c = *(aSrc++);

                if ((c == '\\') || (c == '/'))
                {
                        if (!lastSlash)
                                *(aDest++) = '\\';
                        lastSlash = true;
                }
                else if ((c == '.') && (lastSlash) && (*aSrc == '.'))
                {
                        // We have a '/..' on our hands
                        aDest--;
                        while ((aDest > theUpperName + 1) && (*(aDest-1) != '\\'))
                                --aDest;
                        aSrc++;
                }
                else
                {
                        *(aDest++) = toupper((uchar) c);
                        if (c == 0)
                                break;
                        lastSlash = false;                             
                }
        }
}

PFILE* PakInterface::FOpen(const char* theFileName, const char* anAccess)
{
        if ((stricmp(anAccess, "r") == 0) || (stricmp(anAccess, "rb") == 0) || (stricmp(anAccess, "rt") == 0))
        {
                char anUpperName[256];
                FixFileName(theFileName, anUpperName);
               
                PakRecordMap::iterator anItr = mPakRecordMap.find(anUpperName);
                if (anItr != mPakRecordMap.end())
                {                      
                        PFILE* aPFP = new PFILE;
                        aPFP->mRecord = &anItr->second;
                        aPFP->mPos = 0;
                        aPFP->mFP = NULL;
                        return aPFP;
                }
        }

        FILE* aFP = fopen(theFileName, anAccess);
        if (aFP == NULL)
                return NULL;
        PFILE* aPFP = new PFILE;
        aPFP->mRecord = NULL;
        aPFP->mPos = 0;
        aPFP->mFP = aFP;
        return aPFP;
}

int PakInterface::FClose(PFILE* theFile)
{
        if (theFile->mRecord == NULL)
                fclose(theFile->mFP);
        delete theFile;
        return 0;
}

int PakInterface::FSeek(PFILE* theFile, long theOffset, int theOrigin)
{
        if (theFile->mRecord != NULL)
        {
                if (theOrigin == SEEK_SET)
                        theFile->mPos = theOffset;
                else if (theOrigin == SEEK_END)
                        theFile->mPos = theFile->mRecord->mSize - theOffset;
                else if (theOrigin == SEEK_CUR)
                        theFile->mPos += theOffset;

                theFile->mPos = max(min(theFile->mPos, theFile->mRecord->mSize), 0);
                return 0;
        }
        else
                return fseek(theFile->mFP, theOffset, theOrigin);
}

int PakInterface::FTell(PFILE* theFile)
{
        if (theFile->mRecord != NULL)
                return theFile->mPos;
        else
                return ftell(theFile->mFP);    
}

size_t PakInterface::FRead(void* thePtr, int theElemSize, int theCount, PFILE* theFile)
{
        if (theFile->mRecord != NULL)
        {
                int aSizeBytes = min(theElemSize*theCount, theFile->mRecord->mSize - theFile->mPos);

                uchar* src = (uchar*) theFile->mRecord->mCollection->mDataPtr + theFile->mRecord->mStartPos + theFile->mPos;
                uchar* dest = (uchar*) thePtr;         
                for (int i = 0; i < aSizeBytes; i++)
                        *(dest++) = (*src++) ^ 0xF7; // 'Decrypt'
                theFile->mPos += aSizeBytes;
                return aSizeBytes / theElemSize;
        }
       
        return fread(thePtr, theElemSize, theCount, theFile->mFP);     
}

int PakInterface::FGetC(PFILE* theFile)
{
        if (theFile->mRecord != NULL)
        {
                for (;;)
                {
                        if (theFile->mPos >= theFile->mRecord->mSize)
                                return EOF;            
                        char aChar = *((char*) theFile->mRecord->mCollection->mDataPtr + theFile->mRecord->mStartPos + theFile->mPos++) ^ 0xF7;
                        if (aChar != '\r')
                                return (uchar) aChar;
                }
        }

        return fgetc(theFile->mFP);
}

int PakInterface::UnGetC(int theChar, PFILE* theFile)
{
        if (theFile->mRecord != NULL)
        {
                // This won't work if we're not pushing the same chars back in the stream
                theFile->mPos = max(theFile->mPos - 1, 0);
                return theChar;
        }

        return ungetc(theChar, theFile->mFP);
}

char* PakInterface::FGetS(char* thePtr, int theSize, PFILE* theFile)
{
        if (theFile->mRecord != NULL)
        {
                int anIdx = 0;
                while (anIdx < theSize)
                {
                        if (theFile->mPos >= theFile->mRecord->mSize)
                        {
                                if (anIdx == 0)
                                        return NULL;
                                break;
                        }
                        char aChar = *((char*) theFile->mRecord->mCollection->mDataPtr + theFile->mRecord->mStartPos + theFile->mPos++) ^ 0xF7;
                        if (aChar != '\r')
                                thePtr[anIdx++] = aChar;
                        if (aChar == '\n')
                                break;
                }
                thePtr[anIdx] = 0;
                return thePtr;
        }

        return fgets(thePtr, theSize, theFile->mFP);
}

int PakInterface::FEof(PFILE* theFile)
{
        if (theFile->mRecord != NULL)
                return theFile->mPos >= theFile->mRecord->mSize;
        else
                return feof(theFile->mFP);
}

bool PakInterface::PFindNext(PFindData* theFindData, LPWIN32_FIND_DATA lpFindFileData)
{
        PakRecordMap::iterator anItr;
        if (theFindData->mLastFind.size() == 0)
                anItr = mPakRecordMap.begin();
        else
        {
                anItr = mPakRecordMap.find(theFindData->mLastFind);
                if (anItr != mPakRecordMap.end())
                        anItr++;
        }

        while (anItr != mPakRecordMap.end())
        {
                const char* aFileName = anItr->first.c_str();
                PakRecord* aPakRecord = &anItr->second;

                int aStarPos = (int) theFindData->mFindCriteria.find('*');
                if (aStarPos != -1)
                {
                        if (strncmp(theFindData->mFindCriteria.c_str(), aFileName, aStarPos) == 0)
                        {                              
                                // First part matches
                                const char* anEndData = theFindData->mFindCriteria.c_str() + aStarPos + 1;
                                if ((*anEndData == 0) || (strcmp(anEndData, ".*") == 0) ||                                                             
                                        (strcmp(theFindData->mFindCriteria.c_str() + aStarPos + 1,
                                        aFileName + strlen(aFileName) - (theFindData->mFindCriteria.length() - aStarPos) + 1) == 0))
                                {
                                        // Matches before and after star
                                        memset(lpFindFileData, 0, sizeof(lpFindFileData));
                                       
                                        int aLastSlashPos = (int) anItr->second.mFileName.rfind('\\');
                                        if (aLastSlashPos == -1)
                                                strcpy(lpFindFileData->cFileName, anItr->second.mFileName.c_str());
                                        else
                                                strcpy(lpFindFileData->cFileName, anItr->second.mFileName.c_str() + aLastSlashPos + 1);

                                        const char* aEndStr = aFileName + strlen(aFileName) - (theFindData->mFindCriteria.length() - aStarPos) + 1;
                                        if (strchr(aEndStr, '\\') != NULL)
                                                lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;

                                        lpFindFileData->nFileSizeLow = aPakRecord->mSize;
                                        lpFindFileData->ftCreationTime = aPakRecord->mFileTime;
                                        lpFindFileData->ftLastWriteTime = aPakRecord->mFileTime;
                                        lpFindFileData->ftLastAccessTime = aPakRecord->mFileTime;
                                        theFindData->mLastFind = aFileName;

                                        return true;
                                }
                        }
                }

                ++anItr;
        }

        return false;
}

HANDLE PakInterface::FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData)
{
        PFindData* aFindData = new PFindData;

        char anUpperName[256];
        FixFileName(lpFileName, anUpperName);
        aFindData->mFindCriteria = anUpperName;
        aFindData->mWHandle = INVALID_HANDLE_VALUE;

        if (PFindNext(aFindData, lpFindFileData))
                return (HANDLE) aFindData;

        aFindData->mWHandle = ::FindFirstFile(aFindData->mFindCriteria.c_str(), lpFindFileData);
        if (aFindData->mWHandle != INVALID_HANDLE_VALUE)
                return (HANDLE) aFindData;

        delete aFindData;
        return INVALID_HANDLE_VALUE;
}

BOOL PakInterface::FindNextFile(HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData)
{
        PFindData* aFindData = (PFindData*) hFindFile;

        if (aFindData->mWHandle == INVALID_HANDLE_VALUE)
        {
                if (PFindNext(aFindData, lpFindFileData))
                        return TRUE;

                aFindData->mWHandle = ::FindFirstFile(aFindData->mFindCriteria.c_str(), lpFindFileData);
                return (aFindData->mWHandle != INVALID_HANDLE_VALUE);                  
        }
       
        return ::FindNextFile(aFindData->mWHandle, lpFindFileData);
}

BOOL PakInterface::FindClose(HANDLE hFindFile)
{
        PFindData* aFindData = (PFindData*) hFindFile;

        if (aFindData->mWHandle != INVALID_HANDLE_VALUE)
                ::FindClose(aFindData->mWHandle);

        delete aFindData;
        return TRUE;
}