Subversion Repositories spk

Rev

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

// SpkFile.cpp: implementation of the CSpkFile class.
//
//////////////////////////////////////////////////////////////////////

#include "BaseFile.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#include "spk.h"

#include "DirIO.h"
#include "File_IO.h"
#include "CatFile.h"
#include "archive/zip.h"
#include "Packages.h"



CArchiveFile::~CArchiveFile()
{
        Delete ();
}

CArchiveFile::CArchiveFile() : CBaseFile ()
{
        SetDefaults ();

        m_iType = TYPE_ARCHIVE;
}

void CBaseFile::SetDefaults ()
{
        m_pIconFile = NULL;

        m_bAutoGenerateUpdateFile = false;
        m_SHeader.iValueCompression = SPKCOMPRESS_ZLIB;
        m_SHeader2.iFileCompression = SPKCOMPRESS_ZLIB;
        m_SHeader2.iDataCompression = SPKCOMPRESS_LZMA;
        m_pParent = NULL;
        m_bChanged = false;
        m_bUpdate = false;
        m_iPluginType = PLUGIN_NORMAL;

        ClearError();

        m_iLoadError = 0;

        m_bFullyLoaded = false;
        m_bSigned = false;
        m_bEnable = m_bGlobal = m_bProfile = m_bModifiedEnabled = true;
        m_bOverrideFiles = false;

        m_iRecommended = m_iEaseOfUse = m_iGameChanging = -1;
}

CBaseFile::CBaseFile()
{
        SetDefaults ();

        m_iType = TYPE_BASE;
}
CBaseFile::~CBaseFile()
{
        Delete();
}

void CBaseFile::Delete ()
{
        m_lFiles.clear(true);

        if ( m_pIconFile )
        {
                delete m_pIconFile;
                m_pIconFile = NULL;
        }

        m_lInstallText.clear(true);
        m_lUninstallText.clear(true);
        m_lNames.clear(true);
}

CyString CBaseFile::GetLanguageName ( int lang )
{
        for ( CListNode<SNames> *node = m_lNames.Front(); node; node = node->next() )
        {
                SNames *n = node->Data();
                if ( n->iLanguage == lang )
                        return n->sName;
        }
        return m_sName;
}


/*
##########################################################################################
##################                Base Class Functions                  ##################
##########################################################################################
*/

CLinkList<C_File> *CBaseFile::GetFileList(int type)
{
        CLinkList<C_File> *list = new CLinkList<C_File>;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() == type )
                        list->push_back(f);
        }

        return list;
}

C_File *CBaseFile::GetNextFile(C_File *prev) const
{
        int type = -1;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( type == -1 )
                {
                        if ( f == prev )
                                type = f->GetFileType();
                }
                else
                {
                        if ( f->GetFileType() == type )
                                return f;
                }
        }

        return NULL;
}

C_File *CBaseFile::GetPrevFile(C_File *next) const
{
        if ( !next )
                return NULL;

        int type = -1;
        for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
        {
                C_File *f = node->Data();
                if ( type == -1 )
                {
                        if ( f == next )
                                type = f->GetFileType();
                }
                else
                {
                        if ( f->GetFileType() == type )
                                return f;
                }
        }

        return NULL;
}

C_File *CBaseFile::GetFirstFile(int type) const
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() == type )
                        return f;
        }

        return NULL;
}

int CBaseFile::CheckFile ( CyString filename, float *version )
{
        FILE *id = fopen ( filename.c_str(), "rb" );
        if ( !id )
                return false;

        CyString line = GetEndOfLine ( id, NULL, false );
        CyString type = line.GetToken ( 1, ';' );
        fclose ( id );

        // check for old version
        if ( line.Left(3) == "HiP" )
                return SPKFILE_OLD;

        // check for format
        if ( version )
                *version = line.GetToken ( 2, ';' ).ToFloat();

        if ( type == "BaseCycrow" )
                return SPKFILE_BASE;
        if ( type == "SPKCycrow" )
                return SPKFILE_SINGLE;
        if ( type == "XSPCycrow" )
                return SPKFILE_SINGLESHIP;
        if ( type == "MSPKCycrow" )
                return SPKFILE_MULTI;
        return SPKFILE_INVALID;
}

void CBaseFile::ClearFileData()
{
        for ( C_File *f = m_lFiles.First(); f; f = m_lFiles.Next() )
        {
                f->DeleteData();
        }
}

CyString CBaseFile::GetNameValidFile ()
{
        CyString name = m_sName;
        name.RemoveChar ( ':' );
        name.RemoveChar ( '/' );
        name.RemoveChar ( '\\' );
        name.RemoveChar ( '*' );
        name.RemoveChar ( '?' );
        name.RemoveChar ( '"' );
        name.RemoveChar ( '<' );
        name.RemoveChar ( '>' );
        name.RemoveChar ( '|' );
        return name;
}

void CBaseFile::SwitchFilePointer(C_File *oldFile, C_File *newFile)
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f == oldFile )
                {
                        node->ChangeData(newFile);
                        break;
                }
        }
}

bool CBaseFile::AnyFileType ( int type )
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() == type )
                        return true;
        }

        return false;
}

void CBaseFile::AddFile ( C_File *file )
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() != file->GetFileType() )
                        continue;
                if ( f->GetName() != file->GetName () )
                        continue;
                if ( f->GetDir() != file->GetDir() )
                        continue;
                if ( f->GetGame() != file->GetGame() )
                        continue;

                m_lFiles.remove(node, true);
                break;
        }

        file->UpdateSigned();
        m_bChanged = true;
        m_lFiles.push_back ( file );
}

C_File *CBaseFile::AddFile ( CyString file, CyString dir, int type, int game )
{
        C_File *newfile = new C_File ( file );
        newfile->SetDir ( dir );
        newfile->SetFileType ( type );
        newfile->SetGame(game);

        // first check if the file already exists
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() != newfile->GetFileType() )
                        continue;
                if ( f->GetName() != newfile->GetName () )
                        continue;
                if ( f->GetDir() != newfile->GetDir() )
                        continue;
                if ( f->GetGame() != newfile->GetGame() )
                        continue;

                // must already exist, delete this one
                m_lFiles.remove(node, true);
                break;
        }

        m_bChanged = true;
        newfile->UpdateSigned();
        m_lFiles.push_back ( newfile );

        return newfile;
}

bool CBaseFile::AddFileNow ( CyString file, CyString dir, int type, CProgressInfo *progress )
{
        C_File *f = AddFile ( file, dir, type );
        if ( !f->ReadFromFile () )
                return false;

        // compress the file
        return f->CompressData ( m_SHeader2.iDataCompression, progress );
}

C_File *CBaseFile::AppendFile ( CyString file, int type, int game, CyString dir, CProgressInfo *progress )
{
        C_File *newfile = AddFile ( file, dir, type, game );
        if ( !newfile )
                return NULL;


        // read the file into memory
        if ( newfile->ReadFromFile () )
        {
                // now compress the file
                if ( newfile->CompressData ( m_SHeader2.iDataCompression, progress ) )
                        return newfile;
        }
        else if ( newfile->GetLastError() == SPKERR_MALLOC )
        {
                if ( newfile->CompressFile ( progress ) )
                        return newfile;
        }

        m_lFiles.pop_back ();
        delete newfile;

        return NULL;
}

C_File *CBaseFile::FindFileAt ( int filetype, int pos )
{
        int count = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != filetype )
                        continue;

                if ( count == pos )
                        return file;

                ++count;
        }

        return NULL;
}

C_File *CBaseFile::FindFile ( CyString filename, int type, CyString dir, int game )
{
        CyString lfile = filename.ToLower();
        lfile = lfile.FindReplace ( "\\", "/" );

        lfile = lfile.GetToken ( lfile.NumToken('/'), '/' );

        CListNode<C_File> *node = m_lFiles.Front();
        while ( node )
        {
                C_File *f = node->Data();
                node = node->next();

                if ( type != f->GetFileType() )
                        continue;
                if ( dir != f->GetDir() )
                        continue;
                if ( game != f->GetGame() )
                        continue;
                if ( f->GetName().ToLower() == lfile )
                        return f;
        }
        return NULL;
}

bool CBaseFile::RemoveFile ( CyString file, int type, CyString dir, int game )
{
        C_File *f = FindFile (file, type, dir, game);
        if ( !f )
                return false;
        return RemoveFile ( f );
}

bool CBaseFile::RemoveFile ( C_File *file )
{
        int count = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f == file )
                        return RemoveFile ( count );
                ++count;
        }
        return false;
}

bool CBaseFile::RemoveFile ( int pos )
{
        if ( (pos < 0) || (pos >= m_lFiles.size()) )
                return false;

        C_File *file = m_lFiles.Get ( pos );
        m_lFiles.erase ( pos + 1 );

        if ( file )
                delete file;

        m_bChanged = true;

        return true;
}


void CBaseFile::RemoveAllFiles ( int type, int game )
{
        if ( m_lFiles.empty() )
                return;

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                if ( game > -1 ) {
                        if ( node->Data()->GetGame() != game )
                                continue;
                }
                if ( type == -1 || node->Data()->GetFileType() == type )
                        node->DeleteData();
        }

        m_lFiles.RemoveEmpty();

        m_bChanged = true;
}

void CBaseFile::RecompressAllFiles ( int type, CProgressInfo *progress )
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( progress )
                        progress->UpdateFile(file);

                if ( file->GetCompressionType() == type )
                        continue;

                if ( !file->GetData() )
                        file->ReadFromFile();

                file->ChangeCompression ( type, progress );
        }
}

void CBaseFile::CompressAllFiles ( int type, CProgressInfo *progress, int level )
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( progress )
                        progress->UpdateFile(file);

                if ( !file->GetData() )
                        file->ReadFromFile();

                file->CompressData ( type, progress, level );
        }
}

bool CBaseFile::UncompressAllFiles ( CProgressInfo *progress )
{
        int countFile = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();
                if ( progress )
                {
                        progress->UpdateFile ( fit );
                        progress->UpdateProgress(countFile++, m_lFiles.size());
                }

                bool uncomprToFile = false;

                if ( progress )
                        progress->SwitchSecond();

                if ( !fit->UncompressData ( progress ) )
                {
                        if ( fit->GetCompressionType() == SPKCOMPRESS_7ZIP )
                        {
                                if ( !fit->UncompressToFile ( "temp", this, false, progress ) )
                                        return false;
                                else
                                {
                                        uncomprToFile = true;
                                        fit->SetFullDir ( "temp" );
                                }
                        }

                        if ( !uncomprToFile )
                                return false;
                }

                if ( progress )
                        progress->SwitchSecond();
        }
        return true;
}

long CBaseFile::GetFullFileSize()
{
        long fullsize = 1000;

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                fullsize += node->Data()->GetUncompressedDataSize();

        if ( m_pIconFile )
                fullsize += m_pIconFile->GetUncompressedDataSize();

        return fullsize;
}


/*
        Func:   GetEndOfLine
        Input:  id - The file id for the current file to read from
                line - Pointed to hold the line number thats read
                        upper - true if it converts to uppercase
        Return: String - the string it has read
        Desc:   Reads a string from a file, simlar to readLine() classes, reads to the end of the line in a file
*/
CyString CBaseFile::GetEndOfLine ( FILE *id, int *line, bool upper )
{
        CyString word;

        char c = fgetc ( id );
        if ( c == -1 )
                return "";

        while ( (c != 13) && (!feof(id)) && (c != '\n') )
        {
                word += c;
                c = fgetc ( id );
        }

        if ( line )
                ++(*line);

        if ( upper )
                return word.ToUpper();

        return word;
}

int CBaseFile::CountFiles ( int filetype )
{
        int i = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != filetype )
                        continue;
                ++i;
        }

        return i;
}

void CBaseFile::ClearNames ()
{
        m_lNames.clear(true);
}
void CBaseFile::RemoveLanguageName ( int lang )
{
        SNames *n;
        for ( n = m_lNames.First(); n; n = m_lNames.Next() )
        {
                if ( n->iLanguage == lang )
                {
                        m_lNames.RemoveCurrent();
                        delete n;
                        m_bChanged = true;
                }
        }
}

void CBaseFile::AddLanguageName ( int lang, CyString name )
{
        // first check for an existing language
        SNames *n;
        for ( n = m_lNames.First(); n; n = m_lNames.Next() )
        {
                if ( n->iLanguage == lang )
                {
                        n->sName = name;
                        return;
                }
        }

        // not found, add a new entry
        n = new SNames;
        n->iLanguage = lang;
        n->sName = name;
        m_lNames.push_back ( n );

        m_bChanged = true;
}

/*
        Func:   AddInstallText
        Input:  before - true for before text, false for after text
                        install - true to add to install text list, false for unisntall text list
                        lang - string containing the language to use
                        data - The text to add for the language
        Return: install text - Returns the text struct that it used to add
        Desc:   Adds the text to list, adds either install or uninstall text
                        If language already exists, then its overwritten
                        Allows adding before and after seperatly to the same entry
*/
SInstallText *CBaseFile::AddInstallText ( bool before, bool install, int lang, CyString data )
{
        CLinkList<SInstallText> *list = NULL;
        if ( install )
                list = &m_lInstallText;
        else
                list = &m_lUninstallText;


        SInstallText *unist = NULL;
        // check if language already exists and overright if needed
        for ( SInstallText *u = list->First(); u; u = list->Next() )
        {
                if ( u->iLanguage == lang )
                {
                        unist = u;
                        break;
                }
        }

        if ( !unist )
        {
                unist = new SInstallText;
                unist->iLanguage = lang;
                if ( lang == 0 )
                        list->push_front ( unist );
                else
                        list->push_back ( unist );
        }

        if ( !before )
                unist->sAfter = data;
        else
                unist->sBefore = data;

        m_bChanged = true;

        return unist;
}

SInstallText *CBaseFile::FindInstallText ( bool install, int lang )
{
        CLinkList<SInstallText> *list = NULL;
        if ( install )
                list = &m_lInstallText;
        else
                list = &m_lUninstallText;

        for ( SInstallText *it = list->First(); it; it = list->Next() )
        {
                if ( it->iLanguage == lang )
                        return it;
        }
        return NULL;
}

void CBaseFile::AddInstallText ( SInstallText *add )
{
        SInstallText *it = FindInstallText ( true, add->iLanguage );
        if ( it == add )
                return;

        if ( it )
        {
                m_lInstallText.remove ( it );
                delete it;
        }

        m_bChanged = true;

        m_lInstallText.push_back ( add );
}

void CBaseFile::AddUninstallText ( SInstallText *add )
{
        SInstallText *it = FindInstallText ( false, add->iLanguage );
        if ( it == add )
                return;

        if ( it )
        {
                m_lUninstallText.remove ( it );
                delete it;
        }

        m_bChanged = true;

        m_lUninstallText.push_back ( add );
}

void CBaseFile::RemoveInstallText ( bool install, int lang )
{
        SInstallText *it = FindInstallText ( install, lang );
        if ( it )
        {
                if ( install )
                        m_lInstallText.remove ( it );
                else
                        m_lUninstallText.remove ( it );
                delete it;
                m_bChanged = true;
        }
}

bool CBaseFile::IsThereInstallText ( bool install )
{
        CLinkList<SInstallText> *list = NULL;
        if ( install )
                list = &m_lInstallText;
        else
                list = &m_lUninstallText;

        if ( list->size() > 1 )
                return true;
        if ( list->size() <= 0 )
                return false;

        SInstallText *it = list->First();
        if ( !it->sAfter.Empty() )
                return true;
        if ( !it->sBefore.Empty() )
                return true;
        return false;
}

CyString CBaseFile::GetBeforeText ( CLinkList<SInstallText> *list, int lang, bool noDefault )
{
        CyString beforetext;
        for ( SInstallText *u = list->First(); u; u = list->Next() )
        {
                if ( !noDefault )
                {
                        if ( (beforetext.Empty()) && (u->iLanguage == 0) )
                                beforetext = u->sBefore;
                }

                if ( (u->iLanguage == lang) && (!u->sBefore.Empty()) )
                        beforetext = u->sBefore;
        }
        return beforetext;
}

CyString CBaseFile::GetAfterText ( CLinkList<SInstallText> *list, int lang, bool noDefault )
{
        CyString text;
        for ( SInstallText *u = list->First(); u; u = list->Next() )
        {
                if ( (u->iLanguage == lang) && (!u->sAfter.Empty()) )
                        text = u->sAfter;

                if ( !noDefault )
                {
                        if ( (text.Empty()) && (u->iLanguage == 0) )
                                text = u->sAfter;
                }
        }
        return text;
}

CyString CBaseFile::GetFullPackageName(CyString format, int lang)
{
        if ( format.Empty() )
                return GetFullPackageName(lang);

        CyString args[3] = { this->GetLanguageName(lang), this->GetVersion(), this->GetAuthor() };
        return format.Args(args, 3);
}

/*
        Func:   CreateFilesLine
        Return: String - returns the full string for files list
        Desc:   Creates a signle line list of all the files
*/
CyString CBaseFile::CreateFilesLine ( bool updateheader, CProgressInfo *progress )
{
        CyString line;

        if ( progress )
        {
                progress->SetDone(0);
                progress->UpdateStatus(STATUS_COMPRESS);
        }

        if ( updateheader )
        {
                m_SHeader2.iNumFiles = 0;
                m_SHeader2.lFullSize = 0;
        }

        if ( m_pIconFile )
        {
                // no data, read it from file
                if ( !m_pIconFile->GetData() )
                        m_pIconFile->ReadFromFile ();

                // compress the file
                if ( !m_pIconFile->CompressData ( m_SHeader2.iDataCompression, progress ) )
                        m_pIconFile->SetDataCompression(SPKCOMPRESS_NONE);

                line += CyString("Icon:") + (m_pIconFile->GetDataSize() + (long)4) + ":" + m_pIconFile->GetUncompressedDataSize() + ":" + (long)m_pIconFile->GetCompressionType() + ":" + m_sIconExt + "\n";
                if ( updateheader )
                {
                        ++m_SHeader2.iNumFiles;
                        m_SHeader2.lFullSize += (m_pIconFile->GetDataSize() + 4);
                }
        }

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( progress )
                        progress->UpdateFile ( file );

                // no data, read it from file
                if ( !file->GetData() )
                {
                        if ( !file->ReadFromFile () )
                        {
                                if ( file->GetLastError() == SPKERR_MALLOC )
                                {
                                        if ( !file->CompressFile ( progress ) )
                                                continue;
                                }
                        }
                }

                if ( !file->GetData() )
                        continue;

                // compress the file
                if ( !file->CompressData ( m_SHeader2.iDataCompression, progress ) )
                {
                        file->SetDataCompression(SPKCOMPRESS_NONE);
                        file->SetUncompressedDataSize(file->GetDataSize());
                }

                CyString command = GetFileTypeString ( file->GetFileType() );
                if ( command.Empty() )
                        continue;

                if ( file->IsShared() )
                        command = CyString("$") + command;

                if ( file->GetDir().Empty() )
                        line += (command + ":" + (file->GetDataSize() + (long)4) + ":" + file->GetUncompressedDataSize() + ":" + (long)file->GetCompressionType() + ":" + (long)file->GetCreationTime() + ":" + ((file->IsCompressedToFile()) ? "1" : "0") + ":" + file->GetFilename() + ":GAME_" + (long)file->GetGame() + "\n");
                else
                        line += (command + ":" + (file->GetDataSize() + (long)4) + ":" + file->GetUncompressedDataSize() + ":" + (long)file->GetCompressionType() + ":" + (long)file->GetCreationTime() + ":" + ((file->IsCompressedToFile()) ? "1" : "0") + ":" + file->GetFilename() + ":" + file->GetDir() + ":GAME_" + (long)file->GetGame() + "\n");

                if ( updateheader )
                {
                        ++m_SHeader2.iNumFiles;
                        m_SHeader2.lFullSize += (file->GetDataSize() + 4);
                }
        }

        return line;
}


/*
######################################################################################
##########                                                      Reading Functions                                       ##########
######################################################################################
*/

void CBaseFile::ReadAllFilesToMemory ()
{
        // no file to read from
        if ( m_sFilename.Empty() )
                return;

        // now open the file
        FILE *id = fopen ( m_sFilename.c_str(), "rb" );
        if ( !id )
                return;

        // read the header
        GetEndOfLine ( id, NULL, false );
        // skip past values
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader.lValueCompressSize, SEEK_CUR );

        // read the next header
        GetEndOfLine ( id, NULL, false );
        // skip past files
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader2.lSize, SEEK_CUR );

        if ( m_pIconFile )
        {
                if ( (!m_pIconFile->GetData()) && (!m_pIconFile->Skip()) )
                        m_pIconFile->ReadFromFile ( id, m_pIconFile->GetDataSize() );
                else
                {
                        fseek ( id, 4, SEEK_CUR );
                        fseek ( id, m_pIconFile->GetDataSize(), SEEK_CUR );
                }
        }

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();
                if ( (!fit->GetData()) && (!fit->Skip()) )
                        fit->ReadFromFile ( id, fit->GetDataSize() );
                else
                {
                        fseek ( id, 4, SEEK_CUR );
                        fseek ( id, fit->GetDataSize(), SEEK_CUR );
                }
        }

        fclose ( id );
}

bool CBaseFile::ReadFileToMemory(C_File *f)
{
        if ( f->GetData() && f->GetDataSize() )
                return true;
        // no file to read from
        if ( m_sFilename.Empty() || !f )
                return false;

        // check the file is part of the package
        if ( !m_lFiles.FindData(f) )
                return false;

        // now open the file
        FILE *id = fopen ( m_sFilename.c_str(), "rb" );
        if ( !id )
                return false;

        // read the header
        GetEndOfLine ( id, NULL, false );
        // skip past values
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader.lValueCompressSize, SEEK_CUR );

        // read the next header
        GetEndOfLine ( id, NULL, false );
        // skip past files
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader2.lSize, SEEK_CUR );

        if ( m_pIconFile )
        {
                fseek ( id, 4, SEEK_CUR );
                fseek ( id, m_pIconFile->GetDataSize(), SEEK_CUR );
        }

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();
                if (fit == f )
                {
                        fit->ReadFromFile ( id, fit->GetDataSize() );
                        break;
                }
                else
                {
                        fseek ( id, 4, SEEK_CUR );
                        fseek ( id, fit->GetDataSize(), SEEK_CUR );
                }
        }


        fclose(id);
        return true;
}

void CBaseFile::ReadIconFileToMemory ()
{
        // no file to read from
        if ( m_sFilename.Empty() )
                return;

        if ( !m_pIconFile )
                return;

        // now open the file
        FILE *id = fopen ( m_sFilename.c_str(), "rb" );
        if ( !id )
                return;

        // read the header
        GetEndOfLine ( id, NULL, false );
        // skip past values
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader.lValueCompressSize, SEEK_CUR );

        // read the next header
        GetEndOfLine ( id, NULL, false );
        // skip past files
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader2.lSize, SEEK_CUR );

        if ( m_pIconFile )
        {
                if ( (!m_pIconFile->GetData()) && (!m_pIconFile->Skip()) )
                        m_pIconFile->ReadFromFile ( id, m_pIconFile->GetDataSize() );
                else
                {
                        fseek ( id, 4, SEEK_CUR );
                        fseek ( id, m_pIconFile->GetDataSize(), SEEK_CUR );
                }
        }

        fclose ( id );
}

void CBaseFile::_install_adjustFakePatches(CPackages *pPackages)
{
        CyStringList lPatches;
        int startfake = pPackages->FindNextFakePatch();

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();
                // only do fake patchs
                if ( !fit->IsFakePatch() )
                        continue;

                // we should only have cat and dat files, but lets check just incase they have been added incorrectly
                if ( !fit->CheckFileExt ("cat") && !fit->CheckFileExt("dat") )
                        continue;

                // search for the name on the list
                SStringList *opposite = lPatches.FindString(fit->GetBaseName());
                CyString newname;
                if ( opposite )
                        newname = opposite->data;
                else
                {
                        newname = CyString::Number((long)startfake).PadNumber(2);
                        lPatches.PushBack(fit->GetBaseName(), newname);
                }

                // rename the file
                fit->FixOriginalName();
                fit->SetName ( newname + "." + fit->GetFileExt() );

                // find the next gap
                if ( !opposite ) {
                        startfake = pPackages->FindNextFakePatch(startfake + 1);
                }
        }

}

bool CBaseFile::InstallFiles ( CyString destdir, CProgressInfo *progress, CLinkList<C_File> *filelist, CyStringList *errorStr, bool enabled, CPackages *packages )
{
        if ( enabled )
        {
/*              int startfake = packages->FindNextFakePatch();
                while ( startfake < 99 )
                {
                        CyString filename = destdir;
                        if ( !filename.Empty() )
                                filename += "/";
                        filename += CyString::Number((long)startfake).PadNumber(2);

                        if ( !CFileIO(filename + ".cat").Exists() )
                        {
                                if ( !CFileIO(filename + ".dat").Exists() )
                                        break;
                        }
                        startfake++;
                }

                for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                {
                        C_File *fit = node->Data();
                        // only do fake patchs
                        if ( !fit->IsFakePatch() )
                                continue;

                        if ( !fit->CheckFileExt ("cat") && !fit->CheckFileExt("dat") )
                                continue;

                        // search for the name on the list
                        SStringList *opposite = lPatches.FindString(fit->GetBaseName());
                        CyString newname;
                        if ( opposite )
                                newname = opposite->data;
                        else
                        {
                                newname = CyString::Number((long)startfake).PadNumber(2);
                                lPatches.PushBack(fit->GetBaseName(), newname);
                        }

                        // rename the file
                        fit->FixOriginalName();
                        fit->SetName ( newname + "." + fit->GetFileExt() );

                        // find the next gap
                        if ( !opposite )
                        {
                                startfake++; // make sure we moved past the one we've just used
                                while ( startfake < 99 )
                                {
                                        CyString filename = destdir;
                                        if ( !filename.Empty() )
                                                filename += "/";
                                        filename += CyString::Number((long)startfake).PadNumber(2);

                                        if ( !CFileIO(filename + ".cat").Exists() )
                                        {
                                                if ( !CFileIO(filename + ".dat").Exists() )
                                                        break;
                                        }
                                        startfake++;
                                }
                        }
                }
                */
                this->_install_adjustFakePatches(packages);

                // find renameable text file
                if ( packages )
                {
                        int starttext = packages->FindNextTextFile();
                        /*
                        while ( starttext < 9999 )
                        {
                                CyString filename = destdir;
                                if ( !filename.Empty() )
                                        filename += "/t/";
                                filename += SPK::FormatTextName(starttext, packages->GetLanguage(), (packages->GetCurrentGameFlags() & EXEFLAG_TCTEXT));

                                if ( !CFileIO(filename + ".xml").Exists() )
                                {
                                        if ( !CFileIO(filename + ".pck").Exists() )
                                                break;
                                }
                                starttext++;
                        }*/

                        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                        {
                                C_File *fit = node->Data();
                                if ( !fit->IsAutoTextFile() )
                                        continue;

                                CyString newname = SPK::FormatTextName(starttext, packages->GetLanguage(), (packages->GetCurrentGameFlags() & EXEFLAG_TCTEXT));
                                fit->FixOriginalName();
                                fit->SetName ( newname + "." + fit->GetFileExt() );

                                ++starttext;
                        }
                }
        }

        CDirIO Dir(destdir);
        int fileCount = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();
                bool fileEnabled = enabled;
                if ( !fileEnabled )
                {
                        if ( (fit->GetFileType() == FILETYPE_UNINSTALL) || (fit->GetFileType() == FILETYPE_README) || (fit->GetFileType() == FILETYPE_ADVERT) )
                                fileEnabled = true;
                        else if ( (fit->GetFileType() == FILETYPE_EXTRA) && (fit->GetDir().Left(7).ToLower() == "Extras/") )
                                fileEnabled = true;
                        else if ( (IsPatch()) && (fit->GetFileType() == FILETYPE_MOD) && (!fit->IsFakePatch()) )
                                fileEnabled = true;
                }

                if ( fit->GetGame() && packages->GetGame() ) {
                        if ( fit->GetGame() != packages->GetGame() )
                                continue;
                }

                if ( progress )
                {
                        if ( progress->IsSecond() )
                                progress->SwitchSecond();
                        progress->UpdateFile ( fit );
                        progress->UpdateProgress(fileCount++, m_lFiles.size());
                        progress->SwitchSecond();
                }

                // first uncompress the file
                bool uncomprToFile = false;
                m_sLastError = fit->GetNameDirectory(this);
                m_iLastError = SPKERR_UNCOMPRESS;
                if ( !fit->UncompressData ( progress ) )
                {
                        if ( fit->GetCompressionType() == SPKCOMPRESS_7ZIP )
                        {
                                if ( fit->UncompressToFile ( NullString, this, false, progress ) )
                                        uncomprToFile = true;
                        }

                        if ( !uncomprToFile )
                        {
                                if ( errorStr )
                                        errorStr->PushBack(m_sLastError, ERRORLOG(SPKINSTALL_UNCOMPRESS_FAIL));
                                return false;
                        }
                }
                ClearError ();
                bool dofile = true;

                // new check if we should install the file
                // first get the version
                if ( !m_bOverrideFiles && fit->ReadScriptVersion() )
                {
                        C_File checkfile;
                        CyString checkfilename = destdir;
                        if ( !checkfilename.Empty() )
                                checkfilename += "/";
                        checkfilename += fit->GetNameDirectory(this);
                        checkfile.SetFilename ( checkfilename );
                        checkfile.SetFileType ( fit->GetFileType() );
                        if ( checkfile.CheckValidFilePointer() )
                        {
                                if ( checkfile.ReadScriptVersion() > fit->GetVersion() )
                                        dofile = false;
                        }
                }

                // change file pointer
                CyString filename = destdir;
                if ( !filename.Empty() )
                        filename += "/";
                if ( (IsPatch()) && (fit->GetFileType() == FILETYPE_MOD) )
                        fit->SetDir ( CyString("Patch") );

                if ( fit->IsInMod() )
                {
                        if ( fileEnabled )
                                fit->SetFilename(filename + fit->GetInMod() + "::" + fit->GetNameDirectory(this));
                        else
                                fit->SetFilename(filename + "PluginManager/DisabledFiles.cat::" + fit->GetNameDirectory(this));
                }
                else
                        fit->SetFilename ( filename + fit->GetNameDirectory(this) );

                if ( !fileEnabled )
                {
                        if ( !fit->IsInMod() )
                        {
                                if ( fit->IsFakePatch() )
                                        fit->SetFilename ( filename + "PluginManager/Disabled/FakePatches/FakePatch_" + this->GetNameValidFile() + "_" + m_sAuthor + "_" + fit->GetName() );
                                else if ( fit->IsAutoTextFile() )
                                        fit->SetFilename ( filename + "PluginManager/Disabled/TextFiles/Text_" + this->GetNameValidFile() + "_" + m_sAuthor + "_" + fit->GetName() );
                                else
                                        fit->SetFullDir ( filename + "PluginManager/Disabled/" + fit->GetDirectory(this) );
                        }
                        fit->SetDisabled(true);
                }

                C_File *adjustPointer = NULL;

                bool checkFile = dofile;
                if ( filelist )
                {
                        C_File *cFile = NULL;
                        if ( checkFile )
                        {
                                if ( !fit->IsFakePatch() && fit->GetFileType() != FILETYPE_README )
                                {
                                        for ( cFile = filelist->First(); cFile; cFile = filelist->Next() )
                                        {
                                                if ( !cFile->MatchFile ( fit ) )
                                                        continue;

                                                if ( !m_bOverrideFiles && !cFile->CompareNew ( fit ) )
                                                {
                                                        if ( errorStr )
                                                                errorStr->PushBack(fit->GetNameDirectory(this), ERRORLOG(SPKINSTALL_SKIPFILE));
                                                        dofile = false;
                                                }
                                                break;
                                        }
                                }
                        }

                        // no matching file found, adding to main list
                        if ( !cFile )
                                filelist->push_back ( fit );
                        else
                        {
                                // if the file is not enabled, we need to check for any that might be enabled
                                if ( !fileEnabled )
                                {
                                        if ( !cFile->GetUsed() )
                                        {
                                                CFileIO rFile(cFile->GetFilePointer());
                                                if ( rFile.Exists() )
                                                {
                                                        if ( errorStr )
                                                        {
                                                                if ( rFile.Remove() )
                                                                        errorStr->PushBack(cFile->GetFilePointer().Remove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE));
                                                                else
                                                                        errorStr->PushBack(cFile->GetFilePointer().Remove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE_FAIL));
                                                        }
                                                }
                                                cFile->SetFilename(fit->GetFilePointer());
                                                cFile->SetDisabled(true);
                                        }
                                        else
                                        {
                                                fit->SetFullDir ( filename + fit->GetDirectory(this) );
                                                fit->SetDisabled(false);
                                        }
                                }
                                else
                                // move it to enabled
                                {
                                        // found a file, check if its in the disabled directory
                                        CyString dir = cFile->GetFilePointer();
                                        dir = dir.GetToken ( 1, dir.NumToken ('/') - 1, '/' );
                                        CyString lastDir = dir.GetToken ( dir.NumToken('/'), '/' ).ToLower();

                                        // if its disabled, rename it so its enabled
                                        if ( ((cFile->IsDisabled()) || (lastDir == "disabled") || (dir.ToLower().IsIn ("/disabled/"))) && (enabled) )
                                        {
                                                // first check if the directory exists
                                                if ( cFile->IsInMod() )
                                                {
                                                        CyString tofile = cFile->GetFilePointer().GetToken("::", 2, 2);

                                                        CCatFile tocat;
                                                        int err = tocat.Open ( fit->GetFilePointer().GetToken("::", 1, 1), "", CATREAD_CATDECRYPT, true );
                                                        if ( (err == CATERR_NONE) || (err == CATERR_CREATED) )
                                                        {
                                                                tocat.AppendFile ( cFile->GetFilePointer(), tofile );
                                                        }

                                                        CCatFile fromcat;
                                                        err = fromcat.Open ( cFile->GetFilePointer().GetToken("::", 1, 1), "", CATREAD_CATDECRYPT, false );
                                                        if ( err == CATERR_NONE )
                                                        {
                                                                fromcat.RemoveFile(tofile);
                                                        }

                                                        cFile->SetFilename ( fit->GetFilePointer() );
                                                        cFile->SetInMod(fit->GetInMod());
                                                }
                                                else
                                                {
                                                        CyString to = cFile->GetDirectory(this);
                                                        CDirIO Dir(destdir);
                                                        if ( !Dir.Exists(to) )
                                                        {
                                                                if ( !Dir.Create ( to ) )
                                                                {
                                                                        if ( errorStr )
                                                                                errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));
                                                                        return false;
                                                                }
                                                                if ( errorStr )
                                                                        errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY));
                                                        }

                                                        CyString destfile = destdir + "/" + cFile->GetNameDirectory(this);
                                                        if ( CFileIO(destfile).Exists() )
                                                                CFileIO(destfile).Remove();
                                                        rename ( cFile->GetFilePointer().c_str(), destfile.c_str() );
                                                        cFile->SetFilename ( destdir + "/" + cFile->GetNameDirectory(this) );
                                                }
                                                cFile->SetDisabled(false);

                                                if ( !dofile && errorStr )
                                                        errorStr->PushBack(cFile->GetNameDirectory(this), ERRORLOG(SPKINSTALL_ENABLEFILE));
                                        }
                                }

                                adjustPointer = cFile;
                                if ( dofile )
                                        adjustPointer->SetCreationTime ( fit->GetCreationTime() );
                        }
                }

                if ( dofile )
                {
                        // uncompressed to file, rename and move
                        if ( uncomprToFile )
                        {
                                m_iLastError = SPKERR_WRITEFILE;
                                CyString to = fit->GetDirectory(this);
                                //to = to.GetToken ( 1, to.NumToken ('/') - 1, '/' );
                                if ( !fileEnabled )
                                        to = CyString("PluginManager/Disabled/") + to;

                                CDirIO Dir(destdir);
                                if ( !Dir.Exists(to) )
                                {
                                        if ( !Dir.Create ( to ) )
                                        {
                                                if ( errorStr )
                                                        errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));
                                                return false;
                                        }
                                        if ( errorStr )
                                                errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY));
                                }

                                int err = 1;
                                m_sLastError = to;
                                if ( !fit->GetTempFile ().Empty() )
                                        err = rename ( fit->GetTempFile().c_str(), to.c_str() );
                                if ( err )
                                        return false;
                        }
                        //otherwise, just extract the file
                        else
                        {
                                // old file is found in list, switch to using new one
                                if ( (filelist) && (adjustPointer) )
                                        adjustPointer->CopyData(fit, false);

                                CyString fpointer = fit->GetFilePointer();
                                m_iLastError = SPKERR_CREATEDIRECTORY;
                                CyString dir = fit->GetFilePointer().GetToken ( "/", 1, fit->GetFilePointer().NumToken("/") - 1 );

                                dir = dir.Remove(destdir);
                                if ( dir[0] == '/' || dir[0] == '\\' )
                                        dir.Erase(0, 1);

                                m_sLastError = dir;
                                if ( !dir.IsIn ( "::" ) )
                                {
                                        if ( !Dir.Exists(dir) )
                                        {
                                                if ( !Dir.Create(dir) )
                                                {
                                                        if ( errorStr )
                                                                errorStr->PushBack(dir, ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));
                                                        return false;
                                                }
                                                if ( errorStr )
                                                        errorStr->PushBack(dir, ERRORLOG(SPKINSTALL_CREATEDIRECTORY));
                                        }
                                }
                                else
                                        fit->SetFilename(CCatFile::PckChangeExtension(fit->GetFilePointer()));

                                m_iLastError = SPKERR_WRITEFILE;
                                m_sLastError = fit->GetFilePointer();
                                CyString sInstalledFile = fit->GetNameDirectory(this);
                                if ( fit->IsDisabled() )
                                {
                                        sInstalledFile = fit->GetFilePointer().Remove(destdir);
                                        if ( sInstalledFile[0] == '/' || sInstalledFile[0] == '\\' )
                                                sInstalledFile.Erase(0, 1);
                                }

                                if ( !fit->WriteFilePointer() )
                                {
                                        if ( errorStr )
                                                errorStr->PushBack(sInstalledFile, ERRORLOG(SPKINSTALL_WRITEFILE_FAIL));
                                }
                                else
                                {
                                        fit->UpdateSigned();
                                        if ( errorStr )
                                                errorStr->PushBack(sInstalledFile, ERRORLOG(SPKINSTALL_WRITEFILE));

                                        switch(fit->GetFileType())
                                        {
                                                case FILETYPE_SCRIPT:
                                                case FILETYPE_UNINSTALL:
                                                        fit->UpdateSignature();
                                                        break;
                                        }
                                }
                        }
                        ClearError ();
                }

                if ( adjustPointer )
                {
                        node->ChangeData(adjustPointer);
                        delete fit;
                }
        }

        // now clear or data memory
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                // add plugin manager file to identify fake patches
                /*
                if ( fit->IsFakePatch() && fit->CheckFileExt ("cat") )
                {
                        CFileIO plugin("pluginmanager.txt");
                        std::vector<CyString> lines;
                        CyString version;
                        version.FromFloat(GetLibraryVersion(), 2);
                        SStringList *strList = lPatches.FindData(fit->GetBaseName());

                        CyString baseName;
                        if ( strList )
                                baseName = strList->str;
                        else
                                baseName = fit->GetBaseName();

                        lines.push_back(CyString("FakePatch:") + baseName);
                        lines.push_back(CyString("spklibrary:") + version);
                        lines.push_back(CyString("Package:") + m_sName);
                        lines.push_back(CyString("Author:") + m_sAuthor);
                        lines.push_back(CyString("OriginalFile:") + fit->GetOriginalName());
                        if ( plugin.WriteFile(&lines) )
                        {
                                CCatFile cat;
                                if ( cat.Open(fit->GetFilePointer(), CATREAD_DAT, false) == CATERR_NONE )
                                {
                                        cat.AppendFile(plugin.GetFilename(), plugin.GetFilename(), true);
                                        cat.WriteCatFile();
                                }
                                plugin.Remove();
                        }
                }
*/
                node->Data()->DeleteData();
        }

        return true;
}



/*######################################################################################################*/


/*
        Func:   ParseHeader
        Input:  Header String - string formated directly from the file
        Return: Boolean - If string is a valid header
        Desc:   Splits up the main header string to get all required settings
*/
bool CBaseFile::ParseHeader ( CyString header )
{
        if ( !this->CheckHeader(header.GetToken ( 1, ';' ).ToString()) )
                return false;

        m_SHeader.fVersion = header.GetToken ( 2, ';' ).ToFloat();
        if ( m_SHeader.fVersion > FILEVERSION )
                return false;

        m_SHeader.iValueCompression = header.GetToken ( 3, ';' ).ToInt();
        m_SHeader.lValueCompressSize = header.GetToken ( 4, ';' ).ToLong();

        return true;
}

bool CBaseFile::CheckHeader(const Utils::String header) const
{
        if ( header.Compare("BaseCycrow") )
                return true;
        return false;
}

/*
        Func:   ParseFileHeader
        Input:  Header String - string formated directly from the file
        Return: Boolean - If string is a valid header
        Desc:   Splits up the file header string to get all required settings
*/
bool CBaseFile::ParseFileHeader ( CyString header )
{
        if ( header.GetToken ( 1, ';' ) != "FileHeader" )
                return false;

        m_SHeader2.iNumFiles = header.GetToken ( 2, ';' ).ToInt();
        m_SHeader2.lSize = header.GetToken ( 3, ';' ).ToInt();
        m_SHeader2.lFullSize = header.GetToken ( 4, ';' ).ToInt();
        m_SHeader2.iFileCompression = header.GetToken ( 5, ';' ).ToInt();
        m_SHeader2.iDataCompression = header.GetToken ( 6, ';' ).ToInt();

        return true;
}


/*
        Func:   ParseValueLine
        Input:  String - single line from a file to set
        Return: Boolean - returns true if value exists
        Desc:   Reads the line and assigns the parameters for the file
*/
bool CBaseFile::ParseValueLine(const Utils::String &sLine)
{
        Utils::String first = sLine.token(" ", 1);
        Utils::String rest  = sLine.tokens(" ", 2);

        if ( first.Compare("Name:") )
                m_sName = rest;
        else if ( first.Compare("Author:") )
                m_sAuthor = rest;
        else if ( first.Compare("Version:") )
                m_sVersion = rest;
        else if ( first.Compare("fGameVersion:") ) {
                if ( m_lGames.Back() ) {
                        m_lGames.Back()->Data()->sVersion = rest;
                }
        }
        else if ( first.Compare("GameVersion:") ) {
                if ( m_lGames.Back() ) {
                        m_lGames.Back()->Data()->iVersion = rest;
                }
        }
        else if ( first.Compare("Game:") )
                this->AddGameCompatability(rest, NullString);
        else if ( first.Compare("GameCompat:") )
                this->AddGameCompatability(rest.token(" ", 1), rest.tokens(" ", 2));
        else if ( first.Compare("GameCompatExact:") )
                this->AddGameCompatability(rest.token(" ", 1), rest.tokens(" ", 2));
        else if ( first.Compare("Date:") )
                m_sCreationDate = rest;
        else if ( first.Compare("WebAddress:") )
                m_sWebAddress = rest;
        else if ( first.Compare("WebSite:") )
                m_sWebSite = rest;
        else if ( first.Compare("Email:") )
                m_sEmail = rest;
        else if ( first.Compare("WebMirror1:") || first.Compare("Mirror1:") || first.Compare("WebMirror:") )
                this->AddWebMirror(rest);
        else if ( first.Compare("WebMirror2:") || first.Compare("Mirror2:") )
                this->AddWebMirror(rest);
        else if ( first.Compare("PluginType:") )
                m_iPluginType = rest;
        else if ( first.Compare("Desc:") )
        {
                m_sDescription = rest;
                m_sDescription.RemoveFirstChar('\n');
                m_sDescription.RemoveFirstChar('\r');
                m_sDescription.RemoveFirstSpace();
                m_sDescription = m_sDescription.FindReplace("&quot;", "\"");
                m_sDescription = m_sDescription.FindReplace("&gt;", ">");
                m_sDescription = m_sDescription.FindReplace("&lt;", "<");
                m_sDescription = m_sDescription.FindReplace("&amp;", "&");
                m_sDescription = m_sDescription.FindReplace("<newline>", "\n");
                m_sDescription = m_sDescription.FindReplace("<br>", "\n");

                if ( m_sDescription.Left(6).lower() == "<html>" )
                {
                        int foundFirst = -1;
                        int pos = 0;
                        while ( foundFirst == -1 )
                        {
                                pos = m_sDescription.FindPos(">", pos);

                                pos++;
                                if ( pos >= (int)m_sDescription.Length() )
                                        break;
                                char c = m_sDescription[pos];

                                if ( c != '<' )
                                        foundFirst = pos;
                        }

                        if ( foundFirst == -1 )
                                m_sDescription = "";
                        else
                        {
                                CyString firstStr = m_sDescription.Left(foundFirst);
                                firstStr.FindRemove("<br />");
                                firstStr.FindRemove("<br/>");
                                CyString lastStr = m_sDescription.Right(m_sDescription.Length() - foundFirst);

                                m_sDescription = firstStr + lastStr;
                        }
                }
        }
        else if ( first.Compare("UninstallAfter:") )
                AddUninstallAfterText(ParseInstallText(rest.token("|", 1)), rest.tokens("|", 2));
        else if ( first.Compare("UninstallBefore:") )
                AddUninstallBeforeText(ParseInstallText(rest.token("|", 1)), rest.tokens("|", 2));
        else if ( first.Compare("InstallAfter:") )
                AddInstallAfterText(ParseInstallText(rest.token("|", 1)), rest.tokens("|", 2));
        else if ( first.Compare("InstallBefore:") )
                AddInstallBeforeText(ParseInstallText(rest.token("|", 1)), rest.tokens("|", 2));
        else if ( first.Compare("ScriptName:") )
                AddLanguageName(ParseLanguage(rest.token(":", 1)), rest.token(":", 2));
        else if ( first.Compare("GameChanging:") )
                m_iGameChanging = rest;
        else if ( first.Compare("EaseOfUse:") )
                m_iEaseOfUse = rest;
        else if ( first.Compare("Recommended:") )
                m_iRecommended = rest;
        else if ( first.Compare("NeededLibrary:") )
                this->AddNeededLibrary(rest.token("||", 1), rest.token("||", 2), rest.token("||", 3));
        else if ( first.Compare("FakePatchBefore:") )
                this->AddFakePatchOrder(false, rest.token("||", 1), rest.token("||", 2));
        else if ( first.Compare("FakePatchAfter:") )
                this->AddFakePatchOrder(true, rest.token("||", 1), rest.token("||", 2));
        else if ( first.Compare("ForumLink:") )
                m_sForumLink = rest;
        else
                return false;

        return true;
}

int CBaseFile::ParseLanguage(CyString lang)
{
        int langID = lang.ToInt();

        if ( !langID )
        {
                lang = lang.ToLower();
                if ( lang == "english" )
                        return 44;
                else if ( lang == "default" )
                        return 0;
                else if ( lang == "german" )
                        return 49;
                else if ( lang == "russian" )
                        return 7;
                else if ( lang == "spanish" )
                        return 34;
                else if ( lang == "french" )
                        return 33;
        }

        return langID;
}


int CBaseFile::ParseInstallText(CyString lang)
{
        return this->ParseLanguage(lang);
}


/*
        Func:   ReadValues
        Input:  String - values in one long line
        Desc:   splits the values data into each line to read the data
*/
void CBaseFile::ReadValues ( CyString values )
{
        int num = 0;
        CyString *lines = values.SplitToken ( '\n', &num );

        for ( int i = 0; i < num; i++ )
                ParseValueLine ( lines[i].ToString() );

        CLEANSPLIT(lines, num)
}

/*
        Func:   ParseFilesLine
        Input:  String - single line from a file to set
        Return: Boolean - returns true if value exists
        Desc:   Reads the line and assigns the parameters for the file
*/
bool CBaseFile::ParseFilesLine ( CyString line )
{
        if ( !line.IsIn(":") )
                return false;

        CyString command = line.GetToken ( 1, ':' );

        long size = line.GetToken ( 2, ':').ToInt ();
        long usize = line.GetToken ( 3, ':').ToInt ();
        long compression = line.GetToken ( 4, ':').ToInt ();

        if ( command == "Icon" )
        {
                m_sIconExt = line.GetToken ( 5, ':' );
                m_pIconFile = new C_File ();
                m_pIconFile->SetDataSize ( size - 4 );
                m_pIconFile->SetDataCompression ( compression );
                m_pIconFile->SetUncompressedDataSize ( usize );

                return true;
        }

        time_t time = line.GetToken ( 5,':' ).ToLong();
        bool compressToFile = (line.GetToken ( 6, ':').ToInt() == 1) ? true : false;
        CyString name  = line.GetToken ( 7, ':' );
        CyString dir = line.GetToken ( 8, ':' );

        if ( name.Empty() )
                return true;

        bool shared = false;
        if ( command.Left(1) == "$" )
        {
                shared = true;
                command.Erase ( 0, 1 );
        }

        int type = -1;
        if ( command == "Script" )
                type = FILETYPE_SCRIPT;
        else if ( command == "Text" )
                type = FILETYPE_TEXT;
        else if ( command == "Readme" )
                type = FILETYPE_README;
        else if ( command == "Map" )
                type = FILETYPE_MAP;
        else if ( command == "Mod" )
                type = FILETYPE_MOD;
        else if ( command == "Uninstall" )
                type = FILETYPE_UNINSTALL;
        else if ( command == "Sound" )
                type = FILETYPE_SOUND;
        else if ( command == "Mission" )
                type = FILETYPE_MISSION;
        else if ( command == "Extra" )
                type = FILETYPE_EXTRA;
        else if ( command == "Screen" )
                type = FILETYPE_SCREEN;
        else if ( command == "Backup" )
                type = FILETYPE_BACKUP;
        else if ( command == "Advert" )
                type = FILETYPE_ADVERT;
        else if ( command == "ShipScene" )
                type = FILETYPE_SHIPSCENE;
        else if ( command == "CockpitScene" )
                type = FILETYPE_COCKPITSCENE;
        else if ( command == "ShipOther" )
                type = FILETYPE_SHIPOTHER;
        else if ( command == "ShipModel" )
                type = FILETYPE_SHIPMODEL;

        if ( type == -1 )
                return false;

        C_File *file = new C_File ();

        if ( dir.Left(5).Compare("GAME_") ) {
                file->SetGame(dir.GetToken("_", 2, 2).ToInt());
                dir = NullString;
        } 
        else if ( line.NumToken(":") >= 9 ) {
                file->SetGame(line.GetToken(":", 9, 9).GetToken("_", 2, 2).ToInt());
        }

        file->SetFileType ( type );
        file->SetCreationTime ( time );
        file->SetName ( name );
        file->SetDir ( dir );
        file->SetDataSize ( size - 4 );
        file->SetDataCompression ( compression );
        file->SetUncompressedDataSize ( usize );
        file->SetShared ( shared );
        file->SetCompressedToFile ( compressToFile );

        m_lFiles.push_back ( file );

        return true;
}


/*
        Func:   ParseFiles
        Input:  String - values in one long line
        Desc:   splits the files data into each line to read the data
*/
void CBaseFile::ReadFiles ( CyString values )
{
        int num = 0;
        CyString *lines = values.SplitToken ( '\n', &num );

        for ( int i = 0; i < num; i++ )
                ParseFilesLine ( lines[i] );

        CLEANSPLIT(lines, num)
}



/*
        Func:   ReadFile
        Input:  filename - the name of the file to open and read
                        readdata - If falses, dont read the files to memory, just read the headers and values
        Return: boolean - return ture if acceptable format
        Desc:   Opens and reads the spk file and loads all data into class
*/
bool CBaseFile::ReadFile ( CyString filename, int readtype, CProgressInfo *progress )
{
        FILE *id = fopen ( filename.c_str(), "rb" );
        if ( !id )
                return false;

        bool ret = ReadFile ( id, readtype, progress );
        if ( ret )
                m_sFilename = filename;

        fclose ( id );

        return ret;
}
bool CBaseFile::ReadFile ( FILE *id, int readtype, CProgressInfo *progress )
{
        ClearError ();

        // first read the header
        if ( !ParseHeader ( GetEndOfLine ( id, NULL, false ) ) )
                return false;

        if ( readtype == SPKREAD_HEADER )
                return true;

        // update the progress for each section
        int maxProgress = (readtype == SPKREAD_VALUES) ? 3 : 6;
        if ( readtype != SPKREAD_ALL && progress )
                progress->UpdateProgress(1, maxProgress);

        long doneLen = 0;
        // next read the data values for the spk files
        if ( m_SHeader.lValueCompressSize )
        {
                // read data to memory
                unsigned char *readData = new unsigned char[m_SHeader.lValueCompressSize];
                unsigned char size[4];
                fread ( size, 4, 1, id );
                fread ( readData, sizeof(unsigned char), m_SHeader.lValueCompressSize, id );
                unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];

                // check for zlib compression
                if ( m_SHeader.iValueCompression == SPKCOMPRESS_ZLIB )
                {
                        // uncomress the data
                        unsigned char *uncompr = new unsigned char[uncomprLen];

                        int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader.lValueCompressSize );
                        // update the progress for each section
                        if ( readtype != SPKREAD_ALL && progress )
                                progress->UpdateProgress(2, maxProgress);
                        if ( err == Z_OK )
                                ReadValues ( CyString ((char *)uncompr) );
                        doneLen = uncomprLen;
                        delete uncompr;
                }
                else if ( m_SHeader.iValueCompression == SPKCOMPRESS_7ZIP )
                {
                        long len = uncomprLen;
                        unsigned char *compr = LZMADecode_C ( readData, m_SHeader.lValueCompressSize, (size_t*)&len, NULL );
                        // update the progress for each section
                        if ( readtype != SPKREAD_ALL && progress )
                                progress->UpdateProgress(2, maxProgress);

                        if ( compr )
                                ReadValues ( CyString ((char *)compr) );
                }
                // no compression
                else
                        ReadValues ( CyString ((char *)readData) );

                delete readData;
        }

        // update the progress for each section
        if ( readtype != SPKREAD_ALL && progress )
                progress->UpdateProgress(3, maxProgress);

        if ( readtype == SPKREAD_VALUES )
                return true;

        // next should be the next header
        if ( !ParseFileHeader ( GetEndOfLine (id, NULL, false) ) )
                return false;

        // clear the current file list
        m_lFiles.clear(true);

        // update the progress for each section
        if ( readtype != SPKREAD_ALL && progress )
                progress->UpdateProgress(4, maxProgress);

        if ( m_SHeader2.lSize )
        {
                unsigned char *readData = new unsigned char[m_SHeader2.lSize];
                unsigned char size[4];
                fread ( size, 4, 1, id );
                fread ( readData, sizeof(char), m_SHeader2.lSize, id );

                unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];
                // check for zlib compression
                if ( m_SHeader.iValueCompression == SPKCOMPRESS_ZLIB )
                {

                        if ( uncomprLen < (unsigned long)doneLen )
                                uncomprLen = doneLen;

                        unsigned char *uncompr = new unsigned char[uncomprLen];
                        int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader2.lSize );
                        // update the progress for each section
                        if ( readtype != SPKREAD_ALL && progress )
                                progress->UpdateProgress(5, maxProgress);
                        if ( err == Z_OK )
                                ReadFiles ( CyString ((char *)uncompr) );
                        delete uncompr;
                }
                else if ( m_SHeader.iValueCompression == SPKCOMPRESS_7ZIP )
                {
                        long len = uncomprLen;
                        unsigned char *compr = LZMADecode_C ( readData, m_SHeader2.lSize, (size_t*)&len, NULL );
                        // update the progress for each section
                        if ( readtype != SPKREAD_ALL && progress )
                                progress->UpdateProgress(5, maxProgress);
                        if ( compr )
                                ReadFiles ( CyString ((char *)compr) );
                }
                else
                        ReadFiles ( CyString ((char *)readData) );

                delete readData;
        }

        // file mismatch
        long numfiles = m_lFiles.size();
        if ( m_pIconFile )
                ++numfiles;
        if ( m_SHeader2.iNumFiles != numfiles )
        {
                m_iLastError = SPKERR_FILEMISMATCH;
                return false;
        }

        // update the progress for each section
        if ( readtype != SPKREAD_ALL && progress )
                progress->UpdateProgress(6, maxProgress);

        if ( readtype == SPKREAD_ALL )
        {
                int fileCount = 2;
                if ( m_pIconFile )
                        m_pIconFile->ReadFromFile ( id, m_pIconFile->GetDataSize() );

                // ok finally we need to read all the files

                for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                {
                        node->Data()->ReadFromFile ( id, node->Data()->GetDataSize() );
                        if ( progress )
                                progress->UpdateProgress(fileCount++, m_lFiles.size() + 2);
                }

                m_bFullyLoaded = true;
        }

        return true;
}

bool CBaseFile::IsMod()
{
        // check for any mod files that are not fake patchs
        for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
        {
                C_File *f = fNode->Data();
                if ( f->GetFileType() != FILETYPE_MOD )
                        continue;

                if ( !f->IsFakePatch() )
                        return true;
        }

        return false;
}

bool CBaseFile::IsFakePatch() const
{
        // check for any mod files that are not fake patchs
        for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
        {
                C_File *f = fNode->Data();
                if ( f->GetFileType() != FILETYPE_MOD )
                        continue;

                if ( f->IsFakePatch() )
                        return true;
        }

        return false;
}

Utils::String CBaseFile::CreateValuesLine () const
{
        Utils::String values("Name: ");
        values += Utils::String(m_sName.ToString()) + "\n";
        values += Utils::String("Author: ") + m_sAuthor.ToString() + "\n";
        values += Utils::String("Version: ") + m_sVersion.ToString() + "\n";
        if ( !m_sCreationDate.Empty() )
                values += Utils::String("Date: ") + m_sCreationDate.ToString() + "\n";
        if ( !m_sWebAddress.Empty() )
                values += Utils::String("WebAddress: ") + m_sWebAddress.ToString() + "\n";
        if ( !m_sWebSite.Empty() )
                values += Utils::String("WebSite: ") + m_sWebSite.ToString() + "\n";
        if ( !m_sEmail.Empty() )
                values += Utils::String("Email: ") + m_sEmail.ToString() + "\n";
        for ( SStringList *str = m_lMirrors.Head(); str; str = str->next )
                values += Utils::String("WebMirror: ") + str->str.ToString() + "\n";
        if ( !m_sDescription.Empty() )
        {
                Utils::String desc = m_sDescription.ToString();
                desc = desc.findReplace("<newline>", "<br>");
                desc = desc.findReplace("\n", "<br>");
                desc.remove('\r');
                values += "Desc: " + desc + "\n";
        }

        for ( CListNode<SGameCompat> *gc = m_lGames.Front(); gc; gc = gc->next() ) {
                if ( !gc->Data()->sVersion.Empty() )
                        values += Utils::String("GameCompatExact: ") + (long)gc->Data()->iGame + " " + gc->Data()->sVersion.ToString() + "\n";
                else
                        values += Utils::String("GameCompat: ") + (long)gc->Data()->iGame + " " + (long)gc->Data()->iVersion + "\n";
        }
        if ( !m_sForumLink.Empty() )
                values += Utils::String("ForumLink: ") + m_sForumLink.ToString() + "\n";

        if ( m_bSigned )
                values += "Signed\n";

        CListNode<SInstallText> *it;
        for ( it = m_lUninstallText.Front(); it; it = it->next() ) {
                if ( !it->Data()->sAfter.Empty() )
                        values += (CyString("UninstallAfter: ") + CyString::Number(it->Data()->iLanguage) + "|" + it->Data()->sAfter + "\n").ToString();
                if ( !it->Data()->sBefore.Empty() )
                        values += (CyString("UninstallBefore: ") + CyString::Number(it->Data()->iLanguage) + "|" + it->Data()->sBefore + "\n").ToString();
        }
        for ( it = m_lInstallText.Front(); it; it = it->next() ) {
                if ( !it->Data()->sAfter.Empty() )
                        values += (CyString("InstallAfter: ") + CyString::Number(it->Data()->iLanguage) + "|" + it->Data()->sAfter + "\n").ToString();
                if ( !it->Data()->sBefore.Empty() )
                        values += (CyString("InstallBefore: ") + CyString::Number(it->Data()->iLanguage) + "|" + it->Data()->sBefore + "\n").ToString();
        }

        values += Utils::String("GameChanging: ") + (long)m_iGameChanging + "\n";
        values += Utils::String("EaseOfUse: ") + (long)m_iEaseOfUse + "\n";
        values += Utils::String("Recommended: ") + (long)m_iRecommended + "\n";

        for ( CListNode<SNames> *nNode = m_lNames.Front(); nNode; nNode = nNode->next() )
                values += Utils::String("ScriptName: ") + (long)nNode->Data()->iLanguage + ":" + nNode->Data()->sName.ToString() + "\n";

        for ( CListNode<SNeededLibrary> *libNode = m_lNeededLibrarys.Front(); libNode; libNode = libNode->next() ) {
                SNeededLibrary *l = libNode->Data();
                values += (CyString("NeededLibrary: ") + l->sName + "||" + l->sAuthor + "||" + l->sMinVersion + "\n").ToString();
        }

        for ( SStringList *fpbNode = m_lFakePatchBefore.Head(); fpbNode; fpbNode = fpbNode->next )
                values += (CyString("FakePatchBefore: ") + fpbNode->str + "||" + fpbNode->data + "\n").ToString();
        for ( SStringList *fpaNode = m_lFakePatchAfter.Head(); fpaNode; fpaNode = fpaNode->next )
                values += (CyString("FakePatchAfter: ") + fpaNode->str + "||" + fpaNode->data + "\n").ToString();

        values += Utils::String("PluginType: ") + (long)m_iPluginType + "\n";

        return values;
}


/*
        Func:   WriteFile
        Input:  filename - The filename of the spk file to write to
        Desc:   Writes the data to an spk file
*/
bool CBaseFile::WriteFile ( CyString filename, CProgressInfo *progress )
{
        FILE *id = fopen ( filename.c_str(), "wb" );
        if ( !id )
                return false;

        bool ret = WriteData ( id, progress );
        fclose ( id );

        return ret;
}

bool CBaseFile::WriteHeader(FILE *id, int valueheader, int valueComprLen)
{
        fprintf ( id, "BaseCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen );
        if ( ferror(id) )
                return false;
        return true;
}

bool CBaseFile::WriteData ( FILE *id, CProgressInfo *progress )
{
        int valueheader = m_SHeader.iValueCompression, fileheader = m_SHeader.iValueCompression;
        if ( valueheader == SPKCOMPRESS_7ZIP )
                valueheader = SPKCOMPRESS_ZLIB;
        if ( fileheader == SPKCOMPRESS_7ZIP )
                fileheader = SPKCOMPRESS_ZLIB;

        // get the script values
        this->UpdateSigned(true);
        CyString values = this->CreateValuesLine();

        // compress the values
        int valueUncomprLen = (int)values.Length();
        unsigned long valueComprLen = 0;
        unsigned char *valueCompr = NULL;
        bool compressed = false;
        if ( valueheader == SPKCOMPRESS_ZLIB )
        {
                valueComprLen = valueUncomprLen;
                if ( valueComprLen < 100 )
                        valueComprLen = 200;
                else if ( valueComprLen < 1000 )
                        valueComprLen *= 2;

                valueCompr = (unsigned char *)calloc((unsigned int)valueComprLen, 1);
                int err = compress ( (unsigned char *)valueCompr, &valueComprLen, (const unsigned char *)values.c_str(), (unsigned long)values.Length(), 0 );
                if ( err == Z_OK )
                        compressed = true;
        }

        if ( !compressed )
        {
                valueComprLen = valueUncomprLen;
                valueCompr = (unsigned char *)calloc((unsigned int)valueComprLen, 1);
                memcpy ( valueCompr, values.c_str(), valueComprLen );
                valueheader = SPKCOMPRESS_NONE;
        }

        // write the main header to the file
        if ( !this->WriteHeader(id, valueheader, valueComprLen) )
                return false;

        // write the compressed data to file
        fputc ( (unsigned char)(valueUncomprLen >> 24), id );
        fputc ( (unsigned char)(valueUncomprLen >> 16), id );
        fputc ( (unsigned char)(valueUncomprLen >> 8), id );
        fputc ( (unsigned char)valueUncomprLen, id );
        fwrite ( valueCompr, sizeof(char), valueComprLen, id );

        free ( valueCompr );

        // now compress the files header
        // create the files values
        CyString files = CreateFilesLine ( true, progress );

        // compress the files values
        long fileUncomprLen = (long)files.Length(), fileComprLen = fileUncomprLen;
        unsigned char *fileCompr = NULL;

        compressed = false;
        if ( fileUncomprLen )
        {
                if ( fileheader == SPKCOMPRESS_ZLIB )
                {
                        if ( fileComprLen < 100 )
                                fileComprLen = 200;
                        else if ( fileComprLen < 1000 )
                                fileComprLen *= 2;
                        fileCompr = (unsigned char *)calloc((unsigned int)fileComprLen, 1);
                        int err = compress ( (unsigned char *)fileCompr, (unsigned long *)&fileComprLen, (const unsigned char *)files.c_str(), (unsigned long)files.Length(), 0 );
                        if ( err == Z_OK )
                                compressed = true;
                }
        }

        // if unable to compress, store it as plain text
        if ( !compressed )
        {
                fileComprLen = fileUncomprLen;
                fileCompr = (unsigned char *)calloc((unsigned int)fileComprLen, 1);
                memcpy ( fileCompr, files.c_str(), fileComprLen );
                fileheader = SPKCOMPRESS_NONE;
        }

        // now write the file header
        m_SHeader2.lSize = fileComprLen;
        fprintf ( id, "FileHeader;%d;%ld;%ld;%d;%d\n", m_SHeader2.iNumFiles, m_SHeader2.lSize, m_SHeader2.lFullSize, fileheader, m_SHeader2.iDataCompression );

        fputc ( (unsigned char)(fileUncomprLen >> 24), id );
        fputc ( (unsigned char)(fileUncomprLen >> 16), id );
        fputc ( (unsigned char)(fileUncomprLen >> 8), id );
        fputc ( (unsigned char)fileUncomprLen, id );
        fwrite ( fileCompr, sizeof(char), fileComprLen, id );

        free ( fileCompr );

        if ( progress )
        {
                progress->UpdateStatus(STATUS_WRITE);
                progress->SetDone(0);
                long max = 0;
                for ( C_File *file = m_lFiles.First(); file; file = m_lFiles.Next() )
                        max += file->GetDataSize();
                if ( m_pIconFile )
                        max += m_pIconFile->GetDataSize();
                progress->SetMax(max);
        }

        // now finally, write all the file data
        if ( m_pIconFile )
        {
                if ( progress )
                        progress->UpdateFile(m_pIconFile);
                fputc ( (unsigned char)(m_pIconFile->GetUncompressedDataSize() >> 24), id );
                fputc ( (unsigned char)(m_pIconFile->GetUncompressedDataSize() >> 16), id );
                fputc ( (unsigned char)(m_pIconFile->GetUncompressedDataSize() >> 8), id );
                fputc ( (unsigned char)m_pIconFile->GetUncompressedDataSize(), id );
                fwrite ( m_pIconFile->GetData(), sizeof(char), m_pIconFile->GetDataSize(), id );
                if ( progress )
                        progress->IncDone(m_pIconFile->GetDataSize());
        }

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( progress )
                        progress->UpdateFile(file);
                fputc ( (unsigned char)(file->GetUncompressedDataSize() >> 24), id );
                fputc ( (unsigned char)(file->GetUncompressedDataSize() >> 16), id );
                fputc ( (unsigned char)(file->GetUncompressedDataSize() >> 8), id );
                fputc ( (unsigned char)file->GetUncompressedDataSize(), id );
                unsigned char *data = file->GetData();
                size_t remaining = file->GetDataSize();
                while ( remaining )
                {
                        size_t writeSize = WRITECHUNK;
                        if ( writeSize > remaining )
                                writeSize = remaining;

                        size_t written = fwrite ( data, sizeof(char), writeSize, id );
                        data += written;
                        remaining -= written;

                        if ( progress )
                                progress->IncDone((int)written);
                }

        }

        m_bChanged = false;

        return true;
}





bool CBaseFile::ExtractFile ( C_File *file, CyString dir, bool includedir, CProgressInfo *progress )
{
        if ( ReadFileToMemory ( file ) )
        {
                // now finally, uncompress the file
                long len = 0;
                unsigned char *data = file->UncompressData ( &len, progress );
                if ( !data )
                {
                        // attempt a file decompress
                        if ( file->GetCompressionType() == SPKCOMPRESS_7ZIP )
                        {
                                if ( file->UncompressToFile ( dir, this, includedir, progress ) )
                                        return true;
                        }
                        return false;
                }

                if ( !file->WriteToDir ( dir, this, includedir, NullString, data, len ) )
                        return false;

                return true;

        }
        else
                return false;
}

bool CBaseFile::ExtractFile ( int filenum, CyString dir, bool includedir, CProgressInfo *progress )
{
        // invalid valus
        if ( filenum < 0 )
                return false;
        // out of range
        if ( filenum > m_lFiles.size() )
                return false;

        // get the file pointer
        C_File *file = m_lFiles.Get ( filenum );
        return ExtractFile ( file, dir, includedir, progress );
}



bool CBaseFile::ExtractAll ( CyString dir, int game, bool includedir, CProgressInfo *progress )
{
        // no file to read from
        if ( m_sFilename.Empty() )
                return false;

        // now open the file
        FILE *id = fopen ( m_sFilename.c_str(), "rb" );
        if ( !id )
                return false;

        fseek ( id, 0, SEEK_SET );
        // read the header
        GetEndOfLine ( id, NULL, false );
        // skip past values
        fseek ( id, m_SHeader.lValueCompressSize, SEEK_CUR );

        // read the next header
        GetEndOfLine ( id, NULL, false );
        // skip past files
        fseek ( id, 4, SEEK_CUR );
        fseek ( id, m_SHeader2.lSize, SEEK_CUR );

        // now were in the file section
        // skip past each one
        if ( m_pIconFile )
        {
                fseek ( id, 4, SEEK_CUR );
                fseek ( id, m_pIconFile->GetDataSize (), SEEK_CUR );
        }

        CDirIO Dir(dir);

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *fit = node->Data();

                if ( progress )
                        progress->UpdateFile ( fit );

                if ( (!fit->GetDataSize ()) || (!fit->GetData()) )
                {
                        if ( !fit->ReadFromFile ( id, fit->GetDataSize() ) )
                        {
                                fclose ( id );
                                return false;
                        }
                }
                else
                        fseek ( id, fit->GetDataSize(), SEEK_CUR );

                if ( game ) {
                        if ( fit->GetGame() && fit->GetGame() != game )
                                continue;
                }

                // create directory first
                Dir.Create(fit->GetDirectory(this));

                long size = 0;
                unsigned char *data = fit->UncompressData (&size, progress);
                if ( (!data) && (fit->GetCompressionType() == SPKCOMPRESS_7ZIP) )
                {
                        if ( !fit->UncompressToFile ( dir, this, includedir, progress ) )
                        {
                                fclose ( id );
                                return false;
                        }
                }
                else if ( (!data) || (!fit->WriteToDir ( dir, this, includedir, NullString, data, size )) )
                {
                        fclose ( id );
                        return false;
                }
        }

        fclose ( id );

        return true;
}




bool CBaseFile::UpdateSigned (bool updateFiles)
{
        m_bSigned = true;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( updateFiles )
                        file->UpdateSigned();
                // extra, text, soundtrack, readmes and screen files do not require a modified game directly
                if ( (file->GetFileType() == FILETYPE_EXTRA) || (file->GetFileType() == FILETYPE_TEXT) || (file->GetFileType() == FILETYPE_SOUND) || (file->GetFileType() == FILETYPE_SCREEN) || (file->GetFileType() == FILETYPE_README) )
                        continue;
                // mods and maps always need modified game
                else if ( (file->GetFileType() == FILETYPE_MOD) || (file->GetFileType() == FILETYPE_MAP) )
                {
                        m_bSigned = false;
                        break;
                }

                // else should be a script file, script or uninstall type
                // all scripts must be signed, if any are not, then no signed status
                if ( !file->IsSigned () )
                {
                        m_bSigned = false;
                        break;
                }
        }

        return m_bSigned;
}


bool CBaseFile::IsPackageNeeded(CyString scriptName, CyString author)
{
        for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )
        {
                SNeededLibrary *l = node->Data();
                if ( l->sName.Compare(scriptName) && l->sAuthor.Compare(author) )
                        return true;
        }

        return false;
}

SNeededLibrary *CBaseFile::FindPackageNeeded(CyString scriptName, CyString author)
{
        for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )
        {
                SNeededLibrary *l = node->Data();
                if ( l->sName.Compare(scriptName) && l->sAuthor.Compare(author) )
                        return l;
        }

        return NULL;
}

void CBaseFile::RemoveFakePatchOrder(CyString scriptName, CyString author)
{
        RemoveFakePatchOrder(true, scriptName, author);
        RemoveFakePatchOrder(false, scriptName, author);
}
void CBaseFile::RemoveFakePatchOrder(bool after, CyString scriptName, CyString author)
{
        CyStringList *list;
        if ( after )
                list = &m_lFakePatchAfter;
        else
                list = &m_lFakePatchBefore;

        for ( SStringList *str = list->Head(); str; str = str->next )
        {
                // already added
                if ( str->str.Compare(scriptName) && str->data.Compare(author) )
                        str->remove = true;
        }

        list->RemoveMarked();
}

void CBaseFile::AddFakePatchOrder(bool after, CyString scriptName, CyString author)
{
        CyStringList *list;
        if ( after )
                list = &m_lFakePatchAfter;
        else
                list = &m_lFakePatchBefore;

        // check if the package already exists
        for ( SStringList *str = list->Head(); str; str = str->next )
        {
                // already added
                if ( str->str.Compare(scriptName) && str->data.Compare(author) )
                        return;
        }

        // cant have it on both list
        RemoveFakePatchOrder(!after, scriptName, author);

        list->PushBack(scriptName, author);
}
void CBaseFile::AddNeededLibrary(CyString scriptName, CyString author, CyString minVersion)
{
        SNeededLibrary *l = this->FindPackageNeeded(scriptName, author);
        if ( !l )
        {
                l = new SNeededLibrary;
                l->sName = scriptName;
                l->sAuthor = author;
                m_lNeededLibrarys.push_back(l);
        }
        l->sMinVersion = minVersion;
}

void CBaseFile::RemovePackageNeeded(CyString scriptName, CyString author)
{
        SNeededLibrary *l = this->FindPackageNeeded(scriptName, author);
        if ( l )
        {
                m_lNeededLibrarys.remove(l);
                delete l;
        }
}

void CBaseFile::ClearNeededPackages()
{
        SNeededLibrary *ns = this->FindPackageNeeded("<package>", "<author>");
        CyString version;
        if ( ns )
                version = ns->sMinVersion;
        for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )
                node->DeleteData();
        m_lNeededLibrarys.clear();

        if ( !version.Empty() )
                this->AddNeededLibrary("<package>", "<author>", version);
}


bool CBaseFile::GeneratePackagerScript(bool wildcard, CyStringList *list, bool datafile)
{
        list->PushBack("#");
        list->PushBack(CyString("# Packager Script"));
        list->PushBack(CyString("# -- Generated by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2) + " --");
        list->PushBack("#");
        list->PushBack("");
        if ( !datafile )
        {
                list->PushBack("# Variable for your game directory, where to get files from");
                list->PushBack("# $PATH variable is used to get the current path");
                list->PushBack("Variable: $GAMEDIR $PATH");
                list->PushBack("");
        }
        list->PushBack("# The name of the script");
        list->PushBack(CyString("Name: ") + m_sName);
        list->PushBack("");
        list->PushBack("# The author of the script, ie, you");
        list->PushBack(CyString("Author: ") + m_sAuthor);
        list->PushBack("");
        list->PushBack("# The creation data, when it was created");
        if ( datafile )
                list->PushBack(CyString("Date: ") + m_sCreationDate);
        else
        {
                list->PushBack("# $DATE variable is used to get the current date");
                list->PushBack("Date: $DATE");
        }
        list->PushBack("");
        list->PushBack("# The version of script");
        if ( datafile )
                list->PushBack(CyString("Version: ") + m_sVersion);
        else
        {
                list->PushBack("# $ASK variable is used to get an input when creating");
                list->PushBack("Version: $ASK");
        }
        list->PushBack("");

        if ( !m_lGames.empty() ) {
                list->PushBack("# The game version the script is for <game> <version> (can have multiple games)");
                for ( SGameCompat *g = m_lGames.First(); g; g = m_lGames.Next() ) {
                        Utils::String game = CBaseFile::ConvertGameToString(g->iGame);

                        if ( !g->sVersion.Empty() )
                        {
                                game += " ";
                                game += g->sVersion.ToString();
                        }
                        else
                        {
                                game += " ";
                                game += (long)g->iVersion;
                        }

                        list->PushBack(CyString("Game: ") + game);
                }

                list->PushBack("");
        }

        if ( !m_sDescription.Empty() )
        {
                list->PushBack("# The description of the script, displays when installing");
                list->PushBack(CyString("Description: ") + m_sDescription);
                list->PushBack("");
        }

        if ( !m_sWebSite.Empty() )
        {
                list->PushBack("# A link to the website for the script, ie for an online help page");
                list->PushBack(CyString("WebSite: ") + m_sWebSite);
                list->PushBack("");
        }

        if ( !m_sForumLink.Empty() )
        {
                list->PushBack("# A direct link to the thread in the egosoft forum");
                list->PushBack(CyString("ForumLink: ") + m_sForumLink);
                list->PushBack("");
        }

        if ( !m_sWebAddress.Empty() )
        {
                list->PushBack("# A link to the address for the update file");
                list->PushBack(CyString("WebAddress: ") + m_sWebAddress);
                list->PushBack("");
        }

        if ( !m_sEmail.Empty() )
        {
                list->PushBack("# The email address of the author, to allow users to contract if needed");
                list->PushBack(CyString("Email: ") + m_sEmail);
                list->PushBack("");
        }

        if ( m_lMirrors.Count() )
        {
                list->PushBack("# A link to the mirror address for the update file, can have many of these");
                for ( SStringList *node = m_lMirrors.Head(); node; node = node->next )
                        list->PushBack(CyString("WebMirror: ") + node->str);
                list->PushBack("");
        }

        if ( m_bAutoGenerateUpdateFile )
        {
                list->PushBack("# Auto generate the package update file when created");
                list->PushBack("GenerateUpdateFile");
        }

        if ( m_lNeededLibrarys.size() )
        {
                list->PushBack("# Needed Library dependacies, require these to be installed");
                for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )
                        list->PushBack(CyString("Depend: ") + node->Data()->sName + "|" + node->Data()->sMinVersion + "|" + node->Data()->sAuthor);

                list->PushBack("");
        }

        if ( m_iEaseOfUse != -1 && m_iRecommended != -1 && m_iGameChanging != -1 )
        {
                list->PushBack("# Ratings Values, 0 to 5, <ease> <changing> <recommended>");
                list->PushBack(CyString("Ratings: ") + (long)m_iEaseOfUse + " " + (long)m_iGameChanging + " " + (long)m_iRecommended);
                list->PushBack("");
        }

        if ( m_lNames.size() )
        {
                list->PushBack("# Package names, uses different names for different languages");
                for ( CListNode<SNames> *node = m_lNames.Front(); node; node = node->next() )
                        list->PushBack(CyString("ScriptName: ") + (long)node->Data()->iLanguage + " " + node->Data()->sName);
                list->PushBack("");
        }

        if ( m_lInstallText.size() )
        {
                list->PushBack("# Install Texts, display text before and/or after installing to inform the use of special conditions");
                for ( CListNode<SInstallText> *node = m_lInstallText.Front(); node; node = node->next() )
                {
                        if ( !node->Data()->sBefore.Empty() )
                                list->PushBack(CyString("InstallBefore: ") + (long)node->Data()->iLanguage + " " + node->Data()->sBefore);
                        if ( !node->Data()->sAfter.Empty() )
                                list->PushBack(CyString("InstallAfter: ") + (long)node->Data()->iLanguage + " " + node->Data()->sAfter);
                }
                list->PushBack("");
        }

        if ( m_lUninstallText.size() )
        {
                list->PushBack("# Uninstall Texts, display text before and/or after uninstalling to inform the use of special conditions");
                for ( CListNode<SInstallText> *node = m_lUninstallText.Front(); node; node = node->next() )
                {
                        if ( !node->Data()->sBefore.Empty() )
                                list->PushBack(CyString("UninstallBefore: ") + (long)node->Data()->iLanguage + " " + node->Data()->sBefore);
                        if ( !node->Data()->sAfter.Empty() )
                                list->PushBack(CyString("UninstallAfter: ") + (long)node->Data()->iLanguage + " " + node->Data()->sAfter);
                }
                list->PushBack("");
        }

        list->PushBack("# Plugin Type, the type the plugin is, mainly used to show users the type, types include: Normal, Stable, Experimental, Cheat, Mod");
        switch ( this->GetPluginType() )
        {
                case PLUGIN_NORMAL:
                        list->PushBack("PluginType: Normal");
                        break;
                case PLUGIN_STABLE:
                        list->PushBack("PluginType: Stable");
                        break;
                case PLUGIN_EXPERIMENTAL:
                        list->PushBack("PluginType: Experimental");
                        break;
                case PLUGIN_CHEAT:
                        list->PushBack("PluginType: Cheat");
                        break;
                case PLUGIN_MOD:
                        list->PushBack("PluginType: Mod");
                        break;
        }
        list->PushBack("");

        return true;
}

bool CBaseFile::GeneratePackagerScriptFile(bool wildcard, CyStringList *list)
{
        // now do files and wildcards
        CyStringList files;
        for ( CListNode<C_File> *f = m_lFiles.Front(); f; f = f->next() )
        {
                CyString name = "$GAMEDIR/";

                bool done = false;
                if ( wildcard )
                {
                        CyString base = f->Data()->GetBaseName();
                        if ( f->Data()->GetFileType() == FILETYPE_SCRIPT )
                        {
                                if ( base.GetToken(".", 1, 1).Compare("plugin") || base.GetToken(".", 1, 1).Compare("lib") )
                                {
                                        name += f->Data()->GetDirectory(this) + "/" + base.GetToken(".", 1, 2) + ".*";
                                        done = true;
                                }
                                else if ( base.GetToken(".", 1, 1).Compare("al") && !base.GetToken(".", 2, 2).Compare("plugin") )
                                {
                                        name += f->Data()->GetDirectory(this) + "/" + base.GetToken(".", 1, 2) + ".*";
                                        done = true;
                                }
                        }
                        else if ( f->Data()->GetFileType() == FILETYPE_TEXT )
                        {
                                if ( base.IsIn("-L") )
                                {
                                        name += f->Data()->GetDirectory(this) + "/" + base.GetToken("-L", 1, 1) + "-L*";
                                        done = true;
                                }
                                else
                                {
                                        name += f->Data()->GetDirectory(this) + "/*" + base.Right(4) + ".*";
                                        done = true;
                                }
                        }
                }

                if ( !done )
                        name += f->Data()->GetNameDirectory(this);

                if ( !f->Data()->GetDir().Empty() )
                {
                        name += "|";
                        name += f->Data()->GetDir();
                }
                files.PushBack(CyString("GAME ") + CBaseFile::ConvertGameToString(f->Data()->GetGame()) + " " + name, f->Data()->GetFileTypeString(), true);
        }


        if ( !files.Empty() )
        {
                list->PushBack("# Files List, all the files to add, can include wild cards");
                for ( SStringList *node = files.Head(); node; node = node->next )
                        list->PushBack(node->data + ": " + node->str);
                list->PushBack("");
        }

        return true;
}

CyString CBaseFile::GetAutosaveName()
{
        CyString cdate = m_sCreationDate;
        cdate = cdate.FindReplace("/", ".");
        CyString name = m_sName + "-V" + m_sVersion + "-" + cdate;

        return name;
}

bool CBaseFile::CheckGameCompatability(int game)
{
        if ( m_lGames.empty() )
                return true; // no game compatability added, assume its ok for all

        for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {
                if ( node->Data()->iGame == 0 || node->Data()->iGame == game )
                        return true;
        }
        return false;
}

bool CBaseFile::CheckGameVersionCompatability(int game, CyString sVersion, int iVersion)
{
        if ( m_lGames.empty() )
                return true; // no game compatability added, assume its ok for all

        bool foundAll = false;
        for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {
                if ( node->Data()->iGame == 0 )
                        foundAll = true;
                else if ( node->Data()->iGame == game ) { // now check the version
                        if ( node->Data()->sVersion.Empty() ) {
                                if ( node->Data()->sVersion.CompareVersion(sVersion) == COMPARE_OLDER )
                                        return false;
                                return true;
                        }
                        else {
                                if ( node->Data()->iVersion > iVersion )
                                        return false;
                                return true;
                        }
                }
        }
        return foundAll;
}

bool CBaseFile::RemoveGameCompatability(int game)
{
        for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {
                if ( node->Data()->iGame == game ) {
                        m_lGames.remove(node);
                        m_bChanged = true;
                        return true;
                }
        }

        return false;
}

SGameCompat *CBaseFile::GetGameCompatability(int game)
{
        for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {
                if ( node->Data()->iGame == game ) {
                        return node->Data();
                }
        }

        return NULL;
}

void CBaseFile::AddGameCompatability(int game, CyString version)
{
        // first check if we already have it on the list
        SGameCompat *Found = this->GetGameCompatability(game);
        if ( !Found ) {
                Found = new SGameCompat;
                m_lGames.push_back(Found);
        }

        Found->iGame = game;
        Found->iVersion = -1;
        Found->sVersion = NullString;

        if ( version.IsIn(".") || !version.IsNumber() )
                Found->sVersion = version;
        else
                Found->iVersion = version.ToInt();
        m_bChanged = true;
}

bool CBaseFile::LoadPackageData(const Utils::String &sFirst, const Utils::String &sRest)
{
        if ( sFirst.Compare("Name") )
                m_sName = sRest;
        else if ( sFirst.Compare("Author") )
                m_sAuthor = sRest;
        else if ( sFirst.Compare("ScriptName") )
                AddLanguageName(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));
        else if ( sFirst.Compare("UninstallBefore") )
                this->AddUninstallBeforeText(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));
        else if ( sFirst.Compare("UninstallAfter") )
                this->AddUninstallAfterText(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));
        else if ( sFirst.Compare("InstallBefore") )
                this->AddInstallBeforeText(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));
        else if ( sFirst.Compare("InstallAfter") )
                this->AddInstallAfterText(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));
        else if ( sFirst.Compare("Date") )
                m_sCreationDate = sRest;
        else if ( sFirst.Compare("Version") )
                m_sVersion = sRest;
        // old version
        else if ( sFirst.Compare("GameVersion") )
                this->AddGameCompatability(-1, sRest);
        else if ( sFirst.Compare("PluginType") )
        {
                if ( sRest.isNumber() )
                        this->SetPluginType(sRest);
                else if ( sRest.Compare("Normal") )
                        this->SetPluginType(PLUGIN_NORMAL);
                else if ( sRest.Compare("Stable") )
                        this->SetPluginType(PLUGIN_STABLE);
                else if ( sRest.Compare("Experimental") )
                        this->SetPluginType(PLUGIN_EXPERIMENTAL);
                else if ( sRest.Compare("Cheat") )
                        this->SetPluginType(PLUGIN_CHEAT);
                else if ( sRest.Compare("Mod") )
                        this->SetPluginType(PLUGIN_MOD);
        }
        // new version
        else if ( sFirst.Compare("GenerateUpdateFile") )
                m_bAutoGenerateUpdateFile = true;
        else if ( sFirst.Compare("Game") )
        {
                Utils::String sGame = sRest.token(" ", 1);
                this->AddGameCompatability(CBaseFile::GetGameFromString(sGame), sRest.token(" ", 2));
        }
        else if ( sFirst.Compare("Description") )
                m_sDescription = sRest;
        else if ( sFirst.Compare("AutoSave") || sFirst.Compare("AutoExport") || sFirst.Compare("AutoRarExport") || sFirst.Compare("AutoZipExport") )
        {
                CyString filename = sRest;
                CyString name = m_sName;
                CyString author = m_sAuthor;
                CyString cdate = m_sCreationDate;
                cdate = cdate.FindReplace("/", ".").Remove(" ");
                if ( filename.IsIn("$AUTOSAVE") )
                {
                        if ( m_iType == TYPE_XSP )
                                filename.FindReplace("$AUTOSAVE", "$NAME-V$VERSION-$CDATE.xsp");
                        else
                                filename.FindReplace("$AUTOSAVE", "$NAME-V$VERSION-$CDATE.spk");
                }
                if ( filename.IsIn("$NAME") )
                        filename.FindReplace("$NAME", name.Remove(" "));
                if ( filename.IsIn("$AUTHOR") )
                        filename.FindReplace("$AUTHOR", author.Remove(" "));
                if ( filename.IsIn("$DATE") )
                        filename.FindReplace("$DATE", cdate);
                if ( filename.IsIn("$CDATE") )
                        filename.FindReplace("$CDATE", cdate);
                if ( filename.IsIn("$VERSION") )
                        filename.FindReplace("$VERSION", m_sVersion);

                if ( sFirst.Compare("AutoZipExport") || sFirst.Compare("AutoExport") )
                        m_sExportFilename = CFileIO(filename).ChangeFileExtension("zip");
                else if ( sFirst.Compare("AutoRarExport") )
                        m_sExportFilename = CFileIO(filename).ChangeFileExtension("rar");
                else
                        m_sFilename = filename;
        }
        else if ( sFirst.Compare("WebSite") )
                m_sWebSite = sRest;
        else if ( sFirst.Compare("ForumLink") || sFirst.Compare("Forum") )
                m_sForumLink = sRest;
        else if ( sFirst.Compare("Email") )
                m_sEmail = sRest;
        else if ( sFirst.Compare("WebAddress") )
                m_sWebAddress = sRest;
        else if ( sFirst.Compare("WebMirror") )
                this->AddWebMirror(sRest);
        else if ( sFirst.Compare("WebMirror1") )
                this->AddWebMirror(sRest);
        else if ( sFirst.Compare("WebMirror2") )
                this->AddWebMirror(sRest);
        else if ( sFirst.Compare("Ftp") )
                m_sFtpAddr = sRest;
        else if ( sFirst.Compare("Ratings") )
        {
                m_iEaseOfUse = sRest.token(" ", 1);
                m_iGameChanging = sRest.token(" ", 2);
                m_iRecommended = sRest.token(" ", 3);
        }
        else if ( sFirst.Compare("EaseOfUse") )
                m_iEaseOfUse = sRest;
        else if ( sFirst.Compare("GameChanging") )
                m_iGameChanging = sRest;
        else if ( sFirst.Compare("Recommended") )
                m_iRecommended = sRest;
        else if ( sFirst.Compare("Depend") )
        {
                CyString version = sRest.token("|", 2);
                CyString name = sRest.token("|", 1);
                CyString author = sRest.tokens("|", 3);

                this->AddNeededLibrary(name, author, version);
        }
        else if ( sFirst.Compare("DependPackage") )
        {
                CPackages p;
                CBaseFile *spk =  p.OpenPackage(sRest, 0, 0, SPKREAD_VALUES);
                if ( spk )
                {
                        this->AddNeededLibrary(spk->GetName(), spk->GetAuthor(), spk->GetVersion());
                        delete spk;
                }
        }
        else if ( sFirst.Compare("Icon") )
        {
                C_File *icon = new C_File(sRest.c_str());
                if ( icon->ReadFromFile() )
                        this->SetIcon(icon, CFileIO(sRest).GetFileExtension());
        }
        else
        {
                Utils::String checkType = sFirst;
                bool shared = false;
                if ( checkType.left(6).Compare("Shared") )
                {
                        checkType = sFirst.right(-6);
                        shared = true;
                }

                // now check type name
                int filetype = GetFileTypeFromString(checkType);
                if ( filetype != -1 )
                        this->AddFileScript(filetype, shared, sRest);
                else if ( !checkType.Compare("changelog") )
                        return false;
        }

        return true;
}

void CBaseFile::AddFileScript(int filetype, bool shared, CyString rest)
{
        CyString dir;
        if ( rest.IsIn("|") )
        {
                dir = rest.GetToken("|", 2);
                rest = rest.GetToken("|", 1, 1);
        }

        int game = 0;
        if ( rest.GetToken(" ", 1, 1).Left(4).Compare("GAME") ) {
                game = CBaseFile::GetGameFromString(rest.GetToken(" ", 2, 2).ToString());
                rest = rest.GetToken(" ", 3);
        }

        rest = rest.FindReplace("\\", "/");

        // wild cards
        if ( rest.IsAnyIn("*?") )
        {
                CDirIO Dir(CFileIO(rest).GetDir());
                CyStringList *dirList = Dir.DirList();
                if ( dirList )
                {
                        for ( SStringList *strNode = dirList->Head(); strNode; strNode = strNode->next )
                        {
                                CyString file = Dir.File(strNode->str);
                                if ( file.WildMatch(rest) )
                                {
                                        C_File *newfile = this->AppendFile(file, filetype, game, dir);
                                        if ( newfile )
                                                newfile->SetShared(shared);
                                }
                        }
                        delete dirList;
                }
        }
        else
        {
                C_File *file = this->AppendFile(rest, filetype, game, dir);
                if ( file )
                        file->SetShared(shared);
        }
}


CyString CBaseFile::GetFullFileSizeString() { return SPK::GetSizeString ( this->GetFullFileSize() ); }

unsigned char *CBaseFile::CreateData(size_t *size, CProgressInfo *progress)
{
        if ( this->WriteFile("temp.dat", progress) )
        {
                FILE *id = fopen("temp.dat", "rb");
                if ( id )
                {
                        fseek(id, 0, SEEK_END);
                        *size = ftell(id);
                        fseek(id, 0, SEEK_SET);
                        unsigned char *data = new unsigned char[*size];
                        fread(data, sizeof(unsigned char), *size, id);
                        fclose(id);

                        remove("temp.dat");
                        return data;
                }
        }

        remove("temp.dat");
        return NULL;
}


void CBaseFile::ConvertNormalMod(C_File *f, CyString to)
{
        C_File *match = this->FindMatchingMod(f);
        if ( match )
        {
                // file link
                if ( !match->GetData() )
                        match->ReadFromFile();
                match->ChangeBaseName(to);
        }

        // file link
        if ( !f->GetData() )
                f->ReadFromFile();

        f->ChangeBaseName(to);
}

void CBaseFile::ConvertAutoText(C_File *f)
{
        CyString to;
        if ( f->GetBaseName().IsIn("-L") )
                to = CyString("0000-L") + f->GetBaseName().GetToken("-L", 2, 2);
        else if ( f->GetBaseName().IsIn("-l") )
                to = CyString("0000-L") + f->GetBaseName().GetToken("-l", 2, 2);
        else
                to = f->GetBaseName().Left(-4) + "0000";

        // file link
        if ( !f->GetData() )
                f->ReadFromFile();

        f->ChangeBaseName(to);

}

void CBaseFile::ConvertFakePatch(C_File *f)
{
        // find next available fake patch
        int num = 0;

        bool found = true;
        while ( found )
        {
                ++num;
                found = false;
                CyString find = CyString::Number(num).PadNumber(2);
                for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                {
                        if ( node->Data()->GetFileType() != FILETYPE_MOD )
                                continue;
                        if ( !node->Data()->IsFakePatch() )
                                continue;
                        if ( node->Data()->GetBaseName().Compare(find) )
                        {
                                found = true;
                                break;
                        }
                }
        }

        CyString to = CyString::Number(num).PadNumber(2);
        C_File *match = this->FindMatchingMod(f);

        // file link
        if ( !f->GetData() )
                f->ReadFromFile();

        f->ChangeBaseName(to);
        if ( match )
        {
                // file link
                if ( !match->GetData() )
                        match->ReadFromFile();
                match->ChangeBaseName(to);
        }
}

C_File *CBaseFile::FindMatchingMod(C_File *f)
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                if ( node->Data()->GetFileType() != FILETYPE_MOD )
                        continue;
                 
                if ( f->GetFileExt().Compare(node->Data()->GetFileExt()) )
                        continue;

                if ( f->GetBaseName().Compare(node->Data()->GetBaseName()) )
                        return node->Data();
        }

        return NULL;
}

void CBaseFile::RenameFile(C_File *f, CyString baseName)
{
        if ( f->GetFileType() == FILETYPE_MOD )
        {
                C_File *match = this->FindMatchingMod(f);
                if ( match )
                        match->ChangeBaseName(baseName);
        }

        // need to edit the file
        if ( f->GetFileType() == FILETYPE_SCRIPT || f->GetFileType() == FILETYPE_UNINSTALL )
                f->RenameScript(baseName);

        f->ChangeBaseName(baseName);
}

CyString CBaseFile::CreateUpdateFile(CyString dir)
{
        CyString file = this->GetNameValidFile() + "_" + m_sAuthor + ".dat";
        file.RemoveChar(' ');

        CyStringList write;
        write.PushBack(CyString("Package: ") + m_sName);
        write.PushBack(CyString("Author: ") + m_sAuthor);
        write.PushBack(CyString("Version: ") + m_sVersion);
        write.PushBack(CyString("File: ") + CFileIO(m_sFilename).GetFilename());

        CFileIO File(dir + "/" + file);
        if ( File.WriteFile(&write) )
                return File.GetFullFilename();
        return NullString;
}

CyString CBaseFile::ErrorString(int error, CyString errorStr)
{
        if ( error == SPKERR_NONE ) return NullString;

        CyString err;

        switch(error)
        {
                case SPKERR_MALLOC:
                        err = "Memory Failed";
                        break;
                case SPKERR_FILEOPEN:
                        err = "Failed to open file";
                        break;
                case SPKERR_FILEREAD:
                        err = "Failed to read file";
                        break;
                case SPKERR_UNCOMPRESS:
                        err = "Failed to Uncompress";
                        break;
                case SPKERR_WRITEFILE:
                        err = "Failed to write file";
                        break;
                case SPKERR_CREATEDIRECTORY:
                        err = "Failed to create directory";
                        break;
                case SPKERR_FILEMISMATCH:
                        err = "File count mismatch";
                        break;
        }

        if ( !err.Empty() )
        {
                if ( !errorStr.Empty() )
                {
                        err += " (";
                        err += errorStr + ")";
                }
                return err;
        }

        return CyString((long)error);
}

bool CBaseFile::SaveToArchive(CyString filename, int game, CProgressInfo *progress)
{
        TCHAR buf[5000];
        wsprintf(buf, L"%hs", filename.c_str());

        HZIP hz = CreateZip(buf, 0);
        if ( !hz ) return false;

        // read files and compress
        ReadAllFilesToMemory();
        if ( !UncompressAllFiles(progress) )
        {
                CloseZip(hz);
                return false;
        }

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                if ( game != -1 ) {
                        if ( game && node->Data()->GetGame() && node->Data()->GetGame() != game )
                                continue;
                        if ( !game && node->Data()->GetGame() )
                                continue;
                }
                CyString fname = node->Data()->GetNameDirectory(this);
                // create the directory
                wsprintf(buf, L"%hs", fname.c_str());
                ZipAdd(hz, buf, node->Data()->GetData(), node->Data()->GetDataSize());
        }

        // add the data file
        CyStringList list;
        if ( this->GeneratePackagerScript(false, &list, true) )
        {
                if ( CFileIO("test.tmp").WriteFile(&list) )
                {
                        ZipAdd(hz, L"pluginmanager.txt", L"test.tmp");
                        CFileIO("test.tmp").Remove();
                }
        }

        CloseZip(hz);

        return true;
}

int CBaseFile::GetGameFromString(const Utils::String &sGame)
{
        int iGame = GAME_ALL;
        if ( sGame.Compare("ALL") )
                iGame = GAME_ALL;
        else if ( sGame.Compare("X3") )
                iGame = GAME_X3;
        else if ( sGame.Compare("X2") )
                iGame = GAME_X2;
        else if ( sGame.Compare("X3TC") )
                iGame = GAME_X3TC;
        else if ( sGame.Compare("X3AP") )
                iGame = GAME_X3AP;
        else if ( sGame.Compare("XREBIRTH") )
                iGame = GAME_XREBIRTH;
        else if ( sGame.isNumber() )
                iGame = sGame;

        return iGame;
}

Utils::String CBaseFile::ConvertGameToString(int iGame)
{
        Utils::String game = "ALL";

        switch(iGame) {
                case GAME_ALL:
                        game = "ALL";
                        break;
                case GAME_X2:
                        game = "X2";
                        break;
                case GAME_X3:
                        game = "X3";
                        break;
                case GAME_X3TC:
                        game = "X3TC";
                        break;
                case GAME_X3AP:
                        game = "X3AP";
                        break;
                case GAME_XREBIRTH:
                        game = "XREBIRTH";
                        break;
                default:
                        game = (long)iGame;
        }

        return game;
}

bool CBaseFile::IsGameInPackage(int game)
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
                if ( node->Data()->GetGame() == game )
                        return true;
        }

        return false;
}

bool CBaseFile::IsAnyGameInPackage()
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
                if ( node->Data()->GetGame() )
                        return true;
        }

        return false;
}

int CBaseFile::FindFirstGameInPackage()
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
                if ( node->Data()->GetGame() )
                        return node->Data()->GetGame();
        }

        return 0;
}
bool CBaseFile::IsMultipleGamesInPackage()
{
        int game = 0;
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
                if ( node->Data()->GetGame() ) {
                        if ( game != node->Data()->GetGame() )
                                return true;
                        game = node->Data()->GetGame();
                }
        }

        return false;
}