Subversion Repositories spk

Rev

Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "CatFile.h"
#include <time.h>
#include "File.h"
#include "DirIO.h"

CCatFile::CCatFile ()
{
        m_iDataType = CATFILE_NONE;
        m_sData = NULL;
        m_lSize = 0;
        m_bCreate = false;
}

CCatFile::~CCatFile ()
{
        for ( SInCatFile *c = m_lFiles.First(); c; c = m_lFiles.Next() )
        {
                if ( c->sData )
                        delete c->sData;
                delete c;
        }
        m_lFiles.clear();
}

bool CCatFile::IsAddonDir(CyString dir)
{
        CyString d = dir;
        d = d.FindReplace("\\", "/");
        d = d.GetToken("/", 1, 1);

        if ( d.Compare("types") )
                return true;
        if ( d.Compare("t") )
                return true;
        if ( d.Compare("maps") )
                return true;
        if ( d.Compare("director") )
                return true;
        if ( d.Compare("cutscenes") )
                return true;
        return false;
}

bool CCatFile::Opened(int error, bool allowCreate) 
{
        if ( error == CATERR_NONE ) return true;
        if ( allowCreate && error == CATERR_CREATED ) return true;
        return false;
}
int CCatFile::Open ( CyString catfile, CyString addon, int readtype, bool create )
{
        m_bCreate = false;

        CyString datfile;
        if ( !catfile.Right(4).Compare(".cat") )
        {
                datfile = catfile + ".dat";
                catfile += ".cat";
        }
        else
                datfile = catfile.Left ( -4 ) + ".dat";

        if ( readtype == CATREAD_JUSTCONTENTS )
                create = false;

        // first check if the dat file exists and opens
        bool created = false;
        if ( readtype != CATREAD_JUSTCONTENTS )
        {
                FILE *id = fopen ( datfile.c_str(), "rb+" );
                if ( !id )
                {
                        if ( create )
                                created = true;
                        else
                                return CATERR_NODATFILE;
                }

                if ( !created && id )
                        fclose ( id );
        }

        // now open the cat file to read
        FILE *cid = fopen ( catfile.c_str(), "rb" );
        if ( !cid )
        {
                if ( create )
                        created = true;
                else
                        return CATERR_NOCATFILE;
        }

        if ( created )
        {
                m_fCatFile.Open ( catfile );
                m_fDatFile.Open ( datfile );
                m_bCreate = true;
                return CATERR_CREATED;
        }

        // find the file size
        fseek ( cid, 0, SEEK_END );
        size_t lFileSize = ftell ( cid );
        fseek ( cid, 0, SEEK_SET );

        if ( !lFileSize )
        {
                fclose ( cid );
                return CATERR_FILEEMPTY;
        }

        // size must be multiples of 5
        size_t size = lFileSize + ((lFileSize % 5) ? 5 - (lFileSize % 5) : 0);

        // read cat to buffer
        m_sData = new unsigned char[size + 1];
        m_sData[lFileSize] = 0;
        m_lSize = size;
        fread ( m_sData, sizeof(unsigned char), m_lSize, cid );
        if ( ferror(cid)  )
        {
                RemoveData ();
                fclose ( cid );
                return CATERR_READCAT;
        }

        m_iDataType = CATFILE_READ;

        fclose ( cid );

        if ( readtype != CATREAD_CAT )
        {
                if ( !DecryptData () )
                        return CATERR_DECRYPT;
                m_sData[lFileSize] = 0;

                ReadFiles ();
        }

        m_sAddonDir = addon;

        m_fCatFile.Open ( catfile );

        if ( readtype != CATREAD_JUSTCONTENTS )
        {
                m_fDatFile.Open ( datfile );

                // check the file size matches
                long compare = 0;
                FILE *id = fopen ( datfile.c_str(), "rb" );
                if ( id )
                {
                        fseek ( id, 0, SEEK_END );
                        compare = ftell ( id );
                        fclose ( id );
                }
                /*
                SInCatFile *c = m_lFiles.Back ()->Data();
                if ( (c->lSize + c->lOffset) != compare )
                        return CATERR_MISMATCH;
                */
        }


        if ( readtype >= CATREAD_DAT )
                LoadDatFile ();

        return CATERR_NONE;
}

bool CCatFile::RemoveFile ( CyString file )
{
        if ( !m_sAddonDir.Empty() ) {
                SInCatFile *f = FindData ( m_sAddonDir + "\\" + file );
                if ( f )
                        RemoveFile(f);
        }
        {
                SInCatFile *f = FindData ( CyString("addon\\") + file );
                if ( f )
                        RemoveFile(f);
        }
        SInCatFile *f = FindData ( file );
        if ( !f )
        {
                m_iError = CATERR_NOFILE;
                return false;
        }

        return RemoveFile ( f );
}

void CCatFile::LoadDatFile ()
{
        if ( m_fDatFile.NoFile() )
                return;

        FILE *id = fopen ( m_fDatFile.GetFullFilename().c_str(), "rb" );
        if ( id )
        {
                for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() )
                {
                        SInCatFile *c = node->Data();
                        if ( c->sData )
                                delete c->sData;
                        c->sData = new unsigned char[c->lSize + 1];
                        size_t readAmount = fread ( c->sData, sizeof(unsigned char), c->lSize, id );
                        if ( readAmount != c->lSize )
                break;

                        DecryptDAT(c->sData, c->lSize);
                }
                fclose ( id );
        }
}

bool CCatFile::ReadFiles ()
{
        int num = 0;

        size_t offset = 0;

        unsigned char *data = m_sData;
        int spacePos = -1;
        while ( m_sData[num] != '\0' )
        {
                if ( m_sData[num] == '\n' )
                {
                        m_sData[num] = 0;
                        if ( spacePos != -1 )
                        {
                                CyString file;
                                int size = 0;

                                m_sData[spacePos] = '\0';
                                file = (char *)data;
                                data = (m_sData + (spacePos + 1));
                                if ( !CyString((char *)data).IsNumber() )
                                        size = 0;
                                else
                                        size = atoi((const char *)data);

                                if ( size )
                                {
                                        SInCatFile *catfile = new SInCatFile;

                                        catfile->lSize = size;
                                        catfile->sFile = file;
                                        catfile->lOffset = offset;
                                        catfile->sData = 0;

                                        offset += catfile->lSize;

                                        m_lFiles.push_back ( catfile );
                                }
                        }
                        data = (m_sData + (num + 1));
                        spacePos = -1;
                }
                else if ( m_sData[num] == ' ' )
                        spacePos = num;
                ++num;
        }

        /*
        CyString s((const char*)m_sData);
        CyString *lines = s.SplitToken ( '\n', &num );


        for ( int i = 1; i < num; i++ )
        {
                CyString l = lines[i];
                if ( l.NumToken ( ' ' ) <= 1 )
                        continue;

                SInCatFile *catfile = new SInCatFile;

                catfile->lSize = l.GetToken ( l.NumToken ( ' ' ), ' ' ).ToLong();
                catfile->sFile = l.GetToken ( 0, l.NumToken ( ' ' ) - 1, ' ' );
                catfile->lOffset = offset;
                catfile->sData = 0;

                offset += catfile->lSize;

                m_lFiles.push_back ( catfile );
        }
*/
        return true;
}

bool CCatFile::DecryptData ( unsigned char *data, size_t size )
{
        unsigned char cl=0xDB, dh=0xDC, dl=0xDD, ah=0xDE, ch=0xDF, al;
        unsigned char *ptr = data, *end = data + size;
        for ( ptr = data; ptr < end; ptr += 5)
        {
                al=ch;
                ch+=5;
                *(ptr + 4) ^= al;
                al=ah;
                ah+=5;
                *(ptr + 3) ^= al;
                al=dl;
                dl+=5;
                *(ptr + 2) ^= al;
                al=dh;
                dh+=5;
                *(ptr + 1) ^= al;
                al=cl;
                cl+=5;
                *(ptr + 0) ^= al;
        }

        return true;
}

bool CCatFile::DecryptData ()
{
        if ( !DecryptData ( m_sData, m_lSize ) )
                return false;

        m_iDataType = CATERR_DECRYPT;

        return true;
}

void CCatFile::RemoveData ()
{
        if ( m_sData )
                delete m_sData;
        m_sData = NULL;
        m_iDataType = CATFILE_NONE;
        m_lSize = 0;
}

bool CCatFile::ReadFileToData ( CyString filename )
{
        SInCatFile *c = FindData ( filename );
        return ReadFileToData ( c );
}

bool CCatFile::ReadFileToData ( SInCatFile *c )
{
        if ( !c )
                return false;
        size_t size = 0;
        c->sData = ReadData ( c, &size );

        if ( c->sData )
                return true;

        return false;
}

unsigned char *CCatFile::ReadData ( SInCatFile *c, size_t *size )
{
        *size = c->lSize;

        FILE *id = fopen ( m_fDatFile.GetFullFilename().c_str(), "rb" );
        if ( id )
        {
                fseek ( id, (long)c->lOffset, SEEK_SET );
                unsigned char *data = new unsigned char[c->lSize + 1];
                fread ( data, sizeof(unsigned char), c->lSize, id );
                *size = c->lSize;
                data[c->lSize] = '\0';

                DecryptDAT(data, *size);

                fclose ( id );

                return data;
        }

        return NULL;
}

void CCatFile::DecryptDAT(unsigned char *buffer, size_t size)
{
        for(unsigned char *pos=buffer, *end=buffer + size; pos < end; pos++){
                *pos^=0x33;
        }
}
unsigned char *CCatFile::ReadData ( CyString filename, size_t *size )
{
        *size = 0;

        if ( !m_fDatFile.NoFile() )
        {
                SInCatFile *c = FindData ( filename );
                if ( c )
                        return ReadData ( c, size );
        }

        return NULL;
}

unsigned char *CCatFile::UnpackFile ( SInCatFile *c, size_t *size, bool forcepck )
{
        *size = 0;
        if ( !c )
                return NULL;

        if ( IsDataPCK ( (const unsigned char *)c->sData, c->lSize ) || forcepck )
                return UnPCKData ( c->sData, c->lSize, size );

        *size = c->lSize;
        return c->sData;
}

bool CCatFile::RemoveFile ( SInCatFile *f )
{
        if ( (m_bCreate) || (m_lFiles.empty()) )
                return false;

        if ( !f )
                return false;

        int err = m_fDatFile.TruncateFile ( f->lOffset, f->lSize );
        if ( err == FILEERR_TOSMALL )
        {
                if ( (int)m_fDatFile.GetFilesize() > this->GetEndOffset() )
                        m_fDatFile.TruncateFile( this->GetEndOffset(), m_fDatFile.GetFilesize() - this->GetEndOffset() );
        }
        else if ( err != FILEERR_NONE )
                return false;

        // adjust the file positions
        bool bAdjust = false;
        int iOffset;
        for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() ) {
                int iNextOffset = node->Data()->lOffset;
                if ( bAdjust ) {
                        node->Data()->lOffset = iOffset;
                }
                else if ( node->Data() == f ) {
                        bAdjust = true;
                }
                else if ( !bAdjust ) continue;
                iOffset = iNextOffset;
        }

        // now just write the new cat file
        m_lFiles.remove ( f );
        WriteCatFile ();

        return true;
}

bool CCatFile::WriteCatFile ()
{
        if ( (m_bCreate) && (m_lFiles.empty()) )
                return false;

        CyString cat = m_fCatFile.GetFilename() + "\n";

        for ( SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next() )
        {
                if ( f->sFile.Empty() )
                        continue;
                cat += f->sFile.findreplace("/", "\\") + " " + (long)f->lSize + "\n";
        }

        if ( !cat.Length() )
                return false;

        size_t len = cat.Length();
//      if ( len % 5)
//              len += (5 - (len % 5));

        unsigned char *data = new unsigned char[len + 1];
        memcpy ( data, cat.c_str(), cat.Length() );

        for ( size_t i = len; i > cat.Length(); i-- )
                data[i] = '\0';

        DecryptData ( data, len );

        m_fCatFile.WriteData ( (const char *)data, len );

        return true;
}

bool CCatFile::CheckExtensionPck ( CyString filename )
{
        CyString ext = filename.GetToken ( ".", filename.NumToken  ( "." ) ).lower();

        if ( ext == "xml" )
                return true;
        else if ( ext == "txt" )
                return true;
        else if ( ext == "bob" )
                return true;
        else if ( ext == "bod" )
                return true;

        return false;
}

bool CCatFile::CheckPackedExtension ( CyString filename )
{
        CyString ext = filename.GetToken ( ".", filename.NumToken  ( "." ) ).lower();

        if ( ext == "pck" )
                return true;
        else if ( ext == "pbb" )
                return true;
        else if ( ext == "pbd" )
                return true;

        return false;
}

CyString CCatFile::PckChangeExtension ( CyString f )
{
        CFileIO fo ( f );
        CyString ext = fo.GetFileExtension ().lower();
        if ( ext == "txt" )
                return fo.ChangeFileExtension ( "pck" );
        else if ( ext == "xml" )
                return fo.ChangeFileExtension ( "pck" );
        else if ( ext == "bob" )
                return fo.ChangeFileExtension ( "pbb" );
        else if ( ext == "bod" )
                return fo.ChangeFileExtension ( "pbd" );

        return f;
}

bool CCatFile::AppendFile ( CyString filename, CyString to, bool pck, bool bXor )
{
        if ( (!m_bCreate) && (!m_fCatFile.Exists ()) )
                return false;

        if ( !m_sAddonDir.Empty() && CCatFile::IsAddonDir(to) ) {
                to = m_sAddonDir + "\\" + to;
        }

        if ( filename.IsIn ( "::" ) )
                return WriteFromCat ( filename.GetToken ( "::", 1, 1 ), filename.GetToken ( "::", 2, 2 ) );

        // first open the file to check it exists
        FILE *id = fopen ( filename.c_str(), "rb" );
        if ( !id )
                return false;
        fclose ( id );

        // then check if the file already exists
        if ( !m_lFiles.empty() )
        {
                SInCatFile *f = FindData ( to );
                if ( f )
                {
                        if ( !RemoveFile ( f ) )
                                return false;
                }
        }

        bool append = false;

        SInCatFile *f = new SInCatFile;
        f->sData = 0;
        f->lOffset = this->GetEndOffset();

        bool dofile = true;
        if ( pck && CheckExtensionPck(filename) )
        {
                to = PckChangeExtension ( to );

                if ( !m_lFiles.empty() )
                {
                        SInCatFile *checkf = FindData ( to );
                        if ( checkf )
                        {
                                if ( !RemoveFile ( checkf ) )
                                        return false;
                        }
                }
        }

        if ( !m_lFiles.size() )
                m_fDatFile.WipeFile();

//      if ( CheckPackedExtension(to) && !CheckPackedExtension(filename) )
        {
                FILE *id = fopen ( filename.c_str(), "rb" );
                if ( !id )
                        return false;
                fseek ( id, 0, SEEK_END );
                size_t size = ftell ( id );
                fseek ( id, 0, SEEK_SET );
                unsigned char *data = new unsigned char[size + 1];
                fread ( data, sizeof(unsigned char), size, id );
                data[size] = '\0';
                fclose ( id );

                if ( CheckPackedExtension(to) && !IsDataPCK ( data, size ) )
                {
                        size_t newsize = 0;
                        unsigned char *newdata = PCKData ( data, size, &newsize, bXor );

                        this->DecryptDAT(newdata, newsize);
                        f->lSize = newsize;
                        append = m_fDatFile.AppendDataToPos ( (const char *)newdata, newsize, f->lOffset );

                        delete [] newdata;
                }
                else 
                {
                        this->DecryptDAT(data, size);
                        f->lSize = size;
                        append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
                }

                delete [] data;
        }

        if ( append )
        {
                m_bCreate = false;
                f->sFile = to;
                m_lFiles.push_back ( f );
                WriteCatFile ();
        }
        else
                delete f;

        return true;
}

bool CCatFile::AddData ( CyString catfile, unsigned char *data, size_t size, CyString to, bool pck, bool create )
{
        int err = Open ( catfile, "", CATREAD_CATDECRYPT, create );
        if ( (err != CATERR_NONE) && (err != CATERR_CREATED) )
                return false;

        return AppendData ( data, size, to, pck );
}

int CCatFile::GetEndOffset()
{
        if ( m_lFiles.empty() )
                return 0;
        else
                return m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
}


bool CCatFile::AppendData ( unsigned char *data, size_t size, CyString to, bool pck, bool bXor )
{
        if ( (!m_bCreate) && (!m_fCatFile.Exists ()) )
                return false;

        if ( (size <= 0) || (!data) )
                return false;

        // then check if the file already exists
        if ( !m_lFiles.empty() )
        {
                SInCatFile *f = FindData ( to );
                if ( f )
                {
                        if ( !RemoveFile ( f ) )
                                return false;
                }
        }

        bool append = false;

        if ( !m_lFiles.size() )
                m_fDatFile.WipeFile();

        SInCatFile *f = new SInCatFile;
        f->sData = 0;
        if ( m_lFiles.empty() )
                f->lOffset = 0;
        else
                f->lOffset = m_fDatFile.GetFilesize();

        // if file extension is packed but not the file, then pack it, "pck" forces it to be packed unless it already is

        f->lSize = size;
        if ( (((pck) && (CheckExtensionPck (to))) || ((CheckPackedExtension ( to )))) && (!IsDataPCK ( data, size )) )
        {
                to = PckChangeExtension ( to );

                if ( !m_lFiles.empty() )
                {
                        SInCatFile *f = FindData ( to );
                        if ( f )
                        {
                                if ( !RemoveFile ( f ) )
                                        return false;
                        }
                }
                size_t newsize = 0;
                unsigned char *d = PCKData ( data, size, &newsize, bXor );

                f->lSize = newsize;
                this->DecryptDAT(d, newsize);
                append = m_fDatFile.AppendDataToPos ( (const char *)d, newsize, f->lOffset );

                delete [] d;
        }
        else {
                this->DecryptDAT(data, size);
                append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
        }

        if ( append )
        {
                m_bCreate = false;
                f->sFile = to;
                m_lFiles.push_back ( f );
                WriteCatFile ();
        }
        else
                delete f;

        return true;
}

CyString CCatFile::RenameFileExtension(SInCatFile *f)
{
        CFileIO fo ( f->sFile );
        CyString ext = fo.GetFileExtension ().lower();
        if ( ext == "pck" )
        {
                CyString firstDir = f->sFile.FindReplace("/", "\\").GetToken("\\", 1, 1).lower();

                if ( firstDir == "t" )
                        return fo.ChangeFileExtension ( "xml" );
                else if ( firstDir == "director" )
                        return fo.ChangeFileExtension ( "xml" );
                return fo.ChangeFileExtension ( "txt" );
        }
        else if ( ext == "pbb" )
                return fo.ChangeFileExtension ( "bob" );
        else if ( ext == "pbd" )
                return fo.ChangeFileExtension ( "bod" );
        return f->sFile;
}

void CCatFile::FindFiles(CyStringList *files, CyString filemask)
{
        if ( m_lFiles.empty() )
                return;

        for ( SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next() )
        {
                if ( filemask.WildMatch(f->sFile) )
                        files->PushBack(f->sFile);
        }
}

bool CCatFile::ExtractAll(CyString dir)
{
        if ( m_lFiles.empty() )
                return false;

        for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() )
        {
                if ( !ExtractFile(node->Data(), dir, true) )
                        return false;
        }

        return true;
}
bool CCatFile::ExtractFile ( CyString filename, CyString to, bool preserve )
{
        if ( m_lFiles.empty() )
                return false;

        // check if file exists
        if ( !m_sAddonDir.Empty() ) {
                SInCatFile *f = FindData ( m_sAddonDir + "\\" + filename );
                if ( f )
                        return ExtractFile ( f, to, preserve );
        }
        {
                SInCatFile *f = FindData ( CyString("addon\\") + filename );
                if ( f )
                        return ExtractFile ( f, to, preserve );
        }

        SInCatFile *f = FindData ( filename );
        if ( !f )
        {
                m_iError = CATERR_NOFILE;
                return false;
        }

        return ExtractFile ( f, to, preserve );
}

bool CCatFile::ExtractFile ( SInCatFile *f, CyString to, bool preserve )
{
        unsigned char *data = 0;
        size_t size = 0;

        // load the data from file
        ReadFileToData ( f );

        data = UnpackFile ( f, &size, CheckPackedExtension(f->sFile) );
        if ( !data )
        {
                m_iError = CATERR_CANTREAD;
                return false;
        }

        CFileIO fo(CCatFile::RenameFileExtension(f));
        // check for a file name
        CyString checkFile = to;
        CyString todir, tofile = to;
        if ( checkFile.IsAnyIn("\\/") )
        {
                checkFile = checkFile.FindReplace ( "\\", "/" );
                checkFile = checkFile.FindReplace ( "//", "/" );
                tofile = checkFile.GetToken("/", checkFile.NumToken('/'));

                if ( !checkFile.IsIn(".") || preserve )
                {
                        tofile = fo.GetFilename();
                        todir = checkFile;
                }
                else
                {
                        todir = CFileIO(checkFile).GetDir();
                        tofile = CFileIO(checkFile).GetFilename();
                }
        }

        if ( tofile.Empty() )
        {
                if ( !tofile.Empty() )
                        tofile += "/";
                tofile += fo.GetFilename();
                todir = CFileIO(tofile).GetDir();
                tofile = CFileIO(tofile).GetFilename();
        }
        
        if ( preserve )
        {
                if ( !fo.GetDir().Compare(todir.Right(- (int)fo.GetDir().Length())) )
                {
                        if ( !todir.Empty() && todir.Right(1) != "/" )
                                todir += "/";
                        todir += fo.GetDir();
                }
        }

        if ( tofile.Empty() )
                tofile = fo.GetFilename();

        // create the directory to extract to
        if ( !todir.Empty() && !CDirIO(todir).Create() )
                m_iError = CATERR_CANTCREATEDIR;
        else
        {
                CyString file = todir;
                if ( !file.Empty() )
                        file += "/";
                file += tofile;
                file = file.FindReplace("/", "\\");
                file = file.FindReplace("\\\\", "\\");

                FILE *id = fopen ( file.c_str(), "wb" );
                if ( !id )
                        m_iError = CATERR_INVALIDDEST;
                else
                {
                        fwrite ( data, sizeof(char), size, id );
                        fclose ( id );

                        ClearError ();

                        delete data;
                        f->sData = 0;

                        return true;
                }
        }

        delete data;
        f->sData = 0;

        return false;
}

CyString CCatFile::GetErrorString ()
{
        switch ( m_iError )
        {
                case CATERR_NONE:
                        return NullString;
                case CATERR_NODATFILE:
                        return "Unable to open Dat file";
                case CATERR_NOCATFILE:
                        return "Unable to open Cat file";
                case CATERR_FILEEMPTY:
                        return "Cat file is empty";
                case CATERR_READCAT:
                        return "Unable to read from cat file";
                case CATERR_DECRYPT:
                        return "Unable to decrypt cat file";
                case CATERR_MISMATCH:
                        return "File size mismatch with Dat file";
                case CATERR_NOFILE:
                        return "Unable to find file in archive";
                case CATERR_CANTREAD:
                        return "Unable to read file in archive";
                case CATERR_CANTCREATEDIR:
                        return "Unable to create destiantion directory";
                case CATERR_INVALIDDEST:
                        return "Unable to write to destiantion file";
        }

        return "Invalid";
}


unsigned char *CompressPCKData ( unsigned char *buffer, size_t size, size_t *retsize, time_t mtime )
{
        size_t newsize = (size * 2) + 20;
        unsigned char *data = (unsigned char *)malloc ( sizeof(unsigned char) * newsize );

        z_stream zs;
        char flags=0;

//      error(0);

        unsigned char *d = data;
//      if(m_pszComment && strlen(m_pszComment) > 0) flags|=GZ_F_COMMENT;
//      if(m_pszFileName && strlen(m_pszFileName) > 0) flags|=GZ_F_FILENAME;

        int pos = PCKHEADERSIZE;
        *d = 0x1F;              d++;
        *d = 0x8B;              d++;
        *d = 8;                 d++;
        *d = flags;     d++;
        memcpy(d, &mtime, sizeof(mtime));
        d += 4;
        *d = 0;                 d++;
        *d = 11;                d++;
//      if(flags & GZ_F_FILENAME) put((const char *)m_pszFileName);
//      if(flags & GZ_F_COMMENT) put((const char *)m_pszComment);

        memset(&zs, 0, sizeof(zs));
        zs.next_in=buffer;
        zs.avail_in=(long)size;

        int ret;
        unsigned long ubound;
        ret=deflateInit2(&zs, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
        if(ret!=Z_OK)
                return false;

        ubound=deflateBound(&zs, (unsigned long)size);
        if ( newsize < ubound)
        {
                newsize += ubound;
                data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
        }


        zs.next_out=d;
        zs.avail_out=(unsigned int)newsize - pos;

        while((ret=deflate(&zs, Z_FINISH))==Z_OK)
        {
                newsize += 1024;
                data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
                zs.next_out=data + zs.total_out;
                zs.avail_out=(unsigned int)newsize - zs.total_out;
        }
        pos += zs.total_out;

        deflateEnd(&zs);

        unsigned long crc=crc32(0, NULL, 0);
        crc=crc32(crc, buffer, (unsigned int)size);

        int s = sizeof(crc) + sizeof(size);
        if ( newsize < (size_t)(s + pos) )
        {
                newsize += (s + pos) - newsize;
                data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
        }

        memcpy(&data[pos], &crc, sizeof(crc));
        pos += sizeof(crc);
        memcpy(&data[pos], &size, sizeof(size));
        pos += sizeof(size);

        newsize = pos;

        unsigned char *retdata = NULL;
        if ( ret == Z_STREAM_END )
        {
                *retsize = newsize;
                retdata = new unsigned char[newsize];
                memcpy ( retdata, data, newsize );
        }
        free ( data );

        return retdata;
}

unsigned char *PCKData ( unsigned char *data, size_t oldsize, size_t *newsize, bool bXor )
{
        unsigned char *newdata = CompressPCKData ( data, oldsize, newsize, time(NULL) );
        if ( !bXor )
                return newdata;

        if ( newdata )
        {
                char magic = (char)clock(), m;
                m=magic ^ 0xC8;

                unsigned char *ptr = newdata, *end = newdata + *newsize;
                // XOR encryption
                if ( bXor )
                {
                        for ( ; ptr < end; ptr++ )
                                (*ptr)^=magic;
                }

                unsigned char *finalData = new unsigned char[*newsize + 1];
                finalData[0] = m;
                memcpy ( finalData + 1, newdata, *newsize );
                delete [] newdata;
                (*newsize)++;
                return finalData;
        }

        return NULL;
}

bool CCatFile::WriteFromCat ( CCatFile *fcat, CyString file )
{
        // now find the file in the cat file
        SInCatFile *getfile = fcat->FindData ( file );
        if ( !getfile )
                return false;

        // read the dat from the cat file
        size_t size = 0;
        unsigned char *data = fcat->ReadData ( getfile, &size );
        if ( !data )
                return false;

        // now check if it exists in this file
        RemoveFile ( file );

        // now write to the new file
        if ( !m_fDatFile.AppendData ( (const char *)data, size ) )
                return false;

        // finally add to the list
        SInCatFile *f = new SInCatFile;
        f->sData = 0;
        if ( m_lFiles.empty() )
                f->lOffset = 0;
        else
                f->lOffset = m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
        f->sFile = file;
        f->lSize = size;
        m_lFiles.push_back ( f );
        WriteCatFile ();

        return true;
}

bool CCatFile::WriteFromCat ( CyString catfile, CyString file )
{
        CCatFile fcat;
        if ( fcat.Open ( catfile, "", CATREAD_CATDECRYPT, false ) )
                return false;

        return this->WriteFromCat(&fcat, file);
}

SInCatFile *CCatFile::FindData ( CyString filename )
{
        if ( m_lFiles.empty() )
                return NULL;

        CyString check = filename;
        check = check.FindReplace ( "\\", "/" );
        for ( SInCatFile *c = m_lFiles.First(); c; c = m_lFiles.Next() )
        {
                CyString f = c->sFile;
                f = f.FindReplace ( "\\", "/" );
                if ( f.Compare(check) )
                        return c;
        }
        return NULL;
}

bool CCatFile::MarkRemoveFile( CyString file )
{
        SInCatFile *f = FindData ( file );
        if ( !f )
        {
                m_iError = CATERR_NOFILE;
                return false;
        }

        return MarkRemoveFile ( f );
}
bool CCatFile::MarkRemoveFile ( SInCatFile *f )
{
        f->bDelete = true;
        return true;
}


void CCatFile::WriteFiles()
{
        int count = 0;
        CListNode<SInCatFile> *node;
        for ( node = m_lFiles.Front(); node; node = node->next() )
        {
                if ( node->Data()->bDelete )
                        ++count;
        }

        if ( !count )
                return;

        size_t *offset = new size_t[count * 2];
        count = 0;

        node = m_lFiles.Front();
        while ( node )
        {
                CListNode<SInCatFile> *nextNode = node->next();

                SInCatFile *f = node->Data();
                if ( f->bDelete )
                {
                        offset[count++] = f->lOffset;
                        offset[count++] = f->lSize;

                        m_lFiles.remove(node);
                        delete f;
                }

                node = nextNode;
        }

        if ( m_fDatFile.WritePartFile(offset, count) )
                WriteCatFile();
}

CyStringList *CCatFile::GetTShipsEntries()
{
        if ( this->ExtractFile("types/tships.pck", "tships.txt") )
        {
                CFileIO TShips("tships.txt");
                if ( TShips.Exists() )
                {
                        CyStringList *lines = TShips.ReadLinesStr();
                        if ( lines )
                        {
                                // remove any commands, blank lines, and start
                                // and put the id as the data
                                int entries = -1;
                                for ( SStringList *str = lines->Head(); str; str = str->next )
                                {
                                        str->str.RemoveFirstSpace();
                                        str->str.RemoveChar(9);
                                        str->str.RemoveChar('\r');
                                        str->str.RemoveChar('\n');
                                        if ( str->str.Empty() )
                                        {
                                                str->remove = true;
                                                continue;
                                        }

                                        if ( str->str[0] == '/' )
                                        {
                                                str->remove = true;
                                                continue;
                                        }

                                        if ( entries == -1 )
                                        {
                                                entries = str->str.GetToken(";", 2, 2).ToInt();
                                                str->remove = true;
                                                continue;
                                        }

                                        // hopefully we now have a valid tships line
                                        int num = -1;
                                        while ( str->data.Empty() )
                                                str->data = str->str.GetToken(";", num--, (num + 2));
                                }
                        }
                        // remove the tmp file
                        TShips.Remove();

                        if ( lines )
                                lines->RemoveMarked();
                        return lines;
                }
        }

        return NULL;
}

CyString CCatFile::GetTShipsEntry(CyString id)
{
        CyStringList *ships = this->GetTShipsEntries();
        if ( ships )
        {
                SStringList *node = ships->FindData(id.upper());
                if ( node )
                        return node->str;
        }
        return NullString;
}