Subversion Repositories spk

Rev

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

#include "spk.h"
#include "emp.h"

#include <time.h>
#include <vector>

#ifdef _RAR
#include <unrar.h>
#endif

enum {READ_START, READ_GLOBAL, READ_SCRIPT, READ_SCRIPTFILE, READ_WARES};
enum { EXTRACT, TEST, PRINT, LIST };

typedef struct SDummyEntry {
        CyString                sSection;
        CyStringList    lEntries;
} SDummyEntry;

typedef struct SComponantEntry2 {
        CyString                sSection;
        CyStringList    lEntries;
} SComponantEntry2;

typedef struct SComponantEntry {
        CyString                sSection;
        CLinkList<SComponantEntry2>     lEntries;
} SComponantEntry;

CPackages::CPackages()
{
        m_bRenameText = false;
        m_iLanguage = 0;
        m_iGame = -1;
        m_iGameVersion = -1;
        m_iGameFlags = 0;
        m_bSurpressProtectedWarning = false;
        m_iMaxPatch = 1;
        m_iLastUpdated = 0;
        m_iFakePatch = -1;
        m_bVanilla = true;
        m_pEnabledMod = NULL;
        m_bForceModInstall = false;
        m_bLoaded = false;
        m_bRedo = false;
        m_bOldPlugin = false;
        m_bUsedWare = false;
        m_pPackageNode = NULL;
        m_bRemoveDir = false;
        m_bDisableVanilla = false;
        m_bLoadVFS = true;
        m_iSaveGame = -1;
        m_iSaveGameManager = -1;
        m_bForceEMP = false;

        for ( int i = 0; i < WAREBUFFERS; i++ )
                m_iWareBuffer[i] = 0;
        m_iShipBuffer = 0;
}

CPackages::~CPackages ()
{
        m_lDisableList.clear();
        m_lEnableList.clear();
        this->Reset();
}

void CPackages::SetVanilla(bool b)
{
        if ( m_bVanilla != b && b ) {
                for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() ) {
                        pNode->Data()->SetModifiedEnabled(pNode->Data()->IsEnabled());
                }
        }
        m_bVanilla = b;
}

void CPackages::Reset()
{
        m_lFiles.MemoryClear();
        m_lUninstallFiles.MemoryClear();
        for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
                pNode->Data()->GetFileList()->clear();
        m_lPackages.MemoryClear();
        m_lOriginalFiles.MemoryClear();
        m_iFakePatch = -1;
        m_bVanilla = false;
        m_pEnabledMod = NULL;
        m_bLoaded = false;
        m_iLanguage = 0;
        m_bOldPlugin = false;
        m_bRemoveDir = false;
        m_bDisableVanilla = false;
        m_bSurpressProtectedWarning = false;
        m_iSaveGame = -1;
        m_iSaveGameManager = -1;
        m_sSetMod = "";

        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                m_iWareBuffer[i] = 0;
                m_lGameWares[i].MemoryClear();
        }
        m_iShipBuffer = 0;

        m_lGameShips.MemoryClear();
        m_lAvailablePackages.MemoryClear();
        m_lInstallList.MemoryClear();
        m_lEnableList.MemoryClear();
        m_lDisableList.MemoryClear();

        m_lCreatedFiles.Clear();
        m_lNonRemovedFiles.Clear();
        m_lGlobals.Clear();
        m_lFakePatchOrder.Clear();
}

void CPackages::LoadVirtualFileSystem()
{
        if ( !m_bLoadVFS )
                return;

        if ( m_pEnabledMod )
        {
                C_File *f;
                for ( f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); f; f = m_pEnabledMod->GetNextFile(f) )
                {
                        if ( f->IsFakePatch() ) continue;
                        if ( f->CheckFileExt("cat") ) break;
                }

                if ( f )
                        m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), f->GetFilePointer().ToString(), 0);
                else
                        m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), 0);
        }
        else
                m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), 0);
}

bool CPackages::IsOldDir(CyString dir)
{
        bool oldPlugin = false;

        dir = dir.FindReplace("\\", "/");
        CFileIO datFile(dir + "/PluginManager/pluginmanager.dat");
        if ( datFile.Exists() )
        {
                std::vector<CyString> *readFile = datFile.ReadLines();
                if ( readFile )
                {
                        for ( int i = 0; i < (int)readFile->size(); i++ )
                        {
                                CyString line(readFile->at(i));
                                CyString cmd = line.GetToken(":", 1, 1).lower();
                                if ( cmd == "<script>" || cmd == "</scripts>" )
                                        break;
                                else if ( cmd == "spkinstaller" || cmd == "globalfiles" )
                                        break;
                                else if ( cmd == "pluginmanager" )
                                {
                                        oldPlugin = true;
                                        break;
                                }
                        }

                        delete readFile;
                }
        }

        return oldPlugin;
}

bool CPackages::Read ( CyString dir, CProgressInfo *progress )
{
        m_sCurrentDir = dir;
        m_sCurrentDir = m_sCurrentDir.FindReplace("\\", "/");
        m_bOldPlugin = false;
        m_lCreatedFiles.Clear();
        m_bRemoveDir = false;
        m_bSurpressProtectedWarning = false;
        m_iSaveGame = -1;
        m_iSaveGameManager = -1;
        m_lNonRemovedFiles.Clear();
        m_lGlobals.Clear();
        m_lFakePatchOrder.Clear();

        m_bVanilla = true;

        // check the pluginmanager data file exists
        CFileIO datFile(m_sCurrentDir + "/PluginManager/pluginmanager.dat");
        if ( datFile.Exists() )
        {
                std::vector<CyString> *readFile = datFile.ReadLines();
                if ( readFile )
                {
                        float fVersion = 0;
                        bool spkinstaller = false;
                        float fBeta = 0;

                        int iStatus = READ_START;
                        int iCount = 0;
                        int iWare = -1;
                        int iShip = -1;

                        CBaseFile *packageFile = 0;

                        for ( int i = 0; i < (int)readFile->size(); i++ )
                        {
                                CyString line(readFile->at(i));

                                CyString cmd = line.GetToken(":", 1, 1).lower();
                                CyString rest = line.GetToken(":", 2).RemoveFirstSpace();

                                if ( iStatus == READ_GLOBAL )
                                {
                                        if ( cmd == "<script>" )
                                        {
                                                packageFile = new CSpkFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "<ship>" )
                                        {
                                                packageFile = new CXspFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "<base>" )
                                        {
                                                packageFile = new CBaseFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "<archive>" )
                                        {
                                                packageFile = new CArchiveFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd != "</scripts>" )
                                        {
                                                int game = 0;
                                                bool disabled = false;
                                                CyString fileName = line.GetToken(":", 5);
                                                CyString origName;
                                                if ( fileName.Left(2) == "D#" )
                                                {
                                                        fileName.Erase(0, 2);
                                                        disabled = true;
                                                }
                                                if ( fileName.Left(2) == "G#" ) {
                                                        fileName.Erase(0, 2);
                                                        game = fileName.GetToken("#", 1, 1).ToInt();
                                                        fileName = fileName.GetToken("#", 2);
                                                }
                                                if ( fileName.IsIn("O#") )
                                                {
                                                        origName = fileName.GetToken("O#", 2, 2);
                                                        fileName = fileName.GetToken("O#", 1, 1);
                                                }

                                                C_File *newFile = new C_File( m_sCurrentDir + fileName );
                                                newFile->SetGame(game);
                                                newFile->SetOriginalName(origName);
                                                newFile->SetDisabled(disabled);
                                                newFile->SetFileType(line.GetToken(":", 1, 1).ToInt());
                                                newFile->SetCreationTime((time_t)line.GetToken(":", 2, 2).ToInt());
                                                newFile->SetDir(line.GetToken(":", 3, 3));
                                                if ( line.GetToken(":", 4, 4).ToInt() )
                                                        newFile->SetShared(true);
                                                newFile->UpdateSigned();

                                                m_lFiles.push_back(newFile);
                                        }
                                }
                                else if ( iStatus == READ_SCRIPT )
                                {
                                        if ( cmd == "installspk" )
                                                packageFile->SetFilename(rest);
                                        else if ( cmd == "files" )
                                        {
                                                iStatus = READ_SCRIPTFILE;
                                                if ( spkinstaller )
                                                {
                                                        int max;
                                                        CyString *files = rest.SplitToken(' ', &max);

                                                        for ( int i = 0; i < max; i++ )
                                                        {
                                                                int fileNum = files[i].ToInt();
                                                                if ( fileNum >= 0 && fileNum < m_lFiles.size() )
                                                                        packageFile->AddFile(m_lFiles[fileNum]);
                                                        }

                                                        CLEANSPLIT(files, max);
                                                }
                                        }
                                        else if ( cmd == "disabled" )
                                                packageFile->SetEnabled(false);
                                        else if ( cmd == "modifieddisabled" )
                                                packageFile->SetModifiedEnabled(false);
                                        else if ( cmd == "icon" )
                                        {
                                                C_File *icon = new C_File(rest.GetToken(" ", 2));
                                                packageFile->SetIcon(icon, rest.GetToken(" ", 1, 1));
                                        }
                                        else
                                                packageFile->ParseValueLine(line.ToString());
                                }
                                else if ( iStatus == READ_SCRIPTFILE )
                                {
                                        if ( cmd == "<script>" )
                                        {
                                                if ( packageFile->IsMod() && packageFile->IsEnabled() )
                                                        m_pEnabledMod = packageFile;

                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CSpkFile();
                                                iStatus = READ_SCRIPT;

                                        }
                                        else if ( cmd == "<ship>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CXspFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "<base>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CBaseFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "<archive>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CArchiveFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == "</scripts>" )
                                        {
                                                if ( packageFile->IsMod() && packageFile->IsEnabled() )
                                                        m_pEnabledMod = packageFile;

                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                        }
                                        else
                                        {
                                                int fileNum = line.GetToken("::", 1, 1).ToInt();
                                                if ( fileNum >= 0 && fileNum < m_lFiles.size() )
                                                        packageFile->AddFile(m_lFiles[fileNum]);
                                        }
                                }
                                else if ( iWare != -1 )
                                {
                                        --iCount;

                                        SGameWare *gm = new SGameWare;
                                        gm->iPos = line.GetToken(" ", 1, 1).ToInt();
                                        gm->iType = line.GetToken(" ", 2, 2).ToInt();
                                        gm->cType = line.GetToken(" ", 3, 3)[0];
                                        gm->pWare = NULL;
                                        gm->sWareName = line.GetToken(" ", 4);
                                        m_lGameWares[iWare].push_back(gm);
                                        if ( iCount <= 0 )
                                                iWare = -1;
                                }
                                else if ( iShip != -1 )
                                {
                                        --iCount;

                                        SGameShip *gm = new SGameShip;
                                        gm->iType = line.GetToken(" ", 1, 1).ToInt();
                                        if ( line.GetToken(" ", 2, 2).Left(4) == "$#C:" )
                                        {
                                                gm->sShipClass = line.GetToken(" ", 2, 2).Right(-4);
                                                gm->sShipID = line.GetToken(" ", 3);
                                        }
                                        else
                                        {
                                                gm->sShipID = line.GetToken(" ", 2);
                                                gm->sShipClass = "OBJ_SHIP_M5";
                                        }
                                        gm->pPackage = NULL;
                                        m_lGameShips.push_back(gm);
                                        if ( iCount <= 0 )
                                                iShip = -1;
                                }
                                else if ( cmd.Compare("savegamemanager") )
                                        m_iSaveGameManager = rest.ToInt();
                                else if ( cmd.Compare("savegame") )
                                        m_iSaveGame = rest.ToInt();
                                else if ( cmd == "fakepatch" )
                                        m_iFakePatch = rest.ToInt();
                                else if ( cmd == "updatetime" )
                                        m_iLastUpdated = rest.ToInt();
                                else if ( cmd == "surpressprotectedwarning" )
                                        m_bSurpressProtectedWarning = true;
                                else if ( cmd == "pluginmanager" )
                                {
                                        fVersion = rest.ToFloat();
                                        m_bOldPlugin = true;
                                }
                                else if ( cmd == "setmod" )
                                        m_sSetMod = rest;
                                else if ( cmd == "shipbuffer" )
                                        m_iShipBuffer = rest.ToInt();
                                else if ( cmd == "warebuffers" )
                                {
                                        int max = rest.NumToken(" ");
                                        for ( int i = 0; i < WAREBUFFERS; i++ )
                                        {
                                                if ( i > max )
                                                        m_iWareBuffer[i] = 0;
                                                else
                                                        m_iWareBuffer[i] = rest.GetToken(" ", i + 1, i + 1).ToInt();
                                        }
                                }
                                else if ( cmd == "createdfile" )
                                        m_lCreatedFiles.PushBack(rest);
                                else if ( cmd == "wares" )
                                {
                                        iWare = rest.GetToken(" ", 1, 1).ToInt();
                                        iCount = rest.GetToken(" ", 2, 2).ToInt();
                                }
                                else if ( cmd == "ships" )
                                {
                                        iShip = 1;
                                        iCount = rest.GetToken(" ", 1, 1).ToInt();
                                }
                                else if ( cmd == "modified" )
                                        m_bVanilla = false;
                                else if ( cmd == "spkinstaller" )
                                {
                                        fVersion = rest.ToFloat();
                                        spkinstaller = true;
                                }
                                else if ( cmd == "BetaVersion" )
                                        fBeta = rest.ToFloat();
                                else if ( cmd == "Uninstall" )
                                {
                                        C_File *uf = new C_File();
                                        uf->SetFileType(FILETYPE_SCRIPT);
                                        uf->SetCreationTime(rest.GetToken(" ", 1, 1).ToInt());
                                        uf->SetFilename(m_sCurrentDir + "/Scripts/" + rest.GetToken(" ", 2));
                                        m_lUninstallFiles.push_back(uf);
                                }
                                else if ( cmd == "NonRemovedFile" )
                                {
                                        if ( !CFileIO(rest).Remove() )
                                                m_lNonRemovedFiles.PushBack(rest);
                                }
                                else if ( cmd == "Original" )
                                {
                                        C_File *uf = new C_File();
                                        uf->SetFileType(rest.GetToken(" ", 1, 1).ToInt());
                                        if ( uf->GetFileType() == FILETYPE_EXTRA )
                                        {
                                                uf->SetDir(rest.GetToken(" ", 2).GetToken(":", 2));
                                                uf->SetFilename(rest.GetToken(" ", 2).GetToken(":", 1, 1));
                                        }
                                        else
                                                uf->SetFilename(rest.GetToken(" ", 2));
                                        uf->SetFilename(m_sCurrentDir + "/" + uf->GetNameDirectory(NULL));
                                        m_lOriginalFiles.push_back(uf);
                                }
                                else if ( cmd.Compare("GlobalSetting") )
                                        m_lGlobals.PushBack(rest.GetToken(":", 1, 1), rest.GetToken(":", 2));
                                else if ( cmd.Compare("FakePatchOrder") )
                                        m_lFakePatchOrder.PushBack(rest.GetToken(":", 1, 1), rest.GetToken(":", 2));
                                else if ( cmd == "globalfiles" )
                                        iStatus = READ_GLOBAL;

                                if ( progress )
                                {
                                        progress->UpdateProgress((long)i, (long)readFile->size());
                                }
                        }
                        readFile->clear();

                        if ( !spkinstaller )
                                m_bRedo = true;

                        delete readFile;
                }
        }

        if ( m_pEnabledMod && !m_pEnabledMod->IsEnabled() )
                m_pEnabledMod = NULL;

        m_iGame = m_gameExe.GetGameType(m_sCurrentDir) + 1;
        m_iGameVersion = m_gameExe.GetGameVersion(m_sCurrentDir, &m_sGameVersion) + 1;
        m_iGameFlags = m_gameExe.GetGameFlags(m_iGame - 1);
        m_iMaxPatch = m_gameExe.GetMaxPatch(m_iGame - 1);

        // find the fake patch
        if ( !ReadyFakePatch() )
                return false;

        this->RemoveCreatedFiles();

        // match up wares
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if ( node->Data()->GetType() == TYPE_SPK )
                {
                        CSpkFile *p = (CSpkFile *)node->Data();
                        for ( CListNode<SWares> *wNode = p->GetWaresList()->Front(); wNode; wNode = wNode->next() )
                        {
                                SWares *w = wNode->Data();
                                for ( CListNode<SGameWare> *gNode = m_lGameWares[CPackages::ConvertWareType(w->cType)].Front(); gNode; gNode = gNode->next() )
                                {
                                        if ( w->sID == gNode->Data()->sWareName.ToString() )
                                        {
                                                gNode->Data()->pWare = w;
                                                break;
                                        }
                                }
                        }
                }
                else if ( node->Data()->GetType() == TYPE_XSP )
                {
                        CXspFile *p = (CXspFile *)node->Data();
                        for ( CListNode<SGameShip> *gNode = m_lGameShips.Front(); gNode; gNode = gNode->next() )
                        {
                                if ( p->GetShipID().Compare(gNode->Data()->sShipID.ToString()) )
                                {
                                        gNode->Data()->pPackage = p;
                                        break;
                                }
                        }
                }
        }

        // check the purged time
        this->PurgeGameObjects();

        m_bLoaded = true;

        return true;
}

void CPackages::RemoveCreatedFiles()
{
        for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
        {
                if ( CFileIO(node->str).Exists() )
                        CFileIO(node->str).Remove();
                else if ( CFileIO(m_sCurrentDir + "/" + node->str).Exists() )
                        CFileIO(m_sCurrentDir + "/" + node->str).Remove();
        }

        m_lCreatedFiles.Clear();
}

void CPackages::PurgeGameObjects()
{
        // check for the log file
        CyString logDir = GetLogDirectory();
        CFileIO LogFile(logDir + "/log0" + CyString::Number(PMTEXTFILE).PadNumber(4) + ".txt");
        if ( LogFile.Exists() )
        {
                // read the log file to memory
                std::vector<CyString> *lines = LogFile.ReadLines();
                if ( lines )
                {
                        for ( int i = 0; i < (int)lines->size(); i++ )
                        {
                                CyString line(lines->at(i));
                                CyString start = line.GetToken(":", 1, 1).ToLower();
                                CyString rest = line.GetToken(":", 2, 2).RemoveFirstSpace();
                                if ( start.Compare("purged") )
                                {
                                        long time = rest.ToLong();
                                        if ( time == m_iLastUpdated )
                                        {
                                                this->PurgeWares();
                                                this->PurgeShips();
                                                this->RemoveUninstallScripts();
                                        }
                                }
                        }

                        delete lines;
                }
                // remove the log file
                LogFile.Remove();
        }
}

void CPackages::PurgeWares()
{
        // mark all delete wares as available
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
                {
                        SGameWare *w = node->Data();
                        if ( !w ) continue;
                        if ( w->iType == WARETYPE_DELETED )
                                w->iType = WARETYPE_NONE;
                }
                m_lGameWares[i].RemoveEmpty();
        }
}

void CPackages::PurgeShips()
{
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                if ( !node->Data() )
                        continue;
                if ( node->Data()->iType == WARETYPE_DELETED )
                        node->Data()->iType = WARETYPE_NONE;
        }
        m_lGameShips.RemoveEmpty();
}

CyString CPackages::GetLogDirectory()
{
        CyString logDir = m_sCurrentDir;
        if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
        {
                SGameExe *exe = m_gameExe.GetGame(m_iGame - 1);
                if ( exe )
                {
                        if ( !exe->sMyDoc.Empty() )
                                logDir = m_sMyDoc + "/" + exe->sMyDoc;
                }
        }

        return logDir;
}

CyString CPackages::GetLogDirectory(CyString gameExe)
{
        CyString logDir = m_sCurrentDir;
        if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
        {
                SGameExe *exe = m_gameExe.GetGameExe(CFileIO(gameExe).GetFilename());
                if ( exe )
                {
                        if ( !exe->sMyDoc.Empty() )
                                logDir = m_sMyDoc + "/" + exe->sMyDoc;
                }
        }

        return CFileIO(logDir).GetFullFilename();
}

CyString CPackages::GetSaveDirectory()
{
        CyString logDir = this->GetLogDirectory();
        if ( m_iGameFlags & EXEFLAG_NOSAVESUBDIR )
                return logDir;

        return logDir + "/save";
}

bool CPackages::ReadyFakePatch()
{
        // already exists, lets skip it
        if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Exists() && CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Exists() )
                return true;

        // if only one of them exists, lets remove them
        if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Exists() )
                CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Remove();
        if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Exists() )
                CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Remove();

        // now lets find the fake patch
        CyString useFile;
        if ( m_iFakePatch > 0 )
        {
                CyString file = CyString::Number(m_iFakePatch).PadNumber(2);
                if ( CheckValidPluginManagerFile(file) )
                        useFile = file;
                else if ( CheckIfPluginManagerFile(file) )
                        useFile = file;
        }

        if ( useFile.Empty() )
        {
                int nextfree = this->FindNextFakePatch();
                --nextfree; // gets the end fake patch

                // work backwards till we find a valid one
                while ( nextfree > m_iMaxPatch )
                {
                        CyString file = CyString::Number(nextfree).PadNumber(2);
                        if ( CheckValidPluginManagerFile(file) )
                        {
                                useFile = file;
                                break;
                        }
                        --nextfree;
                }
        }

        CyString addonDir = this->GetAddonDir();

        // couldn't find the correct file, lets search for it
        if ( useFile.Empty() ) {
                int nextfree = this->FindNextFakePatch();
                --nextfree; // gets the end fake patch

                // work backwards till we find a valid one
                while ( nextfree > m_iMaxPatch )
                {
                        CyString file = CyString::Number(nextfree).PadNumber(2);
                        if ( CheckIfPluginManagerFile(file) )
                        {
                                useFile = file;
                                break;
                        }
                        --nextfree;
                }               
        }

        if ( !useFile.Empty() )
        {
                // lets check whats in the file first
                CCatFile openCat;
                if ( openCat.Open(m_sCurrentDir + "/" + useFile + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
                {
                        CLinkList<SInCatFile> *files = openCat.GetFiles();
                        bool found = false;
                        if ( files )
                        {
                                CyString useAddonDir = addonDir;
                                if ( !useAddonDir.Empty() ) useAddonDir += "\\";
                                for ( SInCatFile *inCat = files->First(); inCat; inCat = files->Next() )
                                {
                                        if ( inCat->sFile.Compare("PlugMan\\TFake.pck") )
                                                continue;
                                        if ( inCat->sFile.Compare(useAddonDir + "t\\44" + CyString::Number(PMTEXTFILE).PadNumber(4) + ".pck") )
                                                continue;
                                        if ( inCat->sFile.Compare(useAddonDir + "t\\" + CyString::Number(PMTEXTFILE).PadNumber(4) + "-L044.pck") )
                                                continue;

                                        found = true;
                                        break;
                                }
                        }

                        // no files, jsut delete them
                        CFileIO catFile(m_sCurrentDir + "/" + useFile + ".cat");
                        if ( !files || !found )
                        {
                                if ( catFile.Remove() )
                                {
                                        CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");
                                        if ( datFile.Remove() )
                                                return true;
                                }
                        }
                        else
                        {
                                if ( catFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat") )
                                {
                                        CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");

                                        if ( datFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat") )
                                                return true;

                                        // TODO: it failed, restore cat file and do error
                                }
                        }
                }

                // if we're here, we tryed, and failed
                return false;
        }

        // otherwise we didn't need to (hopefully)
        return true;
}


bool CPackages::CheckIfPluginManagerFile(CyString filename)
{
        bool found = false;

        CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
        if ( catFile.Exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").Exists() )
        {
                CCatFile openFile;
                if ( openFile.Open(catFile.GetFullFilename(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
                {
                        int count = 0;
                        int noncount = 0;

                        // check for some of the files
                        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
                                if ( node->Data()->GetFullDir().IsIn("::") ) {
                                        if (CFileIO(node->Data()->GetFullDir().GetToken("::", 1, 1)).GetFilename().Compare("PlugMan_Fake.cat") ) {
                                                CyString filename = node->Data()->GetFilePointer().GetToken("::", 2, 2);
                                                filename = filename.findreplace("/", "\\");
                                                if ( openFile.FindData(filename) )
                                                        ++count;
                                                else
                                                        ++noncount;
                                        }
                                }
                        }

                        if ( (count && !noncount) || (count > noncount) )
                                found = true;
                }
        }

        return found;
}

bool CPackages::CheckValidPluginManagerFile(CyString filename)
{
        bool found = false;

        // both the cat file and dat file exists, lets open it
        CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
        if ( catFile.Exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").Exists() )
        {
                CCatFile openFile;
                if ( openFile.Open(catFile.GetFullFilename(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
                {
                        if ( openFile.FindData("PlugMan\\TFake.pck") )
                                return true;
                        if ( openFile.FindData("pluginmanagerfake.pck") )
                                return true;
                }
        }

        return found;
}

/**
 * Converts a package in old format
 *
 * Some entries in older packages need to be changed for the new installer
 * These Include:
 *              - Game Version, old format is an id of position in the Data/exe file, new is 2 values, one for game, one for the version
 */
void CPackages::ConvertOldPackage(CBaseFile *package)
{
        for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
                if ( gNode->Data()->iGame == -1 ) {
                        // all versions
                        if ( gNode->Data()->iVersion == 0 )
                                gNode->Data()->iGame = 0;
                        else
                        {
                                int version = 0;
                                gNode->Data()->iGame = m_gameExe.ConvertGameType(gNode->Data()->iVersion, &version);
                                gNode->Data()->iVersion = version;
                        }
                }
        }

        if ( package->GetForumLink().Empty() && package->GetWebSite().IsIn("forum.egosoft"))
        {
                package->SetForumLink(package->GetWebSite());
                package->SetWebSite("");
        }

        // convert the version
        if ( package->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)package;
                if ( spk->GetScriptType() == CSpkFile::SCRIPTTYPE_CUSTOM )
                {
                        CyString type = spk->GetScriptTypeString(44);
                        if ( type.Compare("Ship Upgrade") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPUPGRADE);
                        else if ( type.Compare("Trade Script") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_TRADE);
                        else if ( type.Compare("Fleet Management") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_FLEET);
                        else if ( type.Compare("Navigation Script") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_NAVIGATION);
                        else if ( type.Compare("Piracy") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_PIRACY);
                        else if ( type.Compare("Other") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_OTHER);
                        else if ( type.Compare("Ship Command") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPCOMMAND);
                        else if ( type.Compare("Station Command") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_STATIONCOMMAND);
                        else if ( type.Compare("al plugin") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_ALPLUGIN);
                        else if ( type.Compare("combat script") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_COMBAT);
                        else if ( type.Compare("bbs and missions") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_MISSION);
                        else if ( type.Compare("extension mod") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_EXTENSION);
                        else if ( type.Compare("rebalance mod") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_REBALANCE);
                        else if ( type.Compare("general mod") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_GENERALMOD);
                        else if ( type.Compare("total conversion") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_TOTAL);
                        else if ( type.Compare("cheat script") )
                                spk->SetScriptType(CSpkFile::SCRIPTTYPE_CHEAT);
                        else if ( type == "Library Script" )
                        {
                                spk->SetScriptType("");
                                spk->SetLibrary();
                        }

                        if ( spk->GetScriptType() != CSpkFile::SCRIPTTYPE_CUSTOM )
                                spk->SetScriptType("");
                }
        }
        else if ( package->GetType() == TYPE_XSP )
        {
                CXspFile *xsp = (CXspFile *)package;
                Utils::String data = xsp->GetShipData();

                for ( int i = 17; i <= 18; i++ )
                {
                        Utils::String model = data.token(";", i);
                        Utils::String modelExt = model.right(4);
                        // check file extension
                        if ( modelExt.Compare(".bod") || modelExt.Compare(".pbd") )
                                data = data.replaceToken(";", i, model.left(-4));
                }

                xsp->SetShipData(data);
        }

        // any extra files that are in director folder
        if ( package->AnyFileType(FILETYPE_EXTRA) )
        {
                for ( C_File *f = package->GetFirstFile(FILETYPE_EXTRA); f; f = package->GetNextFile(f) )
                {
                        if ( !f->GetDir().Compare("director") )
                                continue;
                        if ( f->CheckFileExt("xml") || f->CheckFileExt("pck") )
                        {
                                f->SetDir("");
                                f->SetFileType(FILETYPE_MISSION);
                        }
                }
        }
}

void CPackages::UpdatePackage(CBaseFile *p)
{
        if ( p->GetType() != TYPE_SPK )
                return;

        CSpkFile *package = (CSpkFile *)p;

        // update the signed status
        package->UpdateSigned(true);

        // check for another mod
        if ( !package->IsAnotherMod() )
                return;

        package->SetParent((CSpkFile *)FindSpkPackage(package->GetOtherName(), package->GetOtherAuthor()));
}

/**
 * Updates the package list once data has been read
 *
 * Finds any original Files
 * Updates Signed status of all packages
 */
bool CPackages::UpdatePackages(int doStatus, bool individual)
{
        // if theres no original files, then set the current structure as the base
        if ( doStatus == 0 || doStatus == -1 )
        {
                if ( m_lOriginalFiles.empty() || m_bRedo )
                {
                        StoreOriginalFiles(FILETYPE_SCRIPT, "scripts");
                        StoreOriginalFiles(FILETYPE_TEXT, "t");
                        StoreOriginalFiles(FILETYPE_SOUND, "soundtrack");
                        StoreOriginalFiles(FILETYPE_EXTRA, "mov");
                }
        }

        // update each package
        // parent/child, signed status
        if ( doStatus == -1 || doStatus == 1 )
        {
                if ( individual )
                {
                        // no package, most likly none installed
                        if ( !m_pPackageNode )
                                return false;

                        this->UpdatePackage(m_pPackageNode->Data());

                        // move to the next package
                        m_pPackageNode = m_pPackageNode->next();
                        if ( !m_pPackageNode )
                                return false;
                }
                else
                {
                        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
                                this->UpdatePackage(node->Data());
                }
        }

        if ( doStatus == -1 || doStatus == 2 )
                this->LoadVirtualFileSystem();

        // adjust save games
        if ( doStatus == -1 || doStatus == 3 )
        {
                /*
                // load our save games to the active mydocs
                if ( m_iSaveGame != -1 )
                {
                        // check we have a valid save game directory
                        CDirIO Dir(this->GetSaveDirectory());
                        if ( Dir.Exists() )
                        {
                                // now check our directory already exists
                                // if we are vanilla mode, then make sure we use the vanilla saves if exists
                                CyString d = CyString("game_") + (long)m_iSaveGame + "/";
                                if ( m_bVanilla )
                                        d += "Vanilla";
                                else
                                        d += "Modified";

                                // copy the files back over
                                if ( Dir.Exists(d) )
                                {
                                        // if vanilla, remove files first
                                        if ( m_bVanilla )
                                        {
                                                CyStringList *lDirs = Dir.DirList();
                                                if ( lDirs )
                                                {
                                                        for ( SStringList *node = lDirs->Head(); node; node = node->next )
                                                        {
                                                                if ( !Dir.IsFile(node->str) || !CFileIO(node->str).CheckFileExtension("sav") )
                                                                        continue;
                                                                CFileIO(Dir.File(node->str)).Remove();
                                                        }

                                                        delete lDirs;
                                                }
                                        }

                                        // copy files to normal save game directory
                                        CDirIO FromDir(Dir.Dir(d));
                                        CyStringList *lDirs = FromDir.DirList();
                                        if ( lDirs )
                                        {
                                                for ( SStringList *node = lDirs->Head(); node; node = node->next )
                                                {
                                                        if ( !FromDir.IsFile(node->str) || !CFileIO(node->str).CheckFileExtension("sav") )
                                                                continue;
                                                        CFileIO F(FromDir.File(node->str));
                                                        F.Copy(Dir.File(F.GetFilename()));
                                                }

                                                delete lDirs;
                                        }
                                }
                        }
                }*/
        }

        m_bRedo = false;
        return true;
}

int CPackages::CheckOpenPackage(CyString file, int *error)
{
        // first check if it exists
        if ( !CFileIO(file).Exists() )
        {
                *error = INSTALLERR_NOEXIST;
                return -1;
        }

        // open the spk file
        float fVersion = 0.0f;
        int check = CBaseFile::CheckFile(file, &fVersion);

        // wrong version
        if ( fVersion > (float)FILEVERSION )
        {
                *error = INSTALLERR_VERSION;
                return -1;
        }

        return check;
}

CMultiSpkFile *CPackages::OpenMultiPackage(CyString file, int *error, CProgressInfo *progress)
{
        int check = CheckOpenPackage(file, error);
        if ( *error == -1 )
                return false;

        if ( check != SPKFILE_MULTI )
        {
                *error = INSTALLERR_NOMULTI;
                return false;
        }

        *error = INSTALLERR_NONE;

        CMultiSpkFile *package = new CMultiSpkFile;

        bool ret = false;
        if ( package->ReadFile(file, true) )
        {
                if ( package->ReadAllPackages(SPKREAD_NODATA) )
                {
                        for ( CListNode<SMultiSpkFile> *node = package->GetFileList()->Front(); node; node = node->next() )
                                this->ConvertOldPackage(node->Data()->pFile);

                        ret = true;
                }
        }

        if ( ret )
                return package;

        delete package;
        return NULL;
}

bool CPackages::OpenMultiPackage ( CyString file, CLinkList<CBaseFile> *packageList, int *error, CProgressInfo *progress )
{
        int check = CheckOpenPackage(file, error);
        if ( *error == -1 )
                return false;

        if ( check != SPKFILE_MULTI )
        {
                *error = INSTALLERR_NOMULTI;
                return false;
        }

        *error = INSTALLERR_NONE;

        CMultiSpkFile *package = new CMultiSpkFile;

        bool ret = false;
        if ( package->ReadFile(file, true) )
        {
                if ( package->ReadAllPackages(SPKREAD_ALL, packageList) )
                {
                        for ( CListNode<CBaseFile> *node = packageList->Front(); node; node = node->next() )
                                this->ConvertOldPackage(node->Data());


                        ret = true;
                }
        }

        delete package;

        return ret;
}

int CPackages::PrepareMultiPackage ( CyString file, CLinkList<CBaseFile> *errorPackageList, int *error, CProgressInfo *progress )
{
        int check = CheckOpenPackage(file, error);
        if ( *error == -1 )
                return 0;

        if ( check != SPKFILE_MULTI )
        {
                *error = INSTALLERR_NOMULTI;
                return 0;
        }

        *error = INSTALLERR_NONE;

        CMultiSpkFile *package = new CMultiSpkFile;

        int count = 0;
        if ( package->ReadFile(file, true) )
        {
                CLinkList<CBaseFile> packageList;
                if ( package->ReadAllPackages(SPKREAD_ALL, &packageList) )
                {
                        for ( CListNode<CBaseFile> *node = packageList.Front(); node; node = node->next() )
                        {
                                CBaseFile *p = node->Data();
                                this->ConvertOldPackage(p);
                                int error = this->PrepareInstallPackage(p);
                                if ( error )
                                {
                                        node->Data()->SetLoadError(error);
                                        errorPackageList->push_back(p);
                                }
                                else
                                        ++count;
                        }
                }
                packageList.clear();
        }

        for ( SMultiSpkFile *ms = package->GetFileList()->First(); ms; ms = package->GetFileList()->Next() )
                ms->pFile = NULL;
        delete package;

        return count;
}

CBaseFile *CPackages::OpenPackage(CyString file, int *error, CProgressInfo *progress, int readtype, int flags)
{
        int check = CheckOpenPackage(file, error);
        if ( *error == -1 )
                return NULL;

        CBaseFile *installFile = 0;

        if ( progress )
                progress->DoHalf();

        switch (check)
        {
                case SPKFILE_OLD:
                        *error = INSTALLERR_OLD;
                        return NULL;
                case SPKFILE_INVALID:
                        // convert xsp
                        if ( CFileIO(file).CheckFileExtension("xsp") )
                        {
                                installFile = new CXspFile();
                                if ( !((CXspFile *)installFile)->ConvertOld(file) )
                                {
                                        delete installFile;
                                        return NULL;
                                }
                                break;
                        }
                        *error = INSTALLERR_INVALID;
                        return NULL;
                case SPKFILE_BASE:
                        installFile = new CBaseFile();
                        if ( !installFile->ReadFile(file, readtype, progress) )
                        {
                                delete installFile;
                                return NULL;
                        }
                        break;
                case SPKFILE_SINGLE:
                        installFile = new CSpkFile();
                        if ( !((CSpkFile *)installFile)->ReadFile(file, readtype, progress) )
                        {
                                delete installFile;
                                return NULL;
                        }
                        break;
                case SPKFILE_MULTI:
                        *error = INSTALLERR_NOMULTI;
                        return NULL;
                case SPKFILE_SINGLESHIP:
                        installFile = new CXspFile();
                        if ( !((CXspFile *)installFile)->ReadFile(file, readtype, progress) )
                        {
                                delete installFile;
                                return NULL;
                        }
                        break;

                default:
                        *error = INSTALLERR_UNKNOWN;
                        return NULL;
        }

        if ( progress )
                progress->SecondHalf();

        // now uncomress all files
        if ( !(flags & READFLAG_NOUNCOMPRESS) )
                installFile->UncompressAllFiles(progress);

        this->ConvertOldPackage (installFile);

        return installFile;
}

void CPackages::PurgeUninstallScripts(CBaseFile *package, CyStringList *errors)
{
        for ( CListNode<C_File> *fNode = m_lUninstallFiles.Front(); fNode; fNode = fNode->next() )
        {
                C_File *uf = fNode->Data();
                // check against any script files
                for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
                {
                        C_File *checkFile = checkNode->Data();
                        if ( !checkFile ) continue;

                        if ( checkFile->GetFileType() != FILETYPE_UNINSTALL && checkFile->GetFileType() != FILETYPE_SCRIPT )
                                continue;

                        if ( uf->GetFilename().Compare(checkFile->GetFilename()) )
                        {
                                if ( RemoveUninstallFile(uf, errors) )
                                        fNode->DeleteData();
                                break;
                        }
                }
        }

        m_lUninstallFiles.RemoveEmpty();

        this->WriteData();
}

int CPackages::InstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *errored, CLinkList<CBaseFile> *installedList)
{
        if ( m_lInstallList.empty() ) return false;

        int installed = 0;
        for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( this->InstallPackage(p, errors, progress, !p->IsEnabled()) )
                {
                        ++installed;
                        if ( installedList )
                                installedList->push_back(p);
                }
                else if ( errored )
                {
                        m_lPackages.remove(p);
                        errored->push_back(p);
                }
        }

        m_lInstallList.clear();

        this->WriteData();

        return installed;
}

bool CPackages::InstallPackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress, bool disabled )
{
        // first check if we are installed a mod
        bool prevDisabled = disabled;
        if ( package->IsMod() && m_pEnabledMod && !m_bForceModInstall )
                disabled = true;
        // if vanilla nad package aint signed
        if ( m_bVanilla && !package->IsSigned() )
                disabled = true;

        // check any depancies
        if ( !disabled && !this->CheckEnabledDependacy(package) )
                disabled = true;

        // search for an old version
        CBaseFile *oldPackage = FindPackage(package);
        if ( oldPackage && oldPackage == m_pEnabledMod && disabled )
                disabled = prevDisabled;

        // update packages must have an old package installed already (should have been checked for already)
        if ( package->GetType() == TYPE_SPK )
        {
                if ( ((CSpkFile *)package)->IsPackageUpdate() )
                {
                        if ( !oldPackage )
                                return false;
                        // change any mods to temp ones
                        for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
                        {
                                if ( f->Data()->GetFileType() != FILETYPE_MOD )
                                        continue;

                                f->Data()->SetDir("temp");
                                if ( f->Data()->IsFakePatch() )
                                        f->Data()->SetName(CyString("Fake_") + f->Data()->GetName());
                        }
                }
        }

        // no need to backup if we're disabling them
        if ( !disabled )
        {
                // find any uninstall files and remove them
                this->PurgeUninstallScripts(package, errors);

                // backup any original files before installing
                CDirIO oDir(m_sCurrentDir + "/PluginManager/Original");
                for ( CListNode<C_File> *oNode = m_lOriginalFiles.Front(); oNode; oNode = oNode->next() )
                {
                        C_File *of = oNode->Data();
                        for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
                        {
                                C_File *f = checkNode->Data();

                                // match the same filetype
                                if ( of->GetFileType() != f->GetFileType() )
                                        continue;

                                // same file
                                if ( of->GetFilename().Compare(f->GetFilename()) )
                                {
                                        // check if original file already exists (assume already backed up)
                                        if ( !BackupOriginalFile(of, errors) )
                                                continue;
                                        break;
                                }
                        }
                }
        }

        // install all the files
        if ( oldPackage )
        {
                CLinkList<CBaseFile> excludeList;
                excludeList.push_back(oldPackage);
                this->UpdateUsedFiles(&excludeList, true);
        }
        else
                this->UpdateUsedFiles(0, true);

        package->ReadAllFilesToMemory();
        if ( !package->InstallFiles (m_sCurrentDir, progress, &m_lFiles, errors, !disabled, this) )
        {
                // TODO: clear up installed files
                return false;
        }

        // if we're installing an addon, lets use the fake patch method for object files
        if ( m_iGameFlags & EXEFLAG_ADDON ) {
                CCatFile cat;
                if ( CCatFile::Opened(cat.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT)) ) {
                        for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() ) {
                                if ( f->Data()->GetFileType() != FILETYPE_SHIPSCENE && f->Data()->GetFileType() != FILETYPE_COCKPITSCENE && f->Data()->GetFileType() != FILETYPE_SHIPMODEL && f->Data()->GetFileType() != FILETYPE_SHIPOTHER )
                                        continue;
                                if ( CCatFile::IsAddonDir(f->Data()->GetNameDirectory(package)) )
                                        continue;
                                if ( cat.AppendFile(f->Data()->GetFilePointer(), f->Data()->GetNameDirectory(package), true, (m_iGameFlags & EXEFLAG_NOXOR) ? false : true) ) {
                                        CFileIO(f->Data()->GetFilePointer()).Remove();
                                        f->Data()->SetFilename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat::" + f->Data()->GetNameDirectory(package));
                                }
                        }

                }
        }

        bool shuffle = false;

        // merge the update into the old package
        bool dontAdd = false;
        if ( package->GetType() == TYPE_SPK )
        {
                if ( ((CSpkFile *)package)->IsPackageUpdate() )
                {
                        // now copy any files from a mod
                        for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
                        {
                                if ( !f->Data() )
                                        continue;
                                if ( f->Data()->GetFileType() != FILETYPE_MOD )
                                        continue;
                                // we only need the cat file
                                if ( !f->Data()->CheckFileExt("cat") )
                                        continue;

                                // if fake patch, find first fake patch in package
                                C_File *findMatching = NULL;
                                if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
                                {
                                        for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
                                        {
                                                if ( !node->Data() )
                                                        continue;
                                                if ( node->Data()->GetFileType() != FILETYPE_MOD )
                                                        continue;
                                                // we only need the cat file
                                                if ( !node->Data()->CheckFileExt("cat") )
                                                        continue;
                                                if ( !node->Data()->IsFakePatch() )
                                                        continue;

                                                findMatching = node->Data();
                                                break;
                                        }
                                }
                                // otherwise, just add to the mod of the same name
                                else
                                {
                                        for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
                                        {
                                                if ( !node->Data() )
                                                        continue;
                                                if ( node->Data()->GetFileType() != FILETYPE_MOD )
                                                        continue;
                                                // we only need the cat file
                                                if ( !node->Data()->CheckFileExt("cat") )
                                                        continue;

                                                if ( node->Data()->GetName().Compare(f->Data()->GetName()) )
                                                {
                                                        findMatching = node->Data();
                                                        break;
                                                }
                                        }
                                }

                                if ( findMatching )
                                {
                                        // copy accross all mods
                                        CCatFile catTo, catFrom;
                                        if ( catFrom.Open(f->Data()->GetFilePointer(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
                                        {
                                                if ( catTo.Open(findMatching->GetFilePointer(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
                                                {
                                                        for ( int i = 0; i < catFrom.GetNumFiles(); i++ )
                                                        {
                                                                SInCatFile *c = catFrom.GetFile(i);                                                              
                                                                catTo.AppendFile(f->Data()->GetFilePointer() + "::" + c->sFile, c->sFile);
                                                        }
                                                }
                                        }

                                        // now remove the files
                                        C_File *m = package->FindMatchingMod(f->Data());
                                        RemoveFile(f->Data(), errors);
                                        m_lFiles.remove(f->Data());
                                        f->ChangeData(NULL);
                                        if ( m )
                                        {
                                                int pos = package->GetFileList()->FindPos(m);
                                                RemoveFile(m, errors);
                                                m_lFiles.remove(m);
                                                if ( pos != -1 )
                                                        package->GetFileList()->GetNode(pos)->ChangeData(NULL);
                                        }
                                }
                                // no matching file, then we shall just renaming back
                                else
                                {
                                        if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
                                        {
                                                shuffle = true;
                                                C_File *match = package->FindMatchingMod(f->Data());
                                                CyString next = CyString::Number(this->FindNextFakePatch()).PadNumber(2);
                                                
                                                CyString oldFilePointer = f->Data()->GetFilePointer();
                                                f->Data()->SetDir("");
                                                f->Data()->ChangeBaseName(next);
                                                if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
                                                        f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));

                                                if ( match )
                                                {
                                                        CyString oldFilePointer = match->GetFilePointer();
                                                        match->SetDir("");
                                                        match->ChangeBaseName(next);
                                                        if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
                                                                match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
                                                }
                                        }
                                        else
                                        {
                                                C_File *match = package->FindMatchingMod(f->Data());

                                                f->Data()->SetDir("");
                                                if ( CFileIO(f->Data()->GetFilePointer()).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
                                                        f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));
                                                if ( match )
                                                {
                                                        match->SetDir("");
                                                        if ( CFileIO(match->GetFilePointer()).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
                                                                match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
                                                }
                                        }
                                }
                        }

                        package->GetFileList()->RemoveEmpty();
                        ((CSpkFile *)oldPackage)->MergePackage(package);
                        delete package;
                        package = oldPackage;
                        oldPackage = false;
                        dontAdd = true;

                        CDirIO(m_sCurrentDir).RemoveDir("temp");
                        CDirIO(m_sCurrentDir).RemoveDir("Mods/temp");
                }
        }

        // if theres an icon, write it
        if ( package->GetIcon() )
        {
                C_File *icon = package->GetIcon();
                if ( !icon->GetData() || !icon->GetDataSize() )
                        package->SetIcon(NULL, "");
                else
                {
                        CDirIO Dir(m_sCurrentDir);
                        bool ready = true;
                        if ( !Dir.Exists("PluginManager") )
                        {
                                if ( !Dir.Create("PluginManager") )
                                        ready = false;
                        }
                        if ( ready && !Dir.Exists("PluginManager/Icons") )
                        {
                                if ( !Dir.Create("PluginManager/Icons") )
                                        ready = false;
                        }

                        if ( ready )
                        {
                                if ( !icon->UncompressData() )
                                        package->SetIcon(NULL, "");
                                else
                                {
                                        CFileIO iconFile(m_sCurrentDir + "/PluginManager/Icons/" + package->GetAuthor() + "_" + package->GetName() + "." + package->GetIconExt());
                                        if ( iconFile.WriteData((const char *)icon->GetData(), icon->GetDataSize()) )
                                        {
                                                icon->SetFilename(package->GetAuthor() + "_" + package->GetName() + "." + package->GetIconExt());
                                                icon->SetFullDir(m_sCurrentDir + "/PluginManager/Icons");
                                        }
                                        else
                                                package->SetIcon(NULL, "");
                                }
                        }

                        if ( package->GetIcon() )
                                package->GetIcon()->DeleteData();
                }
        }

        // remove all data
        package->ClearFileData();

        // add to list
        if ( !dontAdd )
        {
                if ( oldPackage )
                {
                        m_lPackages.insert(oldPackage, package);
                        m_lPackages.remove(oldPackage, false);
                }
                else
                        m_lPackages.push_back (package);
        }

        UpdateUsedFiles();

        if ( disabled )
                package->SetEnabled(false);

        // remove any files no longer used by old package
        if ( oldPackage )
        {
                CListNode<C_File> *fnode = oldPackage->GetFileList()->Front();
                while ( fnode )
                {
                        C_File *f = fnode->Data();

                        // no longer used
                        if ( !f->GetUsed() )
                        {
                                // remove from file list
                                m_lFiles.remove(f, false);

                                // remove from hard drive
                                if ( RemoveFile(f, errors) )
                                {
                                        // if a fake patch, we need to shufle
                                        if ( f->IsFakePatch() )
                                                shuffle = true;
                                        else if ( f->IsAutoTextFile() )
                                                shuffle = true;
                                }
                        }

                        fnode = fnode->next();
                }

        }

        if ( shuffle )
        {
                ShuffleFakePatches(errors);
                ShuffleTextFiles(errors);
        }

        // now we need to link any child/parent packages
        if ( package->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)package;
                if ( spk->IsAnotherMod() )
                        spk->SetParent((CSpkFile *)FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()));
        }

        // store enabled mod
        if ( package->IsMod() && !disabled )
                m_pEnabledMod = package;

        m_bRemoveDir = true;

        this->WriteData();

        return true;
}

bool CPackages::UninstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *uninstalledPackages, CLinkList<CBaseFile> *disabledPackages)
{
        if ( m_lInstallList.empty() ) return false;

        // update the used status, excluding all packages we are about to remove
        UpdateUsedFiles(&m_lInstallList);

        CyStringList removeDirs;
        CLinkList<C_File> uninstallFiles;
        CLinkList<C_File> fileList;
        bool readme = false, original = false, shuffle = false;

        // find all files that need to be removed
        int maxFiles = 0;
        for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                maxFiles += this->GetAllPackageFiles(p, &fileList, true);

                // disable any dependants
                if ( p->GetType() == TYPE_SPK && ((CSpkFile *)p)->IsLibrary() )
                {
                        CLinkList<CBaseFile> depList;
                        if ( this->GetDependacyList(p, &depList) )
                        {
                                for ( CBaseFile *depP = depList.First(); depP; depP = depList.Next() )
                                {
                                        if ( depP->IsEnabled() )
                                                this->PrepareDisablePackage(depP);
                                }

                                if ( m_lDisableList.size() )
                                        this->DisablePreparedPackages(errors, progress, disabledPackages);
                        }
                }
        }

        // interate through all the files in the package
        int fileCount = 0;
        for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();

                // display progress if needed
                if ( progress )
                {
                        progress->UpdateProgress(fileCount++, maxFiles);
                        progress->UpdateFile(f);
                }

                // skip uninstall files
                if ( f->GetFileType() == FILETYPE_UNINSTALL )
                {
                        uninstallFiles.push_back(f);
                        continue;
                }

                // only delete files that are not used
                // if its a shared file, we skip it
                if ( f->GetUsed() || f->IsShared() )
                        continue;

                if ( f->GetFileType() == FILETYPE_README )
                        readme = true;
                else if ( f->GetFileType() == FILETYPE_UNINSTALL || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND || f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_SHIPSCENE || f->GetFileType() == FILETYPE_COCKPITSCENE || f->GetFileType() == FILETYPE_SHIPOTHER || f->GetFileType() == FILETYPE_SHIPMODEL || f->GetFileType() == FILETYPE_ADVERT )
                {
                        CyString dir = f->GetDirectory(NULL);
                        removeDirs.PushBack(dir, true);
                        dir = dir.FindReplace("\\", "/");
                        if ( dir.IsIn("/") )
                        {
                                for ( int i = dir.NumToken("/"); i; i-- )
                                {
                                        CyString remDir = dir.GetToken("/", 1, i);
                                        removeDirs.PushBack(remDir, true);
                                }
                        }
                }

                if ( f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(6).lower() == "extras" )
                        removeDirs.PushBack("Extras", true);

                if ( RemoveFile(f, errors) )
                {
                        // check if its an original file and restore
                        if ( IsOriginalFile(f) )
                        {
                                CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(NULL));
                                if ( of.Exists() )
                                {
                                        original = true;
                                        if ( of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(NULL)) )
                                                this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE, f->GetNameDirectory(NULL), errors);
                                        else
                                                this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE_FAIL, f->GetNameDirectory(NULL), errors);
                                }
                        }
                }
                else // problem removeing (try when the program closes)
                        m_lNonRemovedFiles.PushBack(f->GetFilePointer());

                // check for fake patchs
                if ( f->IsFakePatch() )
                        shuffle = true;
                else if ( f->IsAutoTextFile() )
                        shuffle = true;

                // remove the file from the main list as swell
                m_lFiles.remove(f, false);
                delete f;
        }

        // remove all the packages from memory
        for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                p->GetFileList()->clear();
                m_lPackages.remove(p);

                if ( p == m_pEnabledMod )
                        m_pEnabledMod = NULL;

                if ( uninstalledPackages )
                        uninstalledPackages->push_back(p);
                else
                        delete p;
        }

        m_lInstallList.clear();

        // check unistall files
        if ( !uninstallFiles.empty() )
        {
                removeDirs.PushBack(CyString("PluginManager/Uninstall"));

                // make sure the scripts directory is created, even thou it should always be there anyways
                CDirIO scriptDir(m_sCurrentDir);
                if ( !scriptDir.Exists("scripts") )
                {
                        if ( scriptDir.Create("Scripts") )
                                this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, "Scripts", errors);
                        else
                                this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, "Scripts", errors);
                }

                for ( C_File *uf = uninstallFiles.First(); uf; uf = uninstallFiles.Next() )
                {
                        C_File *newFile = new C_File();
                        newFile->SetFileType(FILETYPE_SCRIPT);
                        newFile->SetFilename(uf->GetFilename());
                        newFile->SetCreationTime(uf->GetCreationTime());

                        // other installed packages use this file as well, copy it
                        CyString newFilename = m_sCurrentDir + "/" + newFile->GetNameDirectory(NULL);
                        CFileIO file(uf->GetFilePointer());

                        if ( uf->GetUsed() )
                        {
                                if ( file.Copy(newFilename) )
                                        this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY, newFile->GetNameDirectory(NULL), errors);
                                else
                                {
                                        this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY_FAIL, newFile->GetNameDirectory(NULL), errors);
                                        delete newFile;
                                        newFile = NULL;
                                }
                        }
                        // otherwise just move it
                        else
                        {
                                if ( file.Rename(newFilename) )
                                        this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE, newFile->GetNameDirectory(NULL), errors);
                                else
                                {
                                        this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE_FAIL, newFile->GetNameDirectory(NULL), errors);
                                        delete newFile;
                                        newFile = NULL;
                                }

                                m_lFiles.remove(uf, false);
                                delete uf;
                        }

                        // add to the list
                        if ( newFile )
                        {
                                // first check if theres a matching one
                                bool found = false;
                                for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
                                {
                                        C_File *checkFile = node->Data();
                                        if ( checkFile->GetFilename().Compare(newFile->GetFilename()) )
                                        {
                                                found = true;
                                                break;
                                        }
                                }

                                // not found, so add it
                                if ( !found )
                                        m_lUninstallFiles.push_back(newFile);
                                else
                                        delete newFile;
                        }
                }
        }
        uninstallFiles.clear();

        // remove all directies that we're not using
        if ( readme )
        {
                removeDirs.PushBack(CyString("PluginManager/Readme"));
                removeDirs.PushBack(CyString("Readme"));
        }
        if ( original )
                removeDirs.PushBack(CyString("PluginManager/Original"));
        removeDirs.PushBack(CyString("PluginManager/Disabled"));
        RemoveUnusedDirectories(removeDirs, errors);

        // finally lets shuffle any fake patchs to fill in gaps
        if ( shuffle )
        {
                ShuffleFakePatches(errors);
                ShuffleTextFiles(errors);
        }

        this->WriteData();

        return true;
}

/**
 * Prepares a package to be uninstalled
 *
 * Adds the package, and all its children onto the uninstall list to be used by UninstallPreparePackages
 */
void CPackages::PrepareUninstallPackage(CBaseFile *package)
{
        // add package to list
        if ( !m_lInstallList.FindData(package) )
                m_lInstallList.push_back(package);

        // add all children
        CLinkList<CBaseFile> children;
        if ( this->GetChildPackages(package, &children, true) )
        {
                for ( CBaseFile *p = children.First(); p; p = children.Next() )
                {
                        if ( !m_lInstallList.FindData(p) )
                                m_lInstallList.push_back(p);
                }
        }
}

bool CPackages::PrepareEnablePackage(CBaseFile *package)
{
        ClearError();

        if ( package->GetParent() && !package->GetParent()->IsEnabled() )
        {
                m_iError = PKERR_NOPARENT;
                return false;
        }

        if ( m_bVanilla && !package->IsSigned() )
        {
                m_iError = PKERR_MODIFIED;
                return false;
        }

        // check if it needs depancies
        if ( package->AnyDependacies() )
        {
                if ( this->GetMissingDependacies(package, NULL, true) )
                {
                        m_iError = PKERR_MISSINGDEP;
                        return false;
                }
        }

        if ( !m_lEnableList.FindData(package) )
        {
                if ( !package->IsEnabled() )
                        m_lEnableList.push_back(package);

                // do all the children as well
                if ( m_bAutoEnableChild )
                {
                        CLinkList<CBaseFile> childList;
                        this->GetChildPackages(package, &childList, true);

                        // add all disabled packages to list
                        for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
                        {
                                if ( !p->IsEnabled() )
                                        m_lEnableList.push_back(p);
                        }
                }
        }

        m_bRemoveDir = true;

        return true;
}

bool CPackages::PrepareDisableForVanilla()
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( !p->IsSigned() && p->IsEnabled() )
                {
                        this->PrepareDisablePackage(p);
                        m_bDisableVanilla = true;
                }
        }

        return true;
}

bool CPackages::PrepareEnableLibrarys()
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( p->GetType() != TYPE_SPK )
                        continue;

                if ( !p->IsEnabled() && ((CSpkFile *)p)->IsLibrary() )
                        this->PrepareEnablePackage(p);
        }

        return true;
}

bool CPackages::PrepareEnableFromVanilla()
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( !p->IsEnabled() && p->IsModifiedEnabled() ) {
                        this->PrepareEnablePackage(p);
                }
        }

        return true;
}

int CPackages::GetDependacyList(CBaseFile *package, CLinkList<CBaseFile> *list)
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( p->IsPackageNeeded(package->GetName(), package->GetAuthor()) )
                        list->push_back(p);
        }

        return list->size();
}

bool CPackages::PrepareDisablePackage(CBaseFile *package)
{
        if ( !m_lDisableList.FindData(package) )
        {
                if ( package->IsEnabled() )
                        m_lDisableList.push_back(package);

                CLinkList<CBaseFile> childList;
                this->GetChildPackages(package, &childList, true);

                // add all disabled packages to list
                for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
                {
                        if ( p->IsEnabled() && !m_lDisableList.FindData(p) )
                                m_lDisableList.push_back(p);
                }

                // if its a library, check for any dependacies
                if ( package->GetType() == TYPE_SPK && ((CSpkFile *)package)->IsLibrary() )
                {
                        CLinkList<CBaseFile> depList;
                        if ( this->GetDependacyList(package, &depList) )
                        {
                                for ( CBaseFile *p = depList.First(); p; p = depList.Next() )
                                {
                                        if ( !m_lDisableList.FindData(p) )
                                                this->PrepareDisablePackage(p);
                                }
                        }
                }
        }
        return true;
}

/**
 * Prepares a package to be installed
 */
int CPackages::PrepareInstallPackage(CBaseFile *package, bool disabled, bool force, int check)
{
        // add package to list
        if ( !m_lInstallList.FindData(package) )
        {
                int error = this->CheckInstallPackage(package, check);
                if ( error == INSTALLCHECK_OK || force )
                {
                        if ( disabled )
                                package->SetEnabled(false);

                        bool added = false;
                        if ( package->GetType() == TYPE_SPK )
                        {
                                // find other mods in the currently added list
                                CSpkFile *spk = (CSpkFile *)package;
                                if ( spk->IsAnotherMod() )
                                {
                                        for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
                                        {
                                                CBaseFile *p = node->Data();
                                                if ( spk->GetOtherName().Compare(p->GetName().ToString()) && spk->GetOtherAuthor().Compare(p->GetAuthor().ToString()) )
                                                {
                                                        m_lInstallList.insert(m_lInstallList.FindPos(p) + 2, package);
                                                        added = true;
                                                        break;
                                                }
                                        }
                                }
                        }

                        if ( !added )
                        {
                                // check if we are a parent
                                for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
                                {
                                        if ( node->Data()->GetType() != TYPE_SPK )
                                                continue;

                                        CSpkFile *spk = (CSpkFile *)node->Data();
                                        if ( !spk->IsAnotherMod() )
                                                continue;

                                        if ( spk->GetOtherName().Compare(package->GetName().ToString()) && spk->GetOtherAuthor().Compare(package->GetAuthor().ToString()) )
                                        {
                                                added = true;
                                                m_lInstallList.insert(node->Data(), package);
                                                break;
                                        }
                                }

                                if ( !added )
                                        m_lInstallList.push_back(package);
                        }

                        return INSTALLCHECK_OK;
                }

                package->SetLoadError(error);
                return error;
        }

        return INSTALLCHECK_ALREADYQUEUED;
}

void CPackages::RemovePreparedInstall(CBaseFile *package)
{
        if ( !package )
        {
                m_lInstallList.MemoryClear();
        }
        else
        {
                if ( m_lInstallList.FindData(package) )
                {
                        m_lInstallList.remove(package, true);
                }
        }
}

char CPackages::ConvertWareTypeBack(int w)
{
        switch ( w )
        {
                case WARES_BIO:
                        return 'b';
                case WARES_ENERGY:
                        return 'e';
                case WARES_FOOD:
                        return 'f';
                case WARES_MINERAL:
                        return 'm';
                case WARES_TECH:
                        return 't';
                case WARES_NATURAL:
                        return 'n';
        }
        return 't';
}
int CPackages::ConvertWareType(char w)
{
        switch ( LOWER(w) )
        {
                case 'b':
                        return WARES_BIO;
                case 'e':
                        return WARES_ENERGY;
                case 'f':
                        return WARES_FOOD;
                case 'm':
                        return WARES_MINERAL;
                case 't':
                        return WARES_TECH;
                case 'n':
                        return WARES_NATURAL;
        }
        return WARES_TECH;
}

void CPackages::SetupShips()
{
        for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
        {
                if ( wNode->Data()->iType != WARETYPE_NONE )
                        wNode->Data()->iType = WARETYPE_DELETED;
        }

        // find any new ships to add to the list
        for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
        {
                if ( pNode->Data()->GetType() != TYPE_XSP )
                        continue;
                CXspFile *p = (CXspFile *)pNode->Data();

                bool found = false;
                for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
                {
                        if ( wNode->Data()->sShipID.Compare(p->GetShipID().c_str()) )
                        {
                                if ( !p->IsEnabled() )
                                        wNode->Data()->iType = WARETYPE_DISABLED;
                                else
                                        wNode->Data()->iType = WARETYPE_ADDED;
                                found = true;
                                wNode->Data()->pPackage = p;
                                wNode->Data()->sShipClass = p->GetShipClass();
                                break;
                        }
                }

                if ( found || !p->IsEnabled() )
                        continue;

                // first find any free
                SGameShip *gw = NULL;
                for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
                {
                        if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
                                gw = wNode->Data();
                        // find an old entry for the ware and add it to the same place
                        if ( wNode->Data()->sShipID.Compare(p->GetShipID().c_str()) )
                        {
                                gw = wNode->Data();
                                break;
                        }
                }

                // none found, create one
                if ( !gw )
                {
                        gw = new SGameShip;
                        gw->sShipID = p->GetShipID();
                        gw->sShipClass = p->GetShipClass();
                        gw->pPackage = p;
                        m_lGameShips.push_back(gw);
                }
                gw->iType = WARETYPE_ADDED;
        }
}

void CPackages::SetupWares()
{
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                for ( CListNode<SGameWare> *wNode = m_lGameWares[i].Front(); wNode; wNode = wNode->next() )
                {
                        if ( wNode->Data()->iType != WARETYPE_NONE )
                                wNode->Data()->iType = WARETYPE_DELETED;
                }
        }

        // find any new wares to add to the list
        for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
        {
                if ( pNode->Data()->GetType() != TYPE_SPK )
                        continue;
                CSpkFile *p = (CSpkFile *)pNode->Data();

                for ( CListNode<SWares> *node = p->GetWaresList()->Front(); node; node = node->next() )
                {
                        SWares *w = node->Data();
                        int wareType = CPackages::ConvertWareType(w->cType);

                        // check if its on the list
                        bool found = false;
                        for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
                        {
                                if ( wNode->Data()->sWareName == w->sID )
                                {
                                        if ( !p->IsEnabled() )
                                                wNode->Data()->iType = WARETYPE_DISABLED;
                                        else
                                                wNode->Data()->iType = WARETYPE_ADDED;
                                        wNode->Data()->pWare = w;
                                        found = true;
                                        break;
                                }
                        }

                        if ( found || !p->IsEnabled() )
                                continue;

                        // first find any free
                        SGameWare *gw = NULL;
                        for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
                        {
                                if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
                                        gw = wNode->Data();
                                // find an old entry for the ware and add it to the same place
                                if ( wNode->Data()->sWareName == w->sID )
                                {
                                        gw = wNode->Data();
                                        break;
                                }
                        }

                        // none found, create one
                        if ( !gw )
                        {
                                gw = new SGameWare;
                                gw->sWareName = w->sID;
                                gw->iPos = m_lGameWares[wareType].size();
                                gw->pWare = w;
                                gw->cType = w->cType;
                                m_lGameWares[wareType].push_back(gw);
                        }
                        gw->iType = WARETYPE_ADDED;
                }
        }
}

/**
 * Closing the current directory
 *
 * When existing, program needs to close the directory
 */
bool CPackages::CloseDir ( CyStringList *errors, CProgressInfo *progress, bool removedir )
{
        if ( m_sCurrentDir.Empty() )
                return true;
        if ( !m_bLoaded )
                return true;

        m_sActiveDir = m_sCurrentDir;

        if ( m_bRenameText )
                CreateLanguageTextFiles(errors);

        this->BackupSaves();

        if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.dat").Exists() )
                CFileIO(m_sCurrentDir + "/mods/PluginManager.dat").Remove();
        if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.cat").Exists() )
                CFileIO(m_sCurrentDir + "/mods/PluginManager.cat").Remove();

        if ( !m_bVanilla )
        {
                // base mode for Reunion
                if ( m_iGame == GAME_X3 && m_pEnabledMod )
                {
                        C_File *fDat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
                        while ( fDat && !fDat->IsFakePatch() && !fDat->CheckFileExt("dat") )
                                fDat = m_pEnabledMod->GetNextFile(fDat);

                        if ( fDat )
                        {
                                C_File *fCat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
                                while ( fCat && !fCat->IsFakePatch() && !fCat->CheckFileExt("cat") && !fCat->GetBaseName().Compare(fDat->GetBaseName()) )
                                        fCat = m_pEnabledMod->GetNextFile(fCat);

                                if ( fCat )
                                {
                                        CFileIO(fDat->GetFilePointer()).Copy(m_sCurrentDir + "/mods/PluginManager.dat");
                                        CFileIO(fCat->GetFilePointer()).Copy(m_sCurrentDir + "/mods/PluginManager.cat");
                                }
                        }
                }
                else if ( m_iGame == GAME_X3 && !m_sSetMod.Empty() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Exists() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").Exists() )
                {
                        CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").Copy(m_sCurrentDir + "/mods/PluginManager.dat");
                        CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Copy(m_sCurrentDir + "/mods/PluginManager.cat");
                }

                if ( !CDirIO(m_sCurrentDir).Exists("mods") )
                        CDirIO(m_sCurrentDir).Create("mods");

                SetupWares();
                SetupShips();
                CreateEMPFile();
                CreateWareFiles();
                CreateDummies();
                CreateComponants();
                CreateTShips();
                CreateCutData();
                CreateBodies();
                CreateAnimations();
                CreateCustomStarts();
                CreateGlobals();
                CreatePluginManagerText();
                RestoreFakePatch();
        }

        RemoveFailedFiles();

        WriteData();
        if ( removedir && m_bRemoveDir )
        {
                m_bRemoveDir = false;
                CyStringList removeDirs;
                removeDirs.PushBack(".");
                RemoveUnusedDirectories(removeDirs, errors);
        }

        m_bLoaded = false;
        return true;
}

CyString CPackages::GetModKey()
{
        return m_gameExe.GetModKey(m_iGame - 1);
}
CyString CPackages::GetSelectedModName()
{
        if ( !m_pEnabledMod )
        {
                if ( m_sSetMod.Empty() && m_iGame == GAME_X3 )
                        return "PluginManager";
                return m_sSetMod;
        }

        if ( !m_pEnabledMod->IsEnabled() )
                return m_sSetMod;

        C_File *f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
        if ( !f )
                return m_sSetMod;

        CyString name = f->GetFilename();
        name = name.Left(-4);
        return name;

}

bool CPackages::RestoreFakePatch()
{
        m_iFakePatch = -1;

        CFileIO catFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat");
        CFileIO datFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat");

        // if only 1 exists, remove it
        if ( catFile.Exists() && !datFile.Exists() )
        {
                if ( !catFile.Remove() )
                        return false;
        }
        else if ( !catFile.Exists() && datFile.Exists() )
        {
                if ( !datFile.Remove() )
                        return false;
        }

        // if both exists, lets rename them
        if ( catFile.Exists() && datFile.Exists() )
        {
                // we need to add the plugin manager file in
                CyString file = m_sTempDir;
                if ( !file.Empty() )
                        file += "/";
                file += "pluginmanagerfake.txt";
                file = file.FindReplace("\\", "/");
                CFileIO fakeFile(file);
                std::vector<CyString> lines;
                lines.push_back("//pluginmanager fake patch");
                fakeFile.WriteFile(&lines);

                CCatFile fakePatch;
                if ( fakePatch.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE )
                {
                        if ( fakePatch.AppendFile(file, "PlugMan\\TFake.pck") )
                        {
                                // find next available fake patch
                                m_iFakePatch = this->FindNextFakePatch();

                                CyString filename = CyString::Number(m_iFakePatch).PadNumber(2);
                                if ( catFile.Rename(m_sCurrentDir + "/" + filename + ".cat") )
                                {
                                        if ( datFile.Rename(m_sCurrentDir + "/" + filename + ".dat") )
                                        {
                                                fakeFile.Remove();
                                                return true;
                                        }

                                        // TODO: restore cat file
                                }
                        }
                }

                fakeFile.Remove();
                return false;
        }

        return true;
}


/**
 * Save package detail to date file
 *
 * Writes the current package list data into pluginmanager.dat file
 */
void CPackages::WriteData()
{
        if ( m_sCurrentDir.Empty() )
                return;

        CyStringList lines;
        CyString version;
        version.FromFloat(GetLibraryVersion(), 2);
        version.Prepend("SpkInstaller: ");
        lines.PushBack(version);

        lines.PushBack(CyString("UpdateTime: ") + (long)m_iLastUpdated);
        if ( m_iFakePatch != -1 )
                lines.PushBack(CyString("FakePatch: ") + (long)m_iFakePatch);
        if ( m_iSaveGame != -1 )
                lines.PushBack(CyString("SaveGame: ") + (long)m_iSaveGame);
        lines.PushBack(CyString("SaveGameManager: ") + (long)m_iSaveGameManager);
        if ( !m_bVanilla )
                lines.PushBack("Modified");
        if ( m_bUsedWare )
                lines.PushBack("UsedWare");
        if ( m_bSurpressProtectedWarning )
                lines.PushBack("SurpressProtectedWarning");
        if ( !m_sSetMod.Empty() )
                lines.PushBack(CyString("SetMod: ") + m_sSetMod);
        lines.PushBack(CyString("ShipBuffer: ") + (long)m_iShipBuffer);
        CyString wareBuffer = "WareBuffers:";
        for ( int i = 0; i < WAREBUFFERS; i++ )
                wareBuffer += CyString(" ") + (long)m_iWareBuffer[i];
        lines.PushBack(wareBuffer);
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                if ( !m_lGameWares[i].size() )
                        continue;
                lines.PushBack(CyString("Wares: ") + (long)i + " " + (long)m_lGameWares[i].size());

                for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
                {
                        SGameWare *gm = node->Data();
                        lines.PushBack(CyString((long)gm->iPos) + " " + (long)gm->iType + " " + CyString((char)gm->cType) + " " + gm->sWareName);
                }
        }

        for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
                lines.PushBack(CyString("NonRemovedFile: ") + str->str);

        if ( m_lGameShips.size() )
        {
                lines.PushBack(CyString("Ships: ") + (long)m_lGameShips.size());

                for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                {
                        SGameShip *gm = node->Data();
                        lines.PushBack(CyString((long)gm->iType) + " $#C:" + gm->sShipClass + " " + gm->sShipID);
                }
        }

        // write created Files
        if ( !m_lCreatedFiles.Empty() )
        {
                for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
                        lines.PushBack(CyString("CreatedFile: ") + node->str.Remove(m_sCurrentDir));
        }

        // write uninstall files
        if ( !m_lUninstallFiles.empty() )
        {
                for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
                {
                        C_File *uf = node->Data();
                        CyString uString = "Uninstall: ";
                        uString += CyString::Number((long)uf->GetCreationTime()) + " ";
                        uString += uf->GetFilename();
                        lines.PushBack(uString);
                }
        }

        // write the original file data
        if ( !m_lOriginalFiles.empty() )
        {
                for ( CListNode<C_File> *node = m_lOriginalFiles.Front(); node; node = node->next() )
                {
                        C_File *uf = node->Data();
                        CyString uString = "Original: ";
                        uString += CyString::Number((long)uf->GetFileType()) + " ";
                        uString += uf->GetFilename();
                        if ( uf->GetFileType() == FILETYPE_EXTRA && !uf->GetDir().Empty())
                        {
                                uString += ":";
                                uString += uf->GetDir();
                        }
                        lines.PushBack(uString);
                }
        }

        // write the global changes
        if ( !m_lGlobals.Empty() )
        {
                for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
                        lines.PushBack(CyString("GlobalSetting: ") + str->str + ":" + str->data.Remove(";"));
        }

        // write the fake patch ordering
        if ( !m_lFakePatchOrder.Empty() )
        {
                for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
                        lines.PushBack(CyString("FakePatchOrder: ") + str->str + ":" + str->data);
        }

        // write the global file list
        lines.PushBack("GlobalFiles:");
        int num = 0;
        for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
        {
                C_File *f = fn->Data();
                f->SetPos(num++);

                CyString line = CyString::Number(f->GetFileType()) + ":" + CyString::Number((long)f->GetCreationTime()) + ":" + f->GetDir() + ":";
                if ( f->IsShared() && !f->IsFakePatch() )
                        line += "1:";
                else
                        line += "0:";

                CyString filename = f->GetFilePointer();
                filename.Remove(m_sCurrentDir);

                if ( f->IsDisabled() )
                        line += "D#";

                line += "G#";
                line += (long)f->GetGame();
                line += "#";

                line += filename;

                if ( !f->GetOriginalName().Empty() )
                {
                        line += "O#";
                        line += f->GetOriginalName();
                }

                lines.PushBack(line);
        }

        // write the package list
        for ( CListNode<CBaseFile> *pn = m_lPackages.Front(); pn; pn = pn->next() )
        {
                CBaseFile *package = pn->Data();

                if ( package->GetType() == TYPE_SPK )
                        lines.PushBack("<script>");
                else if ( package->GetType() == TYPE_XSP )
                        lines.PushBack("<ship>");
                else if ( package->GetType() == TYPE_ARCHIVE )
                        lines.PushBack("<archive>");
                else if ( package->GetType() == TYPE_BASE )
                        lines.PushBack("<base>");
                else
                        continue;

                if ( !package->GetFilename().Empty() )
                        lines.PushBack(CyString("Installspk: ") + package->GetFilename());

                CyString valuesline = package->CreateValuesLine();
                if ( valuesline.Right(1) == '\n' )
                        valuesline.Truncate((int)valuesline.Length() - 1);
                lines.PushBack(valuesline);

                if ( !package->IsEnabled() )
                        lines.PushBack("Disabled");
                if ( !package->IsModifiedEnabled() )
                        lines.PushBack("ModifiedDisabled");

                if ( package->GetIcon() )
                        lines.PushBack(CyString("Icon: ") + package->GetIconExt() + " " + package->GetIcon()->GetFilePointer() );

                CyString fileline("Files:");
                for ( CListNode<C_File> *fn = package->GetFileList()->Front(); fn; fn = fn->next() )
                {
                        C_File *f = fn->Data();
                        fileline += " ";
                        fileline += CyString::Number(f->GetPos());
                }
                lines.PushBack(fileline);
        }

        lines.PushBack("</scripts>");

        CFileIO datFile(m_sCurrentDir + "/PluginManager/PluginManager.dat");

        CDirIO Dir(m_sCurrentDir);
        if ( !Dir.Exists("PluginManager") )
                Dir.Create("PluginManager");


        if ( !datFile.WriteFile(&lines) )
        {
        }
}


/**
 * Get All Files
 *
 * Gets a list of all files, includes any child package files
 */
int CPackages::GetAllPackageFiles(CBaseFile *package, CLinkList<C_File> *fileList, bool includeChild)
{
        for ( CListNode<C_File> *node = package->GetFileList()->Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( !fileList->FindData(f) )
                        fileList->push_back(f);
        }

        if ( includeChild )
        {
                CLinkList<CBaseFile> childList;
                if ( this->GetChildPackages(package, &childList) )
                {
                        for ( CBaseFile *child = childList.First(); child; child = childList.Next() )
                                this->GetAllPackageFiles(child, fileList, includeChild);
                }
        }

        // disablign for vanilla, make sure we add all files
        if ( m_bDisableVanilla )
        {
                for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
                {
                        C_File *f = node->Data();
                        if ( !fileList->FindData(f) )
                                fileList->push_back(f);
                }
        }

        return fileList->size();
}

int CPackages::GetAllPackageFiles(CLinkList<CBaseFile> *list, CLinkList<C_File> *fileList, bool includeChild)
{
        for ( CListNode<CBaseFile> *node = list->Front(); node; node = node->next() )
                this->GetAllPackageFiles(node->Data(), fileList, includeChild);
        return fileList->size();
}

/**
 * Add Log
 *
 * Adds a log entry to displayed at end
 */
void CPackages::AddLogEntry(int type, CyString args, CyStringList *errors)
{
        if ( !errors )
                return;

        errors->PushBack(args, ERRORLOG(type));
}

/**
 * Enable a package
 *
 * Enables all files in the package, unless they are already enabled, ie, used by other packages
 * Backs up any original files before attempting to enable the file
 *
 * Fake patches are renamed to the next available slot
 *
 * Clears up the "PluginManager/Disabled" directory
 *
 * param: package               - The package class to be removed
 * param: errors                - The string list for all the status for debugging, ie has an entry for whats happened to every file
 * param: progress              - The progress class, updates a progress screen of the derived class
 *
 * return: boolen - Returns true if the package enabling was successful
 */
bool CPackages::EnablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
{
        ClearError();

        // if already enabled, just skip
        if ( package->IsEnabled() )
                return true;

        // check if parent is enabled
        if ( package->GetParent() && !package->GetParent()->IsEnabled() )
        {
                m_iError = PKERR_NOPARENT;
                return false;
        }

        // check for modified
        if ( m_bVanilla && !package->IsSigned() )
        {
                m_iError = PKERR_MODIFIED;
                return false;
        }

        if ( this->PrepareEnablePackage(package) )
                return this->EnablePreparedPackages(errors, progress);

        return false;
}
bool CPackages::EnablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *enabledPackages )
{
        ClearError();

        // get all files, including children
        CLinkList<C_File> fileList;
        int maxFiles = this->GetAllPackageFiles(&m_lEnableList, &fileList, false);

        int fileCount = 0;
        // we use this list to match cat and dat files
        CyStringList fakePatches;
        for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetGame() && m_iGame ) {
                        if ( f->GetGame() != m_iGame )
                                continue;
                }
                CBaseFile *package = NULL;
                for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
                {
                        CBaseFile *p = node->Data();
                        if ( p->IsFileAdded(f) )
                        {
                                package = p;
                                break;
                        }
                }

                if ( progress )
                {
                        progress->UpdateProgress(fileCount++, maxFiles);
                        progress->UpdateFile(f);
                }

                // only move fiels that are disabled
                if ( !f->IsDisabled() )
                        continue;

                // make the directory if it dont exist
                CDirIO Dir(m_sCurrentDir);

                // fake patches are in the root, no need for directory
                if ( !f->IsFakePatch() )
                {
                        if ( !Dir.Exists(f->GetDirectory(package)) )
                        {
                                if ( !Dir.Create(f->GetDirectory(package)) )
                                {
                                        this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, f->GetDirectory(package), errors);
                                        continue;
                                }
                                this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, f->GetDirectory(package), errors);
                        }
                }

                // check if theres an original file to backup
                C_File *of = GetOriginalFile(f);
                if ( of )
                {
                        // check if the orignal file is already backed up
                        if ( !CFileIO(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(package)).Exists() )
                        {
                                // lets back up the file now
                                BackupOriginalFile(of, errors);
                        }
                }

                CyString newFilename = f->GetNameDirectory(package);

                // fake patches need to be renamed
                if ( f->IsFakePatch() )
                {
                        // first check if the matching file has been done already
                        SStringList *foundStr = fakePatches.FindString(f->GetBaseName());

                        // already done one, simply change this to match
                        if ( foundStr )
                                newFilename = foundStr->data + "." + f->GetFileExt();

                        // we need to find the next available number instead
                        else
                        {
                                CyString newPos = CyString::Number(FindNextFakePatch()).PadNumber(2);
                                newFilename = newPos + "." + f->GetFileExt();
                                // and wee need to push this onto the string list so we can adjust the matchign pair
                                fakePatches.PushBack(f->GetBaseName(), newPos);
                        }
                }

                // lets actually move the file back now
                // !!error checking!!
                CFileIO currentFile(f->GetFilePointer());
                CFileIO newFile(m_sCurrentDir + "/" + newFilename);
                if ( !currentFile.Exists() )
                {
                        // missing file ??
                        if ( !newFile.Exists() )
                        {
                                this->AddLogEntry(SPKINSTALL_MISSINGFILE, newFilename, errors);
                                continue;
                        }
                }
                // remove existing file
                // file exists, so lets try to move it
                else
                {
                        if ( newFile.Exists() )
                                newFile.Remove();

                        if ( !currentFile.Rename(newFile.GetFullFilename()) )
                        {
                                this->AddLogEntry(SPKINSTALL_ENABLEFILE_FAIL, newFilename, errors);
                                continue;
                        }
                }

                this->AddLogEntry(SPKINSTALL_ENABLEFILE, newFilename, errors);

                // adjust the internal name to match the new filename
                f->SetFilename(m_sCurrentDir + "/" + newFilename);
                // no longer disabled, we need to remove the flag
                f->SetDisabled(false);
        }

        // recursive, auto enable all children
        CBaseFile *oldMod = m_pEnabledMod;
        CBaseFile *pMod = NULL;

        for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                p->SetEnabled(true);
                if ( p->IsMod() && !pMod )
                        pMod = p;

                if ( enabledPackages )
                        enabledPackages->push_back(p);
        }

        if ( pMod )
                m_pEnabledMod = pMod;

        // disabled the mod
        if ( oldMod && oldMod != m_pEnabledMod && !m_bForceModInstall )
                this->DisablePackage(oldMod, errors, progress);

        // lets remove all the directories we might have left empty
        CyStringList removeDirs;
        removeDirs.PushBack(CyString("PluginManager/Disabled"));
        RemoveUnusedDirectories(removeDirs, errors);

        m_lEnableList.clear();

        this->WriteData();

        return true;
}


bool CPackages::DisablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *disabledPackages )
{
        if ( progress )
                progress->UpdateStatus(PROGRESS_DISABLEFILE);

        UpdateUsedFiles(&m_lDisableList, false);

        // checks if there are any original files to restore and if any fake patches were disabled to reshuffle
        bool original = false, shuffle = false;

        // holds our list of directories that we might need to remove, only empty ones from this list will actually be removed
        CyStringList removeDirs;

        // get all files, including children
        CLinkList<C_File> fileList;
        int maxFiles = this->GetAllPackageFiles(&m_lDisableList, &fileList, true);

        // interate through all the files in the package
        int fileCount = 0;
        for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                CBaseFile *checkPackage = NULL;
                for ( CListNode<CBaseFile> *node = m_lDisableList.Front(); node; node = node->next() )
                {
                        CBaseFile *p = node->Data();
                        if ( p->IsFileAdded(f) )
                        {
                                checkPackage = p;
                                break;
                        }
                }

                // update the progress count for the current file
                if ( progress )
                {
                        progress->UpdateProgress(++fileCount, maxFiles);
                        progress->UpdateFile(f);
                }

                // only delete files that are not used by any other enabled packages, counter from UpdateUsedFiles()
                if ( f->GetUsed() || (f->IsShared() && !m_bDisableVanilla) )
                        continue;

                // file is already disabled, no need to disable again
                if ( f->IsDisabled() )
                        continue;

                // readmes, uninstall and extra files dont need to be disabled
                // Extra files not in the "Extras" directory could be anywhere, so these should be disabled incase they are game changing files, ie in "types"
                if ( f->GetFileType() == FILETYPE_README || f->GetFileType() == FILETYPE_UNINSTALL || (f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(5).lower() == "Extra") )
                        continue;

                // check if there is a matching uninstall file, ie there the script file is also an uninstall script file for a previously uninstalled package that has yet to be removed
                if ( f->GetFileType() == FILETYPE_SCRIPT )
                {
                        bool found = false;
                        for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
                        {
                                C_File *uFile = uNode->Data();
                                if ( uFile->GetFilename().Compare(f->GetFilename()) )
                                {
                                        found = true;
                                        break;
                                }
                        }

                        if ( found )
                                continue;
                }

                if ( f->GetFileType() == FILETYPE_MOD && !f->IsFakePatch() && f->CheckFileExt("cat") )
                {
                        if ( f->GetBaseName().Compare(m_sSetMod) )
                                m_sSetMod = NullString;
                }

                // file is not being used by any enabled package
                // set disabled and move to disabled directory
                CDirIO Dir(m_sCurrentDir);
                CyString newFilename = "PluginManager/Disabled/";

                // fake patches have thier own special directory
                if ( f->IsFakePatch() )
                        newFilename += "FakePatches";
                // otherwise we put them in thier usual directory structure inside the disabled dir
                else
                        newFilename += f->GetDirectory(NULL);

                // make sure the directory exists so we can move the file
                if ( !Dir.Exists(newFilename) )
                {
                        // we couldn't create the directory for some reason, this is not good
                        if ( !Dir.Create(newFilename) )
                        {
                                this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newFilename, errors);
                                continue;
                        }
                        this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, newFilename, errors);
                }

                // fake patches need a special directory and filename so they dont overright each other as thier filenames are always changes
                // if a package with a fake patch is installed while another one is disabled, it will fill the gap left by the disabled one
                // they will then end up with the same filename, if it gets disabled they would overright each other, so we change the filename to prevent that from happening
                if ( f->IsFakePatch() )
                {
                        // find package the fake patch belongs to
                        if ( checkPackage )
                        {
                                newFilename = m_sCurrentDir + "/PluginManager/Disabled/FakePatches/FakePatch_" + checkPackage->GetNameValidFile() + "_" + checkPackage->GetAuthor() + "_" + f->GetName();
                                shuffle = true;
                        }
                }
                else if ( f->IsAutoTextFile() )
                {
                        if ( checkPackage )
                        {
                                newFilename = m_sCurrentDir + "/PluginManager/Disabled/TextFiles/Text_" + checkPackage->GetNameValidFile() + "_" + checkPackage->GetAuthor() + "_" + f->GetName();
                                shuffle = true;
                        }
                }
                // otherwise we can just use the standard filename
                else
                        newFilename = m_sCurrentDir + "/PluginManager/Disabled/" + f->GetNameDirectory(checkPackage);

                // now to move the file by renameing it to its new location
                // !!error checking!!
                // check the file, if it doesn't exist, and exists as disabled, we should just adjust the setting instead of an error
                CFileIO currentFile(f->GetFilePointer());
                if ( !currentFile.Exists() )
                {
                        if ( !CFileIO(newFilename).Exists() )
                        {
                                this->AddLogEntry(SPKINSTALL_MISSINGFILE, f->GetNameDirectory(checkPackage), errors);
                                continue;
                        }
                }
                // otherwise the file must exists, so lets move it
                else if ( !currentFile.Rename(newFilename) )
                {
                        this->AddLogEntry(SPKINSTALL_DISABLEFILE_FAIL, f->GetNameDirectory(checkPackage), errors);
                        continue;
                }

                // must have been fine
                this->AddLogEntry(SPKINSTALL_DISABLEFILE, f->GetNameDirectory(checkPackage), errors);

                // check if its an original file and restore
                if ( IsOriginalFile(f) )
                {
                        CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(checkPackage));
                        if ( of.Exists() )
                        {
                                original = true;
                                if ( of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(checkPackage)) )
                                        this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE, f->GetNameDirectory(checkPackage), errors);
                                else
                                        this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE_FAIL, f->GetNameDirectory(checkPackage), errors);
                        }
                }

                // extra file thats not in the extras directory
                if ( f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND )
                        removeDirs.PushBack(f->GetDirectory(checkPackage), NullString, true);

                // change the filename
                f->SetFilename(newFilename);

                // finally mark the file as disabled so we know not to try to move it again
                f->SetDisabled(true);
        }

        // a fake patch has been disabled, we need to reshuffle the rest to fill in any gaps
        if ( shuffle )
        {
                if ( progress )
                        progress->UpdateStatus(PROGRESS_SHUFFLEFAKE);
                ShuffleFakePatches(errors);
                ShuffleTextFiles(errors);
        }

        // original files were restored, check to remove the original file directory if its now empty
        if ( original )
                removeDirs.PushBack(CyString("PluginManager/Original"));

        // remove any empty directories that we might have left
        if ( !removeDirs.Empty() )
                RemoveUnusedDirectories(removeDirs, errors);

        // finally mark the whole package as disabled
        // recursive, we need to disable all children
        for ( CBaseFile *child = m_lDisableList.First(); child; child = m_lDisableList.Next() )
        {
                if ( m_pEnabledMod == child )
                        m_pEnabledMod = NULL;
                child->SetEnabled(false);

                if ( disabledPackages )
                        disabledPackages->push_back(child);
        }

        // disabling has completed successfully, we hope
        m_bDisableVanilla = false;
        m_lDisableList.clear();

        this->WriteData();

        return true;
}

/**
 * Disables the selected package
 *
 * Disables all enabled files that are not being used by any other enabled package
 * Any files that are being used by other enabled packages are skipped and left enabled
 *
 * Original Files are restored when file is disabled
 *
 * Fake patches are shuffled to fill in any gaps caused by disabling fake patches
 *
 * All files go into the Plugin/Disabled directory into thier respective directories.
 *
 * Any directories left empty when disabling files are then removed
 *
 * param: package       - Package file to be disabled
 * param: errors        - A string list used to add the status as it progresses, used in debugging output
 * param: progress      - The progress class, updates the progress of the current disabling.  Needs a divered class to report the progress somewhere
 *
 * return: boolean, true if there was no errors, otherwise false
 */
bool CPackages::DisablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
{
        // if already disabled, just skip
        if ( !package->IsEnabled() )
                return true;

        m_lDisableList.clear();
        if ( this->PrepareDisablePackage(package) )
                return this->DisablePreparedPackages(errors, progress);

        return false;
}


/**
 * Find a Package
 *
 * Finds a matching package so we can find if one is already installed
 *
 * Uses seperate functions for each package type, ie for SPK and XSP packages
 */
CBaseFile *CPackages::FindPackage(CBaseFile *package)
{
        // no point checking if we've been sent a null pointer
        if ( !package )
                return 0;

        // we are checking against a SPK package, so we match the name and author
        if ( package->GetType() == TYPE_SPK )
                return FindSpkPackage(package->GetName(), package->GetAuthor());
        else if ( package->GetType() == TYPE_XSP )
                return FindXspPackage(((CXspFile *)package)->GetShipID());
        else if ( package->GetType() == TYPE_ARCHIVE )
                return FindArchivePackage(package->GetName());

        // nothing found obviously
        return 0;
}

CBaseFile *CPackages::FindFirstPackageWithFile(C_File *f)
{
        return FindNextPackageWithFile(NULL, f);
}

CBaseFile *CPackages::FindNextPackageWithFile(CBaseFile *p, C_File *f)
{
        bool startCheck = (p) ? false : true;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if ( startCheck )
                {
                        if ( node->Data()->IsFileAdded(f) )
                                return node->Data();
                }
                else if ( p == node->Data() )
                        startCheck = true;
        }

        return NULL;
}


/**
 * Find a File
 *
 * Searches for a file matching the filetype and filename
 * Optional dir is used for extras files
 */
C_File *CPackages::FindFile(int filetype, CyString filename, CyString dir)
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() != filetype )
                        continue;

                if ( !f->GetFilename().Compare(filename) )
                        continue;

                if ( !dir.Empty() && f->GetDir().Compare(dir) )
                        continue;

                return f;
        }

        return NULL;
}

CArchiveFile *CPackages::FindArchivePackage(CyString name)
{
        // interate through all packages
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *file = node->Data();

                // only look for archive packages
                if ( file->GetType() != TYPE_ARCHIVE )
                        continue;

                // now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
                if ( file->GetName().Compare(name) )
                        return (CArchiveFile *)file;
        }

        // nothing found
        return 0;
}

/**
 * Find a SPK Package
 *
 * This searching all installed packages for a SPK Package matching the name and author
 */
CBaseFile *CPackages::FindSpkPackage(CyString name, CyString author)
{
        // interate through all packages
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *file = node->Data();

                // only look for spk packages
                if ( file->GetType() != TYPE_SPK )
                        continue;

                // now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
                if ( file->GetName().Compare(name) && file->GetAuthor().Compare(author) )
                        return file;
        }

        // nothing found
        return 0;
}

CBaseFile *CPackages::FindPackage(CyString name, CyString author)
{
        // interate through all packages
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *file = node->Data();

                // now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
                if ( file->GetName().Compare(name) && file->GetAuthor().Compare(author) )
                        return file;
        }

        // nothing found
        return 0;
}

/**
 * Find a XSP Package
 *
 * This searching all installed packages for a XSP Package matching the object id
 */
CBaseFile *CPackages::FindXspPackage(CyString id)
{
        // interate through all packages
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *file = node->Data();

                // only look for spk packages
                if ( file->GetType() != TYPE_XSP )
                        continue;

                // now compare the id, "Compare" is a non case senseative check, opposed to ==.
                if ( ((CXspFile *)file)->GetShipID().Compare(id.ToString()) )
                        return file;
        }

        // nothing found
        return 0;
}

/**
 * Update the used files count
 *
 * counts how many packages are currently using the file
 */
void CPackages::UpdateUsedFiles(CLinkList<CBaseFile> *ignoreList, bool includedisabled)
{
        // clear amounts for all files
        CListNode<C_File> *fnode = m_lFiles.Front();
        while ( fnode )
        {
                fnode->Data()->ClearUsed();
                fnode = fnode->next();
        }

        // update for all packages
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node= node->next() )
        {
                CBaseFile *file = node->Data();
                if ( (ignoreList) && (ignoreList->FindData(file)) )
                        continue;
                if ( !includedisabled && !file->IsEnabled() )
                        continue;
                // now mark all files
                CListNode<C_File> *fnode = file->GetFileList()->Front();
                while ( fnode )
                {
                        fnode->Data()->IncUsed();
                        fnode = fnode->next();
                }
        }
}

/**
 * Removes all empty directories that might have been created
 */
void CPackages::RemoveUnusedDirectories(CyStringList &dirs, CyStringList *errors)
{
        CDirIO Dir(m_sCurrentDir);
        for ( SStringList *str = dirs.Head(); str; str = str->next )
        {
                CyString dir = str->str;

                CyStringList removedDir;
                if ( Dir.RemoveDir(dir, false, true, &removedDir) || !removedDir.Empty() )
                {
                        for ( SStringList *node = removedDir.Head(); node; node = node->next )
                        {
                                CyString displayName = node->str;
                                displayName = displayName.Remove(m_sCurrentDir);
                                this->AddLogEntry(SPKINSTALL_REMOVEDIR, displayName, errors);
                        }
                }
        }
}

/**
 * Update signed status
 *
 * Updates the signed status of all packages
 */
void CPackages::UpdateSigned()
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();

                if ( p->GetType() != TYPE_SPK )
                        continue;

                ((CSpkFile *)p)->UpdateSigned(false);
        }
}

/**
 * Check validity of package file
 *
 * Checks if there is a newer version of the package installed
 */
int CPackages::CheckInstallPackage(CBaseFile *package, int check)
{
        if ( package->GetType() == TYPE_XSP && m_iGame == GAME_X2 )
                return INSTALLCHECK_NOSHIP;

        // search for an old version
        CBaseFile *oldPackage = FindPackage(package);

        // check versions are newer
        if (oldPackage && (check & IC_OLDVERSION))
        {
                if ( oldPackage->GetVersion().CompareVersion(package->GetVersion()) == COMPARE_OLDER )
                        return INSTALLCHECK_OLDVERSION;
        }

        // now check for game version
        if ((check & IC_WRONGGAME) || (check & IC_WRONGVERSION))
        {
                if ( package->AnyGameCompatability() )
                {
                        if ( (check & IC_WRONGGAME) && (!package->CheckGameCompatability(m_iGame)) )
                                return INSTALLCHECK_WRONGGAME;
                        else if ( (check & IC_WRONGVERSION) && (!package->CheckGameVersionCompatability(m_iGame, m_sGameVersion, m_iGameVersion)) )
                                return INSTALLCHECK_WRONGVERSION;
                }
        }

        // check for modified
        if (m_bVanilla && (check & IC_MODIFIED))
        {
                if ( !package->IsSigned() )
                        return INSTALLCHECK_MODIFIED;
        }

        return INSTALLCHECK_OK;
}

bool CPackages::CheckOtherPackage(CBaseFile *package)
{
        // check the need for another mod
        if ( package->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)package;
                if ( spk->IsAnotherMod() )
                {
                        if ( !FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()) )
                        {
                                // check the install list
                                if ( m_lInstallList.empty() )
                                        return false;

                                bool found = false;
                                for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
                                {
                                        if ( spk->GetOtherName().Compare(node->Data()->GetName().ToString()) && spk->GetOtherAuthor().Compare(node->Data()->GetAuthor().ToString()) )
                                        {
                                                found = true;
                                                break;
                                        }
                                }

                                if ( !found )
                                        return false;
                        }
                }
        }

        return true;
}

bool CPackages::CheckEnabledDependacy(CBaseFile *p)
{
        // check the for another mod
        if ( p->GetType() == TYPE_SPK )
        {
                if ( ((CSpkFile *)p)->IsAnotherMod() )
                {
                        CBaseFile *parent = this->FindSpkPackage(((CSpkFile *)p)->GetOtherName(), ((CSpkFile *)p)->GetOtherAuthor());
                        if ( parent )
                        {
                                if ( !parent->IsEnabled() )
                                        return false;
                        }
                        else
                                return false;
                }
        }

        // check any dependacies
        if ( p->AnyDependacies() )
        {
                for ( CListNode<SNeededLibrary> *dNode = p->GetNeededLibraries()->Front(); dNode; dNode = dNode->next() )
                {
                        if ( dNode->Data()->sName.Compare("<package>") )
                                continue;
                        if ( !this->CheckInstalledDependacy(dNode->Data()->sName, dNode->Data()->sAuthor, dNode->Data()->sMinVersion, true, true) )
                                return false;
                }
        }

        return true;
}

bool CPackages::CheckInstalledDependacy(CyString name, CyString author, CyString version, bool onlyEnabled, bool includePrepared)
{
        CBaseFile *p = this->FindSpkPackage(name, author);
        if ( p )
        {
                // now check version
                if ( version.CompareVersion(p->GetVersion()) == COMPARE_OLDER )
                        return false;

                if ( onlyEnabled && !p->IsEnabled() )
                        return false;

                return true;
        }

        // now check the prepared list
        if ( includePrepared )
        {
                for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
                {
                        p = node->Data();
                        if ( !p )
                                continue;

                        if ( p->GetName().Compare(name) && p->GetAuthor().Compare(author) )
                        {
                                if ( version.CompareVersion(p->GetVersion()) == COMPARE_OLDER )
                                        continue;

                                if ( onlyEnabled && !p->IsEnabled() )
                                        continue;

                                return true;
                        }
                }

                m_lInstallList.RemoveEmpty();
        }

        return false;
}

int CPackages::GetMissingDependacies(CBaseFile *p, CyStringList *list, bool onlyEnabled, bool includePrepared)
{
        int count = 0;
        CLinkList<SNeededLibrary> *neededList = p->GetNeededLibraries();
        if ( neededList )
        {
                for ( CListNode<SNeededLibrary> *node = neededList->Front(); node; node = node->next() )
                {
                        SNeededLibrary *nl = node->Data();
                        if ( !CheckInstalledDependacy((nl->sName.Compare("<package>")) ? p->GetName() : nl->sName, (nl->sAuthor.Compare("<author>")) ? p->GetAuthor() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare("<package>")) ? false : onlyEnabled, (nl->sName.Compare("<package>")) ? false : includePrepared) )
                        {
                                if ( list )
                                        list->PushBack(((nl->sName.Compare("<package>")) ? p->GetName() : nl->sName) + "|" + nl->sMinVersion, (nl->sAuthor.Compare("<author>")) ? p->GetAuthor() : nl->sAuthor);
                                ++count;
                        }
                }
        }

        if ( p->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)p;
                if ( spk->IsAnotherMod() )
                {
                        bool found = true;
                        if ( !this->FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()) )
                        {
                                if ( includePrepared )
                                {
                                        found = false;
                                        for ( CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next() )
                                        {
                                                CBaseFile *checkP = pNode->Data();
                                                if ( p->GetAuthor().Compare(checkP->GetAuthor()) && p->GetName().Compare(checkP->GetName()) )
                                                {
                                                        found = true;
                                                        break;
                                                }
                                        }
                                }
                                else
                                        found = false;
                        }

                        if ( !found )
                        {
                                if ( list )
                                        list->PushBack(CyString(spk->GetOtherName()), CyString(spk->GetOtherAuthor()));
                                ++count;
                        }
                }
        }

        return count;
}

int CPackages::CheckPreparedInstallRequired(CLinkList<CBaseFile> *list)
{
        // loop through all packages
        int count = 0;
        for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();

                // no requirements found, remove from list
                bool missingDep = false;
                if ( !this->CheckOtherPackage(p) )
                        missingDep = true;
                else if ( this->GetMissingDependacies(p, NULL, false, true) )
                        missingDep = true;

                if ( missingDep )
                {
                        if ( list )
                        {
                                node->ChangeData(NULL);
                                list->push_back(p);
                        }
                        else
                                node->DeleteData();
                        ++count;
                }
        }

        m_lInstallList.RemoveEmpty();

        return count;
}


/**
 * Remove uninstall file
 *
 * Removes a single uninstall file
 */
bool CPackages::RemoveUninstallFile(C_File *file, CyStringList *errors)
{
        CFileIO fio(file->GetFilePointer());
        if ( fio.Exists() )
        {
                if ( fio.Remove() )
                {
                        this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE, file->GetNameDirectory(NULL), errors);
                        return true;
                }
                else if ( errors )
                        this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE_FAIL, file->GetNameDirectory(NULL), errors);
        }

        return false;
}

/**
 * Remove uninstall scripts
 *
 * Removes any unused unisntall scripts
 * Finds if any scripts are in the current package
 */
int CPackages::RemoveUninstallScripts(CyStringList *errors, CProgressInfo *progress)
{
        if ( m_lUninstallFiles.empty() )
                return 0;

        UpdateUsedFiles();
        int files = 0;

        for ( CListNode<C_File> *node = m_lUninstallFiles.Back(); node; node = node->prev() )
        {
                if ( progress )
                        progress->UpdateProgress(files, m_lUninstallFiles.size());
                files++;

                C_File *file = node->Data();

                // first check if there is a matching file
                bool found = false;
                for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
                {
                        C_File *checkFile = fNode->Data();
                        if ( checkFile->GetFileType() != FILETYPE_SCRIPT )
                                continue;

                        if ( checkFile->GetFilename().Compare(file->GetFilename()) )
                        {
                                found = true;
                                break;
                        }
                }

                // not found a matching file, we can safetly remove it
                if ( !found )
                {
                        if ( RemoveUninstallFile(file) )
                                node->DeleteData();
                }
        }

        m_lUninstallFiles.RemoveEmpty();

        return files;
}


/**
 * Remove unused shared file
 *
 * Removes a single file
 */
bool CPackages::RemoveSharedFile(C_File *file, CyStringList *errors)
{
        CFileIO fio(file->GetFilePointer());
        if ( fio.Exists() )
        {
                if ( fio.Remove() )
                {
                        this->AddLogEntry(SPKINSTALL_SHARED, file->GetNameDirectory(NULL), errors);
                        delete file;
                        return true;
                }
                else if ( errors )
                        this->AddLogEntry(SPKINSTALL_SHARED_FAIL, file->GetNameDirectory(NULL), errors);
        }

        return false;
}

/**
 * Remove Unused Shared Files
 *
 * Files that have been marked as shared will not be removed or disabled when the last package is removed
 * This function will remove any that are no longer connected with packages
 *
 * Marked shared fiels are mainly used for library scripts that arn't always added to packages
 */
int CPackages::RemoveUnusedSharedFiles(CyStringList *errors, CProgressInfo *progress)
{
        UpdateUsedFiles();
        int files = 0;
        int done = 0;

        for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
        {
                if ( progress )
                        progress->UpdateProgress(files, m_lFiles.size());
                files++;

                C_File *file = node->Data();

                // only do marked shared files
                if ( !file->IsShared() )
                        continue;

                // only do ones that are no longer needed
                if ( file->GetUsed() )
                        continue;

                if ( RemoveSharedFile(file, errors) )
                        ++done;
                node->ChangeData(NULL);
        }

        m_lFiles.RemoveEmpty();

        return done;
}

/**
 * Any Unused Shared
 *
 * Checks if theres any unused shared files available
 *
 * Any file thats marked as shared, and is no longer connected to any installed package
 */
bool CPackages::AnyUnusedShared()
{
        UpdateUsedFiles();

        for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
        {
                C_File *file = node->Data();

                // only do marked shared files
                if ( !file->IsShared() )
                        continue;

                // only do ones that are no longer needed
                if ( file->GetUsed() )
                        continue;

                return true;
        }

        return false;

}

/**
 * Update original files list
 *
 * Scan the current directory for any fiels that are already there, ie ones that have not been installed
 * Original files should be all the scripts/sounds, etc that are in an unmodified game directory
 *
 * Save list of files and check if any packages overright these files
 * Any file that gets overrighten from this list will automatically be backed up, and restored once the packages are removed
 */
void CPackages::StoreOriginalFiles(int filetype, CyString searchPath)
{
        CyString ext = "pck";
        switch ( filetype )
        {
                case FILETYPE_SOUND:
                        ext = "mp3";
                        break;
        }

        CDirIO Dir(m_sCurrentDir + "/" + searchPath);

        CyStringList *files = Dir.DirList();
        if ( !files )
                return;

        for ( SStringList *node = files->Head(); node; node = node->next )
        {
                CFileIO File(Dir.File(node->str));

                if ( File.GetFileExtension().Compare(ext) )
                        AddOriginalFile(filetype, File.GetFilename(), searchPath);
        }

        delete files;
}

/**
 * Adds a file onto the original files list
 *
 * Checks if it already exists so we dont create multiples
 */
void CPackages::AddOriginalFile(int filetype, CyString filename, CyString searchPath)
{
        // dont add plugin manager as original files
        if ( filetype == FILETYPE_SCRIPT && filename.IsIn("!init.pmanager") )
                return;
        // first check if a matching one exists
        for ( CListNode<C_File> *node = m_lOriginalFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != filetype )
                        continue;

                if ( file->GetFilename().Compare(filename) )
                        return;
        }

        // check that no packages are currently using them either, not original if being used already
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != filetype )
                        continue;

                if ( file->GetFilename().Compare(filename) )
                        return;
        }

        C_File *of = new C_File(filename);
        of->SetFileType(filetype);
        if ( filetype == FILETYPE_EXTRA )
                of->SetDir(searchPath);
        of->SetFilename(m_sCurrentDir + "/" + of->GetNameDirectory(NULL));

        m_lOriginalFiles.push_back(of);
}

/**
 * Check for original file
 *
 * Checks if the file is an original file or not
 */
bool CPackages::IsOriginalFile(C_File *file)
{
        if ( GetOriginalFile(file) )
                return true;
        return false;
}

/**
 * Get original file
 *
 * Finds a matching original file entry and returns it
 */
C_File *CPackages::GetOriginalFile(C_File *file)
{
        if ( !file )
                return NULL;

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

                // not of the same file type
                if ( of->GetFileType() != file->GetFileType() )
                        continue;

                // same file name, must be the same file
                if ( of->GetFilename().Compare(file->GetFilename()) )
                        return of;
        }

        return NULL;
}

/**
 * Backs up an original file
 *
 * Moves the file into the PluginManager/Original directory so packages dont replace them
 * Allows these files to be restored when the package is remove to prevent breaking the game
 */
bool CPackages::BackupOriginalFile(C_File *f, CyStringList *errors)
{
        CyString newDir = CyString("PluginManager/Original/") + f->GetDirectory(NULL);
        CDirIO oDir(m_sCurrentDir);
        if ( oDir.Exists(newDir + "/" + f->GetFilename()) )
                return true;

        // make sure the directory exissts
        if ( !oDir.Exists(newDir) )
        {
                if ( !oDir.Create(newDir) )
                {
                        this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newDir, errors);
                        return false;
                }

                this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, newDir, errors);
        }

        // now lets copy the file
        CFileIO CopyFile(f->GetFilePointer());
        if ( CopyFile.Copy(oDir.File(newDir + "/" + f->GetFilename())) )
        {
                this->AddLogEntry(SPKINSTALL_ORIGINAL_BACKUP, f->GetNameDirectory(NULL), errors);
                return true;
        }
        else
        {
                this->AddLogEntry(SPKINSTALL_ORIGINAL_BACKUP_FAIL, f->GetNameDirectory(NULL), errors);
                return false;
        }
}

void CPackages::ShuffleTextFiles(CyStringList *errors)
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                // only do files that are enabled
                if ( f->IsDisabled() )
                        continue;

                if ( f->GetFileType() != FILETYPE_TEXT )
                        continue;

                // check if the file is an auto text file
                if ( !f->IsAutoTextFile() )
                        continue;

                // we need to rename it
                int current = FindNextTextFile();
                if ( current < f->GetTextFileID() )
                {
                        CFileIO moveFile(f->GetFilePointer());
        
                        CyString newName = SPK::FormatTextName(current, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT)) + "." + moveFile.GetFileExtension();
                        if ( moveFile.Rename(m_sCurrentDir + "/t/" + newName) )
                        {
                                this->AddLogEntry(SPKINSTALL_AUTOTEXT, f->GetName() + "~" + newName, errors);
                                f->SetName(newName);
                        }
                        else
                                this->AddLogEntry(SPKINSTALL_AUTOTEXT_FAIL, f->GetName() + "~" + newName, errors);
                }
        }
}

/**
 * Shuffle Fake Patches
 *
 * Rename the fake patches so they are always in sequence to be loaded into the game
 *
 * IE, when one is removed, the rest need to be shuffled down so theres no gaps
 */
void CPackages::ShuffleFakePatches(CyStringList *errors)
{
        int check = FindNextFakePatch();

        // lets make sure our order is correct
        // find Lowest Fake Patch Installed
        CLinkList<C_File> doneList;
        int lowest = FindLowestFakePatchInstalled();
        if ( check < lowest ) lowest = check; // gap at the beginning, lets use that

        // lets define the order
        CyStringList fakePatchOrder;
        for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
                fakePatchOrder.PushBack(str->str, str->data);

        // add any other packages that need to be ordered
        // we need to work out the correct order
        CLinkList<CBaseFile> packages;
        for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
        {
                CBaseFile *p = pNode->Data();
                // search for any fake patches in package
                if ( !p->IsEnabled() ) continue;
                if ( !p->AnyFileType(FILETYPE_MOD) ) continue;

                // must have an order define
                if ( !p->AnyFakePatchOrder() ) continue;

                // check if theres any already ordered (manual)
                bool added = false;
                for ( SStringList *sCheck = fakePatchOrder.Head(); sCheck; sCheck = sCheck->next )
                {
                        if ( sCheck->str.Compare(p->GetName()) && sCheck->data.Compare(p->GetAuthor()) )
                        {
                                added = true;
                                break;
                        }
                }

                if ( added ) continue;

                bool anyFound = false;
                for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
                {
                        if ( !file->IsFakePatch() ) continue;
                        if ( !file->CheckFileExt("cat") ) continue;
                        if ( doneList.FindData(file) )  continue;
                        anyFound = true;
                        break;
                }

                // we have some fake patches that need to be shuffled
                if ( anyFound )
                        packages.push_back(p);
        }

        // lets adjust the order (only if theres more than 1
        if ( packages.size() > 1 )
        {
                CLinkList<CBaseFile> sortedPackages;

                // first add all the packages that dont need to be installed after
                for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
                {
                        CBaseFile *p = pNode->Data();
                        // we have a before and not after
                        if ( !p->GetFakePatchBeforeOrder().Empty() && p->GetFakePatchAfterOrder().Empty() )
                        {
                                // if we have to install before, check if any on the list
                                int earliestPos = -1;
                                bool notAdded = true;
                                for ( SStringList *str = p->GetFakePatchBeforeOrder().Head(); str; str = str->next )
                                {
                                        int pos = 0;
                                        for ( CListNode<CBaseFile> *sNode = sortedPackages.Front(); sNode; sNode = sNode->next() )
                                        {
                                                if ( str->str.Compare(sNode->Data()->GetName()) && str->data.Compare(sNode->Data()->GetAuthor()) )
                                                {
                                                        if ( earliestPos == -1 || pos < earliestPos )
                                                                earliestPos = pos;
                                                        break;
                                                }
                                                ++pos;
                                        }                                       
                                }

                                if ( earliestPos > -1 )
                                        sortedPackages.insert(earliestPos, p);
                                // otherwise just add it at the back
                                else
                                        sortedPackages.push_back(p);

                                // remove from the list
                                pNode->ChangeData(NULL);
                        }
                }

                // now do the packages that have both before and after
                packages.RemoveEmpty();
                for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
                {
                        CBaseFile *p = pNode->Data();
                        if ( !p->GetFakePatchBeforeOrder().Empty() && !p->GetFakePatchAfterOrder().Empty() )
                        {
                        }
                }

                // add them onto the list
                for ( CListNode<CBaseFile> *pNode = sortedPackages.Front(); pNode; pNode = pNode->next() )
                        fakePatchOrder.PushBack(pNode->Data()->GetName(), pNode->Data()->GetAuthor());
        }

        // now add to do list
        for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
                fakePatchOrder.PushBack(pNode->Data()->GetName(), pNode->Data()->GetAuthor());

        for ( SStringList *str = fakePatchOrder.Head(); str; str = str->next )
        {
                CBaseFile *package = FindPackage(str->str, str->data);
                if ( package )
                {
                        // might have more than 1 fake patch for the file, so we'll need the lowest
                        while ( true )
                        {
                                C_File *lowestFile = NULL;
                                for ( C_File *file = package->GetFirstFile(FILETYPE_MOD); file; file = package->GetNextFile(file) )
                                {
                                        if ( !file->IsFakePatch() ) continue;
                                        if ( !file->CheckFileExt("cat") ) continue; // only do the cat file, we can shuffle the dat file to match later
                                        if ( doneList.FindData(file) ) continue; // already done?

                                        if ( !lowestFile )
                                                lowestFile = file;
                                        else
                                        {
                                                if ( file->GetBaseName().ToInt() < lowestFile->GetBaseName().ToInt() )
                                                        lowestFile = file;
                                        }
                                }

                                if ( !lowestFile ) // no more files ?
                                        break;

                                // check its filename, it might already be in the correct place
                                if ( lowestFile->GetBaseName().ToInt() != lowest )
                                {
                                        // if the file already exists, we need to move it elsewhere
                                        CyString nextName = CyString::Number(lowest).PadNumber(2);
                                        if ( CFileIO(m_sCurrentDir + "/" + nextName + ".cat").Exists() )
                                        {
                                                // find the file in our internal file list
                                                C_File *moveFile = FindFile(FILETYPE_MOD, nextName + ".cat");
                                                if ( !moveFile ) // must not have it in our list ? lets move to the next
                                                {
                                                        lowest++;
                                                        continue;
                                                }

                                                // now we can move the the cat/dat file elsewhere, lets shuffle it to the highest free number
                                                ShufflePatchTo(moveFile, FindLastFakePatch(), errors);
                                        }

                                        // space should be free, now lets shuffle it
                                        ShufflePatchTo(lowestFile, lowest, errors);
                                }

                                doneList.push_back(lowestFile); // we've done this file now
                                lowest++; // move up the lowest ready for the next patch
                        }
                }
        }

        // now lets shuffle the rest
        // now find any packages with greater fake patchs and fill the gaps
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                // already done?
                if ( doneList.FindData(f) ) 
                        continue;

                // only do files that are enabled
                if ( f->IsDisabled() )
                        continue;

                // check if the file is a fake patch
                if ( !f->IsFakePatch() )
                        continue;

                // we only want cat and dat files, all fake patchs should be, but incase theres an error in the package somewhere we can check
                if ( !f->CheckFileExt("cat") )
                        continue;

                // now lets check if its greater than our gap
                int check = FindNextFakePatch();
                int patchNum = f->GetFilename().GetToken(".", 1, 1).ToInt();
                if ( patchNum <= check )
                        continue;

                ShufflePatchTo(f, check, errors);
        }
}

void CPackages::ShufflePatchTo(C_File *file, int to, CyStringList *errors)
{
        // it is, we need to shift this to fill the gap
        CyString newName = CyString::Number(to).PadNumber(2) + "." + file->GetFileExt();

        // now rename the file
        CFileIO moveFile(file->GetFilePointer());
        if ( moveFile.Rename(m_sCurrentDir + "/" + newName) )
        {
                // display moveing
                this->AddLogEntry(SPKINSTALL_FAKEPATCH, file->GetName() + "~" + newName, errors);

                // now find the matching pairing if it exists
                for ( CListNode<C_File> *node2 = m_lFiles.Front(); node2; node2 = node2->next() )
                {
                        C_File *f2 = node2->Data();

                        if ( f2->IsDisabled() || !f2->IsFakePatch() || f2->CheckFileExt(file->GetFileExt()) )
                                continue;

                        // needs to be the same file
                        if ( f2->GetBaseName() != file->GetBaseName() )
                                continue;

                        CyString newName2 = CyString::Number(to).PadNumber(2) + "." + f2->GetFileExt();
                        CFileIO moveFile(f2->GetFilePointer());
                        if ( moveFile.Rename(m_sCurrentDir + "/" + newName2) )
                        {
                                this->AddLogEntry(SPKINSTALL_FAKEPATCH, f2->GetName() + "~" + newName2, errors);
                                f2->SetName(newName2);
                        }
                        else
                                this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->GetName() + "~" + newName2, errors);
                }

                // finally make sure the internal name matches the new one
                file->SetName(newName);
        }
        else
                this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, file->GetName() + "~" + newName, errors);
}

int CPackages::FindLowestFakePatchInstalled()
{
        int lowest = 99;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( !p->IsEnabled() ) continue;
                if ( !p->AnyFileType(FILETYPE_MOD) ) continue;

                // now get all fake patches
                for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
                {
                        if ( !file->IsFakePatch() ) continue;
                        if ( file->CheckFileExt("dat") ) continue;
                        int check = file->GetBaseName().ToInt();
                        if ( check < lowest )
                                lowest = check;
                }
        }

        return lowest;
}

int CPackages::FindLastFakePatch(int start, CyString dir)
{
        CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);

        int check = start;
        while ( check > 0 )
        {
                CyString checkStr = CyString::Number(check).PadNumber(2);

                // check if a cat file exists
                if ( !Dir.Exists(checkStr + ".cat") )
                {
                        // it doen't, check if theres a dat file (incase of package error)
                        if ( !Dir.Exists(checkStr + ".dat") )
                                break;
                }
                --check;
        }

        return check;
}

/**
 * Find next fake patch
 *
 * Searching for the next gap in patches, starting with 01.cat to 99.cat
 */
int CPackages::FindNextFakePatch(int start, CyString dir)
{
        CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);

        int check = start;
        while ( check < 99 )
        {
                ++check;
                CyString checkStr = CyString::Number(check).PadNumber(2);

                // check if a cat file exists
                if ( !Dir.Exists(checkStr + ".cat") )
                {
                        // it doen't, check if theres a dat file (incase of package error)
                        if ( !Dir.Exists(checkStr + ".dat") )
                                break;
                }
        }

        return check;
}

/**
 * Find next text file
 *
 * Searching for the next gap in automatic text files, start with 0003-LXXX or XX0003
 */
int CPackages::FindNextTextFile(int start, CyString dir)
{
        CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
        Dir.cd("t");

        int check = start;
        if ( check < 2 ) check = 2;
        while ( check < 9999 )
        {
                ++check;
                CyString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));

                // check if a packed file exists
                if ( !Dir.Exists(newFilename + ".pck") )
                {
                        // it doen't, check if theres an unpacked file
                        if ( !Dir.Exists(newFilename + ".xml") )
                                break;
                }
        }

        return check;
}

int CPackages::FindLastTextFile(int start, CyString dir)
{
        CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
        Dir.cd("t");

        int check = start;
        while ( check < 9999 )
        {
                ++check;
                CyString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));

                // check if a packed file exists
                if ( !Dir.Exists(newFilename + ".pck") )
                {
                        // it doen't, check if theres an unpacked file
                        if ( !Dir.Exists(newFilename + ".xml") )
                                break;
                }
        }

        return check;
}

/**
 * Read game language
 *
 * Reads the lang.dat file from the game directory for the language id
 */
void CPackages::ReadGameLanguage(bool force)
{
        // check if lauguage is already set
        if ( !force && m_iLanguage )
                return;

        CDirIO Dir(m_sCurrentDir);

        // check for lang.dat file
        if ( Dir.Exists("lang.dat") )
        {
                CFileIO File(Dir.File("lang.dat"));

                size_t size;
                char *data = File.ReadToData(&size);

                if ( data )
                {
                        CyString str(data);
                        m_iLanguage = str.GetToken("\n", 1, 1).GetToken(" ", 1, 1).ToInt();
                }
        }
}

/**
 * Create Language Text File
 *
 * Creates text files for all packages into the correct language
 */
void CPackages::CreateLanguageTextFiles(CyStringList *errors)
{
        // no need to create them if theres no language to use
        if ( !m_iLanguage )
                return;

        // find all text files
        CyStringList ids;

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                // only do text fiels
                if ( f->GetFileType() != FILETYPE_TEXT )
                        continue;

                CyString id, lang;
                if ( m_iGameFlags & EXEFLAG_TCTEXT )
                {
                        id = f->GetBaseName().GetToken("-", 1, 1);
                        lang = f->GetBaseName().GetToken("-", 2, 2);
                        if ( lang.Empty() )
                                lang = "NULL";
                        else
                                lang = lang.Erase(0, 1);  // remove the "L"
                }
                else
                {
                        lang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
                        id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
                }

                SStringList *strList = ids.FindString(id);

                // not added, add a new one
                if ( !strList )
                        strList = ids.PushBack(id, lang);
                else
                {
                        if ( strList->data.Empty() )
                                strList->data = lang;
                        else
                        {
                                strList->data += ":";
                                strList->data += lang;
                        }
                }
        }

        // we should now have a list of all text files, we need to remove those that have a matching language
        for ( SStringList *sNode = ids.Head(); sNode; sNode = sNode->next )
        {
                int size = 0;
                CyString *data = sNode->data.SplitToken(':', &size);

                // huh ? we shouldn't have this
                if ( !size )
                        continue;

                // lets search for a matching id
                int useId = 0;
                bool found = false;
                for ( int i = 0; i < size; i++ )
                {
                        if ( data[i] == "NULL" )
                                useId = -1;
                        else
                        {
                                int num = data[i].ToInt();
                                if ( num == m_iLanguage )
                                {
                                        found = true;
                                        useId = m_iLanguage;
                                        break;
                                }
                                // prioities the text file to use, no language first, then 44, then 49, otherwise, we pick the first available
                                else if ( num == 44 && useId != -1)
                                        useId = 44;
                                // if we have a german language, and its not yet found english or null
                                else if ( num == 49 && useId != 44 && useId != -1 )
                                        useId = 49;
                                // if we have not found a valid language yet, we will use this one
                                else if ( !useId )
                                        useId = num;
                        }
                }

                if ( found )
                        continue;

                if ( !useId )
                        useId = data[0].ToInt();
                if ( !useId )
                        useId = -1;

                CLEANSPLIT(data, size);

                RenameTextFile(sNode->str, useId, errors);
        }
}

/**
 * Rename a text file
 *
 * Creates a new text file and copies an existing one
 */
bool CPackages::RenameTextFile(CyString textid, int languageid, CyStringList *errors)
{
        // lets check if the file already exists
        CyString newFilename = SPK::FormatTextName(textid.ToInt(), m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
        C_File *addFile = FindFile(FILETYPE_TEXT, newFilename + ".xml");
        if ( !addFile )
                addFile = FindFile(FILETYPE_TEXT, newFilename + ".pck");

        // we have found the file, lets just add it to our scripts
        if ( addFile )
        {
                AddTextFileToScripts(addFile, textid);
                return true;
        }

        // first we need to find our text file
        CyString filename;
        if ( languageid != -1 )
                filename = SPK::FormatTextName(textid.ToInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));
        else
                filename = textid;

        C_File *textFile = FindFile(FILETYPE_TEXT, filename + ".xml");
        if ( !textFile )
        {
                textFile = FindFile(FILETYPE_TEXT, filename + ".pck");
                if ( !textFile )
                        return false;
        }

        // now lets create out new text file
        CFileIO readFile(textFile->GetFilePointer());
        std::vector<CyString> *lines = readFile.ReadLines();

        // find the language id in the lines
        std::vector<CyString> frontLines;
        for(std::vector<CyString>::iterator it = lines->begin(); it != lines->end(); ++it)
        {
                CyString line = *it;
                int pos = line.FindPos("<language id");
                if ( pos != -1)
                {
                        CyString newLine = "<language id=\"";
                        newLine += CyString::Number(m_iLanguage);
                        newLine += "\">";
                        newLine += line.GetToken(">", 2);

                        frontLines.insert(frontLines.begin(), newLine);

                        lines->erase(lines->begin(), ++it);
                        break;
                }
                frontLines.insert(frontLines.begin(), line);
        }

        for(std::vector<CyString>::iterator it = frontLines.begin(); it != frontLines.end(); ++it)
                lines->insert(lines->begin(), *it);

        addFile = new C_File(newFilename + ".xml");
        addFile->SetFileType(FILETYPE_TEXT);

        CFileIO writeFile(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
        if ( writeFile.WriteFile(lines) )
        {
                this->AddLogEntry(SPKINSTALL_WRITEFILE, addFile->GetNameDirectory(NULL), errors);

                addFile->SetFilename(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
                // now we have the file wrriten, we need to add it to all scripts that need it
                // first we add it to the global list
                m_lFiles.push_back(addFile);

                // now add it to the scripts
                AddTextFileToScripts(addFile, textid);
        }
        else
        {
                this->AddLogEntry(SPKINSTALL_WRITEFILE_FAIL, addFile->GetNameDirectory(NULL), errors);
                if ( lines )
                        delete lines;
                return false;
        }

        if ( lines )
                delete lines;


        return true;
}

/**
 * Add file to scripts
 *
 * Adds a file to all scripts that need it
 */
void CPackages::AddTextFileToScripts(C_File *file, CyString textid)
{
        // now we need to find all scripts that have a matching file and add this to the list
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *package = node->Data();

                // first make sure it doesn't already exist
                if ( package->GetFileList()->FindData(file) )
                        continue;

                bool found = false;
                for ( CListNode<C_File> *fNode = package->GetFileList()->Front(); fNode; fNode = fNode->next() )
                {
                        C_File *tFile = fNode->Data();
                        if ( tFile->GetFileType() != FILETYPE_TEXT )
                                continue;

                        CyString id = tFile->GetBaseName().GetToken("-", 1, 1);
                        if ( id == textid )
                        {
                                found = true;
                                break;
                        }
                }

                if ( found )
                        package->GetFileList()->push_back(file);
        }
}

bool CPackages::RemoveFile(C_File *file, CyStringList *errors)
{
        if ( !file )
                return true;

        CyString remFileStr = file->GetFilePointer();
        remFileStr.FindReplace(m_sCurrentDir, "");

        if ( file->GetFilePointer().IsIn("::") ) {
                CFileIO CatFile(file->GetFilePointer().GetToken("::", 1, 1));
                if ( CatFile.Exists() ) {
                        CCatFile cat;
                        if ( cat.Open(CatFile.GetFullFilename(), this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE ) {
                                CyString fileName = file->GetFilePointer().GetToken("::", 2, 2);
                                if ( cat.FindData(fileName) ) {
                                        if ( cat.RemoveFile(fileName) ) {
                                                this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
                                        }
                                        else {
                                                this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
                                                return false;
                                        }
                                }
                        }
                }
        }
        else {
                CFileIO f(file->GetFilePointer());
                if ( f.Exists() )
                {
                        if ( f.Remove() )
                                this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
                        else if ( errors )
                        {
                                this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
                                return false;
                        }
                        else
                                return false;
                }
        }

        return true;
}

int CPackages::RemoveAllPackages(CyStringList *errors, CProgressInfo *progress)
{
        int files = 0;

        // remove all files
        int max = m_lFiles.size() + m_lOriginalFiles.size() + m_lUninstallFiles.size();
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                // update the progress
                if ( progress )
                        progress->UpdateProgress(files, max);
                ++files;

                RemoveFile(node->Data());
                delete node->Data();
        }
        m_lFiles.clear();

        // restore any original files that are backed up
        for ( CListNode<C_File> *oNode = m_lOriginalFiles.Front(); oNode; oNode = oNode->next() )
        {
                // update the progress
                if ( progress )
                        progress->UpdateProgress(files, max);
                ++files;

                C_File *f = oNode->Data();
                CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(NULL));
                if ( of.Exists() )
                        of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(NULL));

                //RemoveFile(oNode->Data());
                delete oNode->Data();
        }
        m_lOriginalFiles.clear();

        // remove any uninstall files that remain
        for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
        {
                // update the progress
                if ( progress )
                        progress->UpdateProgress(files, max);
                ++files;

                RemoveFile(uNode->Data());
                delete uNode->Data();
        }
        m_lUninstallFiles.clear();

        // delete all packages
        for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
        {
                // clear file list as we have removed them already
                pNode->Data()->GetFileList()->clear();
                // delete the memory for the package
                delete pNode->Data();
        }
        m_lPackages.clear();

        return files;
}

/**
 * Get Install Before Text
 *
 * Returns the install before text for the package in the correct language
 */
CyString CPackages::GetInstallBeforeText(CBaseFile *package)
{
        if ( !package->IsThereInstallText() )
                return NullString;

        return package->GetInstallBeforeText(m_iLanguage);
}
CyString CPackages::GetInstallAfterText(CBaseFile *package)
{
        if ( !package->IsThereInstallText() )
                return NullString;

        return package->GetInstallAfterText(m_iLanguage);
}
CyString CPackages::GetUninstallBeforeText(CBaseFile *package)
{
        if ( !package->IsThereUninstallText() )
                return NullString;

        return package->GetUninstallBeforeText(m_iLanguage);
}
CyString CPackages::GetUninstallAfterText(CBaseFile *package)
{
        if ( !package->IsThereUninstallText() )
                return NullString;

        return package->GetUninstallAfterText(m_iLanguage);
}

int CPackages::GetChildPackages(CBaseFile *package, CLinkList<CBaseFile> *children, bool recursive)
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( p->GetParent() == package )
                {
                        children->push_back(p);

                        if ( recursive )
                                this->GetChildPackages(p, children, recursive);
                }
        }

        return children->size();
}

void CPackages::AssignPackageNumbers()
{
        int num = 0;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
                node->Data()->SetNum(num++);
}

CyString CPackages::FindDataDir(CyString dir, CyString file)
{
        CDirIO Dir(dir);
        // data files could be in 4 places depending on what program is used, check for all of these
        if ( Dir.Exists(file) )
                return Dir.File(file);
        else if ( Dir.Exists(CyString("Data/") + file) )
                return Dir.File(CyString("Data/") + file);
        else if ( Dir.Exists(CyString("../") + file) )
                return Dir.File(CyString("../") + file);
        else if ( Dir.Exists(CyString("../Data/") + file) )
                return Dir.File(CyString("../Data/") + file);

        return NullString;
}

void CPackages::Startup(CyString dir, CyString tempDir, CyString myDoc, CyString mod)
{
        this->SetTempDirectory(tempDir);
        this->SetMyDocuments(myDoc);

        m_sSetMod = mod;

        // need to read the game exe versions
        m_gameExe.Reset();

        CyString exeFile = this->FindDataDir(dir, "exe");

        // if file exists, read it, otherwise, just add
        if ( !exeFile.Empty() && CFileIO(exeFile).Exists() )
                m_gameExe.ReadFile(exeFile);
        else
        {
                m_gameExe.ParseExe("x2.exe:5:NOSAVESUBDIR:HKCU/Software/EgoSoftware/X2/ModName:X2 The Threat:!GAMEDIR!:2:1604608!2150400:1.4 Artifical Life:1974272:1.5 Uplink");
                m_gameExe.ParseExe("x3.exe:5:0:HKCU/Software/Egosoft/X3/ModName:X3 Reunion:Egosoft/X3:2:2347008:2.0 Bala Gi:2367488!2375680:2.5 Uplink");
                m_gameExe.ParseExe("x3tc.exe:5:NO_XOR|TC_TEXT|MYDOCLOG:HKCU/Software/Egosoft/X3TC/ModName:X3 Terran Conflict:Egosoft/X3TC:3:1933464!1933520:2.0 Aldrin Expansion:-1:2.5 A New Home (Superbox):-1:3.0 Balance of Power");
                m_gameExe.ParseExe("x3ap.exe:2:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3AP/ModName:X3 Albion Prelude:Egosoft/X3AP:addon!x3tc.exe:0");
        }
}

int CPackages::GetGameLanguage(CyString dir)
{
        if ( dir.Empty() )
                dir = m_sCurrentDir;

        CDirIO Dir(dir);

        // check for lang.dat file
        if ( Dir.Exists("lang.dat") )
        {
                CFileIO File(Dir.File("lang.dat"));

                size_t size;
                char *data = File.ReadToData(&size);

                if ( data )
                {
                        CyString str(data);
                        return str.GetToken("\n", 1, 1).GetToken(" ", 1, 1).ToInt();
                }
        }

        return 0;
}
CyString CPackages::GetGameRunExe(CyString dir)
{
        return m_gameExe.GetGameRunExe((dir.Empty()) ? m_sCurrentDir : dir);
}

CyString CPackages::GetGameNameFromType(int game)
{
        return m_gameExe.GetGameNameFromType(game - 1);
}
CyString CPackages::GetGameName(CyString dir)
{
        return m_gameExe.GetGameName((dir.Empty()) ? m_sCurrentDir : dir);
}
CyString CPackages::GetProperDir(CyString dir)
{
        return m_gameExe.GetProperDir((dir.Empty()) ? m_sCurrentDir : dir);
}
CyString CPackages::GetAddonDir(CyString dir)
{
        return m_gameExe.GetAddonDir((dir.Empty()) ? m_sCurrentDir : dir);
}
int CPackages::GetGameAddons(CyStringList &exes, CyString dir)
{
        return m_gameExe.GetGameAddons((dir.Empty()) ? m_sCurrentDir : dir, exes);
}

CyString CPackages::GetGameTypesString(CBaseFile *package, bool includeVersion)
{
        if ( !package->AnyGameCompatability() )
                return NullString;

        CyString sGames;
        for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
                if ( !sGames.Empty() )
                        sGames += ", ";
                sGames += m_gameExe.GetGameNameFromType(gNode->Data()->iGame - 1);
                if ( includeVersion ) {
                        sGames += " (";
                        sGames += m_gameExe.GetGameVersionFromType(gNode->Data()->iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
                        sGames += ")";
                }
        }
        return sGames;
}

CyString CPackages::GetGameVersionString(CBaseFile *package)
{
        for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
                if ( gNode->Data()->iGame == m_iGame ) {
                        return m_gameExe.GetGameVersionFromType(m_iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
                }
        }

        return NullString;
}

CyString CPackages::GetGameVersionFromType(int game, int version, CyString sVersion)
{
        return m_gameExe.GetGameVersionFromType(game - 1, version - 1, sVersion);
}

int CPackages::CountBuiltInPackages(bool onlyEnabled)
{
        int count = 0;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if ( !node->Data()->CheckGameCompatability(m_iGame) )
                        continue;
                if ( onlyEnabled && !node->Data()->IsEnabled() )
                        continue;
                if ( node->Data()->GetAuthor().Compare("PluginManager") )
                        ++count;
        }

        return count;
}

int CPackages::CountPackages(int type, bool onlyEnabled)
{
        int count = 0;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();

                if ( (type != TYPE_BASE) && (p->GetType() != type) )
                        continue;

                if ( (onlyEnabled) && (!p->IsEnabled()) )
                        continue;

                ++count;
        }

        return count;
}

int CPackages::ExtractGameFile(CyString aFilename, CyString aTo, CyString dir, CyString addon)
{
        // first check the enabled mod
        if ( dir.Empty() )
                dir = m_sCurrentDir;

        CyString addonDir = addon;
        if ( addonDir.Empty() ) addonDir = this->GetAddonDir(dir);

        if ( m_pEnabledMod && m_pEnabledMod->AnyFileType(FILETYPE_MOD) )
        {
                for ( C_File *file = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); file; file = m_pEnabledMod->GetNextFile(file) )
                {
                        if ( !file->CheckFileExt("cat") )
                                continue;

                        CCatFile catFile;
                        if ( catFile.Open(file->GetFilePointer(), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
                        {
                                if ( catFile.ExtractFile(aFilename, aTo) )
                                        return 1;
                                if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
                                {
                                        if ( catFile.ExtractFile(aFilename) )
                                                return -1;
                                }

                        }
                }
        }
        else if ( !m_sSetMod.Empty() )
        {
                if ( CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Exists() )
                {
                        CCatFile catFile;
                        if ( catFile.Open(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
                        {
                                if ( catFile.ExtractFile(aFilename, aTo) )
                                        return 1;
                                if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
                                {
                                        if ( catFile.ExtractFile(aFilename) )
                                                return -1;
                                }
                        }
                }
        }

        // find the highest cat number
        int catNumber = 1;
        while ( CFileIO(dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat").Exists() && catNumber < 99 )
                ++catNumber;

        // get the last one, not the next free one
        --catNumber;

        // work backwards until we find the file
        for ( ; catNumber; catNumber-- )
        {
                CCatFile catFile;
                if ( catFile.Open(dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
                {
                        // check for the file
                        if ( catFile.ExtractFile(aFilename, aTo) )
                                return 1;
                        if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
                        {
                                if ( catFile.ExtractFile(aFilename) )
                                        return -1;
                        }
                }
        }

        return 0;
}

void CPackages::CreateWareFiles()
{
        // do each ware
        int wareTextID = WARETEXTSTART;
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                // its empty, no need to create ware files
                if ( !m_lGameWares[i].size() )
                        continue;

                // lets extract the ware file
                char wareType = CPackages::ConvertWareTypeBack(i);
                CyString wareFile = "TWare";
                wareFile += (char)UPPER(wareType);

                CyString openFile;

                int e;
                if ( i == WARES_TECH && CFileIO(m_sTempDir + "/TWareT.txt").Exists() )
                        openFile = m_sTempDir + "/TWareT.txt";
                else {
                        e = ExtractGameFile(CyString("types/") + wareFile + ".pck", m_sTempDir + "/" + wareFile + ".txt");
                        if ( e == 1 )
                                openFile = m_sTempDir + "/" + wareFile + ".txt";
                        else if ( e == -2 )
                                openFile = wareFile + ".txt";
                }

                if ( !openFile.Empty() )
                {
                        // read the file into memory
                        CyStringList wareLines;
                        int oldSize = -1;
                        int version = -1;

                        // read first number
                        CFileIO readFile(m_sTempDir + "/" + wareFile + ".txt");
                        CyStringList *lines = readFile.ReadLinesStr();
                        if ( lines )
                        {
                                for ( SStringList *str = lines->Head(); str; str = str->next )
                                {
                                        CyString line(str->str);
                                        line.RemoveFirstSpace();
                                        line.RemoveChar(9);
                                        line.RemoveChar('\r');
                                        if ( line.Empty() )
                                                continue;
                                        if ( line[0] == '/' )
                                                continue;

                                        if ( oldSize == -1 )
                                        {
                                                version = line.GetToken(";", 1, 1).ToInt();
                                                oldSize = line.GetToken(";", 2, 2).ToInt();
                                        }
                                        else
                                        {
                                                line.RemoveEndSpace();
                                                if ( line.Right(1) != ';' )
                                                        line += ";";
                                                wareLines.PushBack(line);
                                                if ( wareLines.Count() >= oldSize )
                                                        break;
                                        }
                                }

                                delete lines;

                                // apply the buffer
                                if ( m_iWareBuffer[i] <= 0 )
                                        m_iWareBuffer[i] = wareLines.Count() + 10;
                                // last resort, readjust the buffer
                                else if ( wareLines.Count() > m_iWareBuffer[i] )
                                        m_iWareBuffer[i] = wareLines.Count();

                                // add the buffers
                                while ( wareLines.Count() < m_iWareBuffer[i] )
                                        wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");

                                // add the ware lines
                                bool create = false;
                                for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
                                {
                                        SGameWare *w = node->Data();

                                        if ( w->iType == WARETYPE_NONE )
                                                continue;
                                        else if ( w->iType == WARETYPE_DISABLED )
                                        {
                                                create = true;
                                                wareLines.PushBack(CyString("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;") + w->sWareName + "_DISABLED;");
                                        }
                                        else if ( w->iType == WARETYPE_DELETED || !w->pWare )
                                        {
                                                create = true;
                                                wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_DELETED;");
                                        }
                                        else if ( w->iType == WARETYPE_ADDED )
                                        {
                                                create = true;
                                                w->pWare->iDescID = wareTextID;
                                                w->iText = wareTextID;
                                                wareTextID += 10;
                                                w->iPos = wareLines.Count();
                                                wareLines.PushBack(CyString("28;0;0;0;0;") + (long)wareLines.Count() + ";" + (long)(w->iText + 3) + ";" + (long)w->pWare->iVolumn + ";" + (long)w->pWare->iPrice + ";1;1;" + (long)w->pWare->iSize + ";" + (long)w->pWare->iPrice + ";" + (long)w->pWare->iNotority + ";0;0;" + w->sWareName.ToUpper() + ";");
                                        }
                                }

                                if ( create )
                                {
                                        wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
                                        CyString strV;
                                        strV.FromFloat(GetLibraryVersion(), 2);
                                        wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
                                        if ( readFile.WriteFile(&wareLines) )
                                                this->PackFile(&readFile, CyString("types\\") + wareFile + ".pck");
                                }
                        }
                        readFile.Remove();
                }
        }
}

void CPackages::CreateEMPFile(CyString progDir)
{
        // do emp wares
        int maxsize = 0;
        CyString empWares;

        if ( m_iGame == GAME_X3TC )
        {
                maxsize = EMP_X3TC;
                empWares = GetX3TCEmp();
        }
        else if ( m_iGame == GAME_X3AP )
        {
                maxsize = EMP_X3AP;
                empWares = GetX3TCEmp();
        }
        else if ( m_iGame == GAME_X3 )
        {
                maxsize = EMP_X3;
                empWares = GetX3Emp();
        }

        if ( maxsize )
        {
                int e = ExtractGameFile("types/TWareT.pck", m_sTempDir + "/TWareT.txt");
                if ( e )
                {
                        // read the file into memory
                        CyStringList wareLines;
                        int oldSize = -1;
                        int version = -1;

                        // read first number
                        CFileIO readFile((e == -1) ? "TWareT.txt" : m_sTempDir + "/TWareT.txt");
                        std::vector<CyString> *lines = readFile.ReadLines();
                        if ( lines )
                        {
                                for ( int i = 0; i < (int)lines->size(); i++ )
                                {
                                        CyString line(lines->at(i));
                                        line.RemoveFirstSpace();
                                        line.RemoveChar('\r');
                                        line.RemoveChar(9);
                                        if ( line[0] == '/' )
                                                continue;

                                        if ( oldSize == -1 )
                                        {
                                                version = line.GetToken(";", 1, 1).ToInt();
                                                oldSize = line.GetToken(";", 2, 2).ToInt();
                                        }
                                        else
                                        {
                                                line.RemoveEndSpace();
                                                if ( line.Right(1) != ";" )
                                                        line += ";";
                                                wareLines.PushBack(line);
                                                if ( wareLines.Count() >= oldSize )
                                                        break;
                                        }
                                }

                                delete lines;
                        }

                        // now we too add/remove entries to match
                        // need filler entries
                        while ( wareLines.Count() < maxsize )
                                wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");

                        int empEntries = 0;
                        CyString *empStr = empWares.SplitToken("\n", &empEntries);

                        if ( empEntries && empStr )
                        {
                                // remove any empty end entries
                                while ( empStr[empEntries - 1].Empty() )
                                        --empEntries;

                                CyStringList addAfter;
                                if ( wareLines.Count() > maxsize )
                                {
                                        // force emp, remove entries to allow them to be added
                                        if ( m_bForceEMP )
                                        {
                                                // more after emp
                                                if ( wareLines.Count() > (maxsize + empEntries) )
                                                {
                                                        SStringList *node = wareLines.GetAt(maxsize + empEntries);
                                                        while ( node )
                                                        {
                                                                addAfter.PushBack(node->str);
                                                                node = node->next;
                                                        }
                                                }

                                                // no remove them all
                                                wareLines.DeleteFrom(maxsize);
                                        }
                                        else if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3AP ) // check if old emp is included, and convert it
                                        {
                                                if ( wareLines.Count() > 128 )
                                                {
                                                        CyString test = wareLines.GetAt(128)->str;
                                                        if ( test.GetToken(";", -2).Compare("SS_WARE_SW_CUSTOM16_1;") )
                                                        {
                                                                // if theres any at the end, remove the last emp entry
                                                                if ( wareLines.Count() > (maxsize + empEntries - 1) )
                                                                {
                                                                        SStringList *node = wareLines.GetAt(maxsize + empEntries - 2);
                                                                        if ( node )
                                                                                node->remove = true;
                                                                }
                                                                wareLines.Insert(128, "0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;128;");
                                                                wareLines.RemoveMarked();
                                                        }
                                                }
                                        }
                                }

                                // too many entries, need to remove the first set of EMP wares
                                int i = 0;
                                if ( wareLines.Count() > maxsize )
                                        i = wareLines.Count() - maxsize;

                                for ( ; i < empEntries; i++ )
                                {
                                        CyString str = empStr[i];
                                        str.RemoveEndSpace();
                                        str.RemoveChar(9);
                                        str.RemoveChar('\r');
                                        if ( str.Empty() )
                                                continue;
                                        if ( str.Right(1) != ";" )
                                                str += ";";
                                        wareLines.PushBack(str);
                                }

                                for ( SStringList *afterNode = addAfter.Head(); afterNode; afterNode = afterNode->next )
                                        wareLines.PushBack(afterNode->str);

                                // finally we write the whole file
                                wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
                                CyString strV;
                                strV.FromFloat(GetLibraryVersion(), 2);
                                wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
                                if ( readFile.WriteFile(&wareLines) )
                                        this->PackFile(&readFile, "types\\TWareT.pck");
                        }
                        CLEANSPLIT(empStr, empEntries);
                }
        }
}

CyString parseXmlText(const CyString &str)
{
        CyString newStr(str);
        CyStringList changes;

        // find all XML commands, &<command>;
        std::string sStr = str.ToString();
        std::string::size_type pos = sStr.find_first_of("&", 0);
        while ( pos != std::string::npos ) {
                // find the next space and next ;.  If ; comes first, assume its acommand
                std::string::size_type spacePos = sStr.find_first_of(" ", pos);
                std::string::size_type colonPos = sStr.find_first_of(";", pos);
                if ( colonPos != std::string::npos && colonPos < spacePos ) {
                        // replace with <::command::> so they the & doesn't get replaced
                        std::string repStr = sStr.substr(pos, (colonPos + 1) - pos);
                        std::string repWithStr = "<::" + sStr.substr(pos + 1, colonPos - pos - 1) + "::>";
                        newStr.FindReplace(repStr, repWithStr);
                        changes.PushBack(repStr, repWithStr);
                }

                // find the next command
                pos = sStr.find_first_of("&", pos + 1);
        }

        // replace the & now
        newStr = newStr.FindReplace("&", "&amp;");

        // restore the commands
        for ( SStringList *strNode = changes.Head(); strNode; strNode = strNode->next ) {
                newStr.FindReplace(strNode->data, strNode->str);
        }

        return newStr;
}

CyString CPackages::ConvertTextString(CyString text)
{
        //process any &
        text = parseXmlText(text);

        // change special cases
        text = text.FindReplace("(", "\\(");
        text = text.FindReplace(")", "\\)");
        text = text.FindReplace("[", "{");
        text = text.FindReplace("]", "}");
        text = text.FindReplace(">", "&gt;");
        text = text.FindReplace("<", "&lt;");
        return text;
}

void CPackages::CreatePluginManagerText()
{
        int gameNumber = 0;
        if ( m_iGame == GAME_X3 )
                gameNumber = 30;
        else if ( m_iGame == GAME_X3TC )
                gameNumber = 35;
        else if ( m_iGame == GAME_X3AP )
                gameNumber = 38;

        int lang = m_iLanguage;
        if ( !lang || lang < 0 )
                lang = 44;

        CDirIO Dir(m_sCurrentDir);
        if ( !Dir.Exists("t") )
                Dir.Create("t");

        m_iLastUpdated = (int)time(NULL);

        CyString filename = SPK::FormatTextName(PMTEXTFILE, lang, (m_iGameFlags & EXEFLAG_TCTEXT));
        CFileIO textFile(m_sCurrentDir + "/t/" + filename + ".xml");

        std::vector<CyString> writeData;

        CLinkList<SGameWare> lWares;
        CLinkList<SGameShip> lShips;
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
                {
                        SGameWare *w = node->Data();
                        if ( w->iType == WARETYPE_NONE )
                                continue;
                        lWares.push_back(w);
                }
        }
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                if ( node->Data()->iType == WARETYPE_NONE )
                        continue;
                lShips.push_back(node->Data());
        }

        writeData.push_back(CyString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"));
        writeData.push_back(CyString("<language id=\"") + CyString::Number(lang) + "\">");

        if ( !gameNumber )
                writeData.push_back(CyString("  <page id=\"") + CyString::Number(PMTEXTFILE) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
        else
                writeData.push_back(CyString("  <page id=\"") + (long)gameNumber + CyString::Number(PMTEXTFILE).PadNumber(4) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");

        // write the heading
        int start = 10000;
        writeData.push_back(CyString("          <t id=\"1\">") + CyString::Number(m_iLastUpdated) + "</t>");
        writeData.push_back(CyString("          <t id=\"2\">") + CyString::Number(this->CountPackages(TYPE_SPK, true)) + "</t>");
        writeData.push_back(CyString("          <t id=\"3\">") + CyString::Number(start) + "</t>");
        writeData.push_back(CyString("          <t id=\"4\">") + CyString::Number(lWares.size()) + "</t>");
        writeData.push_back(CyString("          <t id=\"6\">") + CyString::Number(lShips.size()) + "</t>");

        // write some generic texts
        writeData.push_back(CyString("          <t id=\"110\">") + (long)m_iLanguage + "</t>");
        writeData.push_back(CyString("          <t id=\"109\">Plugin Manager: \\033GPoll Gui Data\\033X</t>"));
        writeData.push_back(CyString("          <t id=\"107\">Plugin Manager: \\033GExport Game Data\\033X </t>"));
        writeData.push_back(CyString("          <t id=\"100\">\\n</t>"));
        writeData.push_back(CyString("          <t id=\"101\">\\033B</t>"));
        writeData.push_back(CyString("          <t id=\"102\">\\033G</t>"));
        writeData.push_back(CyString("          <t id=\"103\">\\033B</t>"));
        writeData.push_back(CyString("          <t id=\"104\">\\033X</t>"));
        writeData.push_back(CyString("          <t id=\"105\">\\033Y</t>"));
        writeData.push_back(CyString("          <t id=\"106\">\\033C</t>"));
        writeData.push_back(CyString("          <t id=\"108\">\\033</t>"));
        // now write each package
        int settingStart = 100000;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( !p->IsEnabled() )
                        continue;

                if ( p->GetType() != TYPE_SPK )
                        continue;

                CSpkFile *spk = (CSpkFile *)p;

                // count text files
                CyString textEntries;
                int textCount = 0;
                C_File *f = p->GetFirstFile(FILETYPE_TEXT);
                while ( f )
                {
                        CyString sLang;
                        CyString id;
                        if ( m_iGameFlags & EXEFLAG_TCTEXT )
                        {
                                id = f->GetBaseName().GetToken("-", 1, 1);
                                sLang = f->GetBaseName().GetToken("-", 2, 2);
                                if ( sLang.Empty() )
                                        sLang = "NULL";
                                else
                                        sLang = sLang.Erase(0, 1);  // remove the "L"
                        }
                        else
                        {
                                sLang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
                                id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
                        }

                        if ( sLang != "NULL" )
                        {
                                if ( sLang.ToInt() == lang )
                                {
                                        ++textCount;
                                        if ( !textEntries.Empty() )
                                                textEntries += " ";
                                        textEntries += id;
                                }
                        }
                        f = p->GetNextFile(f);
                }

                CyString sTextCount((long)textCount);
                writeData.push_back(CyString("          <t id=\"") + (long)start + "\">" + this->ConvertTextString(p->GetName()) + "</t>");
                writeData.push_back(CyString("          <t id=\"") + (long)(start + 1) + "\">" + this->ConvertTextString(p->GetAuthor()) + "</t>");
                writeData.push_back(CyString("          <t id=\"") + (long)(start + 2) + "\">" + this->ConvertTextString(p->GetVersion()) + "</t>");
                writeData.push_back(CyString("          <t id=\"") + (long)(start + 3) + "\">" + this->ConvertTextString(p->GetLanguageName(lang)) + "</t>");

                CLinkList<SSettingType> *settings = spk->GetSettingsList();
                if ( settings && settings->size() )
                {
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 4) + "\">" + (long)settings->size() + "</t>");
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 5) + "\">" + (long)settingStart + "</t>");

                        for ( CListNode<SSettingType> *sNode = settings->Front(); sNode; sNode = sNode->next() )
                        {
                                SSettingType *st = sNode->Data();
                                writeData.push_back(CyString("          <t id=\"") + (long)(settingStart++) + "\">" + st->sKey + "</t>");
                                writeData.push_back(CyString("          <t id=\"") + (long)(settingStart++) + "\">" + spk->GetSetting(st) + "</t>");
                        }
                }
                else
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 4) + "\">0</t>");

                writeData.push_back(CyString("          <t id=\"") + (long)(start + 6) + "\">" + sTextCount + "</t>");
                if ( textCount )
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 7) + "\">" + textEntries + "</t>");

                start += 10;
        }

        // write ware names
        if ( lWares.size() )
        {
                writeData.push_back(CyString("          <t id=\"5\">") + CyString::Number(start) + "</t>");
                for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
                {
                        SGameWare *w = node->Data();
                        if ( w->pWare && w->iType == WARETYPE_ADDED )
                                writeData.push_back(CyString("          <t id=\"") + (long)start + "\">" + this->ConvertTextString(w->sWareName) + "</t>");
                        else
                                writeData.push_back(CyString("          <t id=\"") + (long)start + "\">-1</t>");
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 1) + "\">" + CyString((char)w->cType) + "</t>");
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 2) + "\">" + (long)w->iPos + "</t>");
                        start += 10;
                }
        }

        if ( lShips.size() )
        {
                writeData.push_back(CyString("          <t id=\"7\">") + CyString::Number(start) + "</t>");
                for ( CListNode<SGameShip> *node = lShips.Front(); node; node = node->next() )
                {
                        SGameShip *gs = node->Data();
                        if ( gs->iType == WARETYPE_NONE )
                                continue;
                        if ( gs->pPackage && gs->iType == WARETYPE_ADDED )
                                writeData.push_back(CyString("          <t id=\"") + (long)start + "\">" + gs->sShipID + "</t>");
                        else
                                writeData.push_back(CyString("          <t id=\"") + (long)start + "\">-1</t>");
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 1) + "\">" + (long)gs->iPos + "</t>");
                        writeData.push_back(CyString("          <t id=\"") + (long)(start + 2) + "\">Ship</t>");

                        // write shipyard info
                        if ( gs->pPackage )
                        {
                                int doStart = start + 5;
                                for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
                                {
                                        writeData.push_back(CyString("          <t id=\"") + (long)(doStart) + "\">" + ((gs->pPackage->IsShipyard(i)) ? CyString::Number(1) : CyString::Number(0)) + "</t>");
                                        ++doStart;
                                }
                        }

                        start += 20;
                }
        }

        writeData.push_back(CyString("  </page>"));

        // wares
        if ( m_iGame == GAME_X3AP || m_iGame == GAME_X3TC || m_iGame == GAME_X3 || lWares.size() || lShips.size() )
        {
                if ( !gameNumber )
                        writeData.push_back(CyString("  <page id=\"17\" title=\"Plugin Manager Objects\">"));
                else
                        writeData.push_back(CyString("  <page id=\"") + (long)gameNumber + "0017\" title=\"Plugin Manager Objects\">");

                writeData.push_back(CyString("          <t id=\"") + (long)(SHIPSTARTTEXT - 1) + "\">ZZ_BLANKSHIP</t>");
                // do emp
                if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3 || m_iGame == GAME_X3AP )
                        writeData.push_back(GetEMPText());

                // object names
                for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
                {
                        SGameWare *w = node->Data();
                        if ( !w->pWare || w->iType != WARETYPE_ADDED )
                                continue;

                        // find the correct text for the language
                        CyString name = CSpkFile::GetWareText(w->pWare, m_iLanguage);
                        CyString desc = CSpkFile::GetWareDesc(w->pWare, m_iLanguage);
                        if ( !name.Empty() )
                                writeData.push_back(CyString("          <t id=\"") + (long)(w->iText + 3) + "\">" + this->ConvertTextString(name) + "</t>");
                        if ( !desc.Empty() )
                                writeData.push_back(CyString("          <t id=\"") + (long)(w->iText + 4) + "\">" + this->ConvertTextString(desc) + "</t>");
                }
                for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                {
                        SGameShip *s = node->Data();
                        if ( !s->pPackage || s->iType != WARETYPE_ADDED )
                                continue;
                        if ( s->pPackage->GetOriginalDescription() )
                                continue;

                        CyString name = s->pPackage->GetTextName(m_iLanguage);
                        CyString desc = s->pPackage->GetTextDescription(m_iLanguage);
                        if ( !name.Empty() )
                                writeData.push_back(CyString("          <t id=\"") + (long)s->iText + "\">" + this->ConvertTextString(name) + "</t>");
                        if ( !desc.Empty() )
                                writeData.push_back(CyString("          <t id=\"") + (long)(s->iText + 1) + "\">" + this->ConvertTextString(desc) + "</t>");
                }
                writeData.push_back(CyString("  </page>"));
        }
        writeData.push_back(CyString("</language>"));
        textFile.WriteFileUTF(&writeData);

        size_t fileSize;
        char *fileData = CFileIO(textFile.GetFullFilename()).ReadToData(&fileSize);

        if ( fileData && fileSize)
        {
                size_t newFileSize;
                unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
                if ( pckData )
                {
                        CFileIO pckFile(m_sCurrentDir + "/t/" + filename + ".pck");
                        pckFile.WriteData((char *)pckData, newFileSize);
                        this->AddCreatedFile(pckFile.GetFullFilename());
                }
        }
        textFile.Remove();
}

bool CPackages::IsCurrentDir(CyString dir)
{
        if ( dir.Compare(m_sCurrentDir) )
                return true;

        CyString checkDir = m_sCurrentDir;
        checkDir = checkDir.FindReplace("/", "\\");
        if ( checkDir.Compare(dir) )
                return true;

        checkDir = checkDir.FindReplace("\\", "/");
        if ( checkDir.Compare(dir) )
                return true;

        return false;
}

void CPackages::BackupSaves(bool vanilla)
{
        // copy any saves into the vanilla directory
        CyString dir = (vanilla) ? "Vanilla" : "Modified";

        // make sure the directory exists
        CDirIO saveDir(this->GetSaveDirectory());
        CDirIO gameSaveDir(m_sCurrentDir);
        if ( !gameSaveDir.Exists("PluginManager") )
                gameSaveDir.Create("PluginManager");
        gameSaveDir.cd("PluginManager");
        if ( !gameSaveDir.Exists("Saves") )
                gameSaveDir.Create("Saves");
        gameSaveDir.cd("Saves");
        if ( !gameSaveDir.Exists(dir) )
                gameSaveDir.Create(dir);
        gameSaveDir.cd(dir);

        // backup the saves
        CyStringList *dirs = saveDir.DirList();
        if ( dirs )
        {
                for ( SStringList *node = dirs->Head(); node; node = node->next )
                {
                        CFileIO File(saveDir.File(node->str));
                        if ( !File.CheckFileExtension("sav") )
                                continue;
                        // remove the file if already exists
                        if ( gameSaveDir.Exists(node->str) )
                                CFileIO(gameSaveDir.File(node->str)).Remove();

                        // copy the file into the games save dir for backup
                        File.Copy(gameSaveDir.File(File.GetFilename()), true);
                }

                delete dirs;
        }

}

void CPackages::RestoreSaves(bool vanilla)
{
        // get dir to restore from
        CyString dir = (vanilla) ? "Vanilla" : "Modified";
        CDirIO restoreDir(m_sCurrentDir + "/PluginManager/Saves/" + dir);
        CDirIO toDir(this->GetSaveDirectory());

        CyStringList *dirs = restoreDir.DirList();
        if ( dirs )
        {
                for ( SStringList *node = dirs->Head(); node; node = node->next )
                {
                        CFileIO File(restoreDir.File(node->str));
                        // remove the file if already exists
                        if ( toDir.Exists(node->str) )
                                CFileIO(toDir.File(node->str)).Remove();

                        // move file over
                        File.Copy(toDir.File(node->str), true);
                }

                delete dirs;
        }

}

bool CPackages::RemoveCurrentDirectory()
{
        if ( !m_bLoaded )
                return false;

        // remove all package files
        this->RemoveAllPackages();

        // remove all plugin manager files
        this->RemoveCreatedFiles();

        // restore any vanilla saves
        /*
        if ( !m_bVanilla )
        {
                this->BackupSaves();
                m_bVanilla = true;
                this->RestoreSaves();
        }*/

        this->Reset();
        m_bLoaded = false;

        // clear the plugin manager directory
        CDirIO Dir(m_sCurrentDir);
        Dir.RemoveDir("PluginManager", true, true, 0);
        Dir.RemoveDir("dds", false, true, 0);
        Dir.RemoveDir("objects", false, true, 0);
        Dir.RemoveDir("types", false, true, 0);
        Dir.RemoveDir("textures", false, true, 0);

        // remove the plugin manager mod files
        if ( Dir.Exists("mods/PluginManager.cat") )
                CFileIO(Dir.File("mods/PluginManager.cat")).Remove();
        if ( Dir.Exists("mods/PluginManager.dat") )
                CFileIO(Dir.File("mods/PluginManager.dat")).Remove();

        return true;
}

void CPackages::CreateDummies()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return;

        CLinkList<SDummyEntry> dummyList;

        // now extract the existing dummies
        int e = ExtractGameFile("types/Dummies.pck", m_sTempDir + "/Dummies.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.Open((e == -1) ? "Dummies.txt" : m_sTempDir + "/Dummies.txt") )
                {
                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                int insection = 0;
                                SDummyEntry *currentSection = NULL;
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        line.RemoveChar(9);
                                        line.RemoveChar('\r');
                                        line.RemoveFirstSpace();
                                        if ( line.Empty() )
                                                continue;
                                        if ( line[0] == '/' )
                                                continue;

                                        // read the section, first entry is section, second is size
                                        while ( !line.Empty() )
                                        {
                                                if ( !insection )
                                                {
                                                        CyString section = line.GetToken(";", 1, 1);
                                                        insection = line.GetToken(";", 2, 2).ToInt();

                                                        // search for the sections
                                                        currentSection = NULL;
                                                        for ( CListNode<SDummyEntry> *node = dummyList.Front(); node; node = node->next() )
                                                        {
                                                                SDummyEntry *d = node->Data();
                                                                if ( d->sSection.Compare(section) )
                                                                {
                                                                        currentSection = node->Data();
                                                                        break;
                                                                }
                                                        }

                                                        if ( !currentSection )
                                                        {
                                                                currentSection = new struct SDummyEntry;
                                                                currentSection->sSection = section;
                                                                dummyList.push_back(currentSection);
                                                        }

                                                        // we have some more ?
                                                        line = line.DelToken(";", 1, 2);
                                                }
                                                else
                                                {
                                                        --insection;
                                                        // check the last entry for number of states
                                                        if ( currentSection->sSection.Compare("SDTYPE_GUN") )
                                                        {
                                                                int states = line.GetToken(";", 3, 3).ToInt();
                                                                int parts = line.GetToken(";", 4 + (states * 2), 4 + (states * 2)).ToInt();
                                                                CyString data = line.GetToken(";", 1, 4 + (states * 2) + (parts * 2)) + ";";
                                                                currentSection->lEntries.PushBack(data);

                                                                // remove done
                                                                line = line.DelToken(";", 1, 4 + (states * 2) + (parts * 2));
                                                        }
                                                        else
                                                        {
                                                                int states = line.GetToken(";", 3, 3).ToInt();
                                                                CyString data = line.GetToken(";", 1, 3 + (states * 2)) + ";";
                                                                currentSection->lEntries.PushBack(data);

                                                                // remove done
                                                                line = line.DelToken(";", 1, 3 + (states * 2));
                                                        }
                                                }
                                        }
                                }

                                delete lines;
                        }

                        File.Remove();
                }

                // add the new entries for the ships
                for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                {
                        SGameShip *s = node->Data();
                        if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                                continue;

                        // no dummies to add?
                        if ( !s->pPackage->AnyDummies() )
                                continue;

                        // add each dummy to list
                        for ( CListNode<SDummy> *dNode = s->pPackage->GetDummies()->Front(); dNode; dNode = dNode->next() )
                        {
                                SDummy *dummy = dNode->Data();
                                SDummyEntry *found = NULL;
                                for ( CListNode<SDummyEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
                                {
                                        if ( eNode->Data()->sSection.Compare(dummy->sSection) )
                                        {
                                                found = eNode->Data();
                                                break;
                                        }
                                }
                                if ( !found )
                                {
                                        found = new SDummyEntry;
                                        found->sSection = dummy->sSection;
                                        dummyList.push_back(found);
                                }
                                // check if its already on the list
                                else
                                {
                                        bool f = false;
                                        for ( SStringList *strNode = found->lEntries.Head(); strNode; strNode = strNode->next )
                                        {
                                                if ( strNode->str.GetToken(";", 1, 1).Compare(dummy->sData.GetToken(";", 1, 1)) )
                                                {
                                                        f = true;
                                                        break;
                                                }
                                        }

                                        if ( f )
                                                continue;
                                }

                                found->lEntries.PushBack(dummy->sData);
                        }
                }

                // finally, write the file
                std::vector<CyString> lines;
                lines.push_back(CyString("// Dummies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
                for ( SDummyEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
                {
                        lines.push_back("");
                        lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.Count());
                        lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.Count()) + ";");
                        for ( SStringList *str = dummy->lEntries.Head(); str; str = str->next )
                        {
                                CyString strLine = str->str;
                                strLine.RemoveChar(9);
                                strLine.RemoveChar('\r');
                                strLine.RemoveEndSpace();
                                strLine.RemoveFirstSpace();
                                strLine = strLine.FindReplace("<::PiPe::>", "|");
                                if ( strLine.Right(1) != ";" )
                                        strLine += ";";
                                lines.push_back(strLine);
                        }
                }
                lines.push_back("");

                // write the file to disk
                CFileIO WriteFile(m_sTempDir + "/dummies.txt");
                if ( WriteFile.WriteFile(&lines) )
                {
                        this->PackFile(&WriteFile, "types\\dummies.pck");
                        WriteFile.Remove();
                }
        }
}

void CPackages::CreateCutData()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return;

        bool found = false;
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyCutData() )
                        continue;
                found = true;
                break;
        }

        if ( !found )
                return;

        CyStringList cutList;
        int e = ExtractGameFile("types/CutData.pck", m_sTempDir + "/CutData.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.Open((e == -1) ? "CutData.txt" : m_sTempDir + "/CutData.txt") )
                {
                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                int entries = -1;
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        line.RemoveChar(9);
                                        line.RemoveChar('\r');
                                        line.RemoveChar(' ');
                                        if ( line.Empty() || line[0] == '/' )
                                                continue;
                                        if ( entries == -1 )
                                                entries = line.GetToken(";", 1, 1).ToInt();
                                        else
                                        {
                                                if ( line.Right(1) != ";" )
                                                        line += ";";
                                                cutList.PushBack(line);
                                                if ( cutList.Count() == entries )
                                                        break;
                                        }
                                }

                                delete lines;
                        }

                        File.Remove();
                }
        }

        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyCutData() )
                        continue;

                // add each dummy to list
                for ( SStringList *strNode = s->pPackage->GetCutData()->Head(); strNode; strNode = strNode->next )
                {
                        CyString str = strNode->str;
                        str.RemoveChar(' ');
                        if ( str.Right(1) != ";" )
                                str += ";";
                        cutList.PushBack(str, true);
                }
        }

        cutList.PushFront(CyString::Number(cutList.Count()) + ";");
        cutList.PushFront("/cut id;filename (leave blank to use id)");
        cutList.PushFront(CyString("// Cut Data file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + "/CutData.txt");
        if ( WriteFile.WriteFile(&cutList) )
        {
                this->PackFile(&WriteFile, "types\\CutData.pck");
                WriteFile.Remove();
        }
}

void CPackages::CreateAnimations()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return;

        bool found = false;
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyAnimations() )
                        continue;
                found = true;
                break;
        }

        if ( !found )
                return;

        CyStringList aniList;
        int e = ExtractGameFile("types/Animations.pck", m_sTempDir + "/Animations.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.Open((e == -1) ? "Animations.txt" : m_sTempDir + "/Animations.txt") )
                {
                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        aniList.PushBack(line);
                                }

                                delete lines;
                        }

                        File.Remove();
                }
        }

        CyStringList parsedAniList;
        CXspFile::ReadAnimations(&aniList, &parsedAniList, 0);

        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyAnimations() )
                        continue;

                // add each dummy to list
                for ( SStringList *strNode = s->pPackage->GetAnimations()->Head(); strNode; strNode = strNode->next )
                        parsedAniList.PushBack(strNode->str);
        }

        // format the list with added spaces
        CyStringList formatedAniList;
        int lineCount = -1;
        for ( SStringList *strNode = parsedAniList.Head(); strNode; strNode = strNode->next )
        {
                // format the comment to match the line number
                lineCount++;
                CyString oldComment = strNode->str.GetToken("//", 2);
                CyString comment = CyString("//") + CyString::Number(lineCount);
                if ( !oldComment.Empty() )
                {
                        comment += " ";
                        oldComment.RemoveFirstSpace();
                        if ( oldComment.GetToken(" ", 1, 1).IsNumber() )
                                comment += oldComment.GetToken(" ", 2);
                        else
                                comment += oldComment;
                }
                CyString line = strNode->str.GetToken("//", 1, 1);

                // split into seperate lines
                CyString first = line.GetToken(";", 1, 1);
                if ( first.Compare("TAT_TAGSINGLESTEP") )
                {
                        formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
                        int max;
                        CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
                        if ( max && sLines )
                        {
                                if ( sLines[max - 1].Empty() )
                                        --max; // remove the last ";"

                                for ( int i = 0; i < max; i++ )
                                {
                                        CyString l = CyString("\t") + sLines[i] + ";";
                                        if ( i == (max - 1) )
                                                formatedAniList.PushBack(l + comment);
                                        else
                                                formatedAniList.PushBack(l);
                                }
                        }
                        CLEANSPLIT(sLines, max);
                }
                else if ( (first.Compare("TAT_TAGONESHOT") || first.Compare("TAT_TAGLOOP")) && (line.IsIn("TATF_COORDS")) )
                {
                        formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
                        int max;
                        CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
                        if ( max && sLines )
                        {
                                if ( sLines[max - 1].Empty() )
                                        --max; // remove the last ";"

                                CyString prevLine;
                                for ( int i = 0; i < max; i++ )
                                {
                                        CyString l = sLines[i] + ";";
                                        if ( l.IsIn("TATF_COORDS") && !prevLine.Empty() )
                                        {
                                                formatedAniList.PushBack(CyString("\t") + prevLine);
                                                prevLine = "";
                                        }
                                        prevLine += l;
                                }

                                if ( !prevLine.Empty() )
                                        formatedAniList.PushBack(CyString("\t") + prevLine + comment);

                        }
                        CLEANSPLIT(sLines, max);
                }
                else
                        formatedAniList.PushBack(line + comment);
        }

        formatedAniList.PushFront(CyString::Number(parsedAniList.Count()) + ";");
        formatedAniList.PushFront(CyString("// Animations, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + "/Animations.txt");
        if ( WriteFile.WriteFile(&formatedAniList) )
        {
                this->PackFile(&WriteFile, "types\\Animations.pck");
                WriteFile.Remove();
        }
}

void CPackages::CreateBodies()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return;

        bool found = false;
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyBodies() )
                        continue;
                found = true;
                break;
        }

        if ( !found )
                return;

        // lets read our current bodies file
        CLinkList<SBodies> bodiesList;
        SBodies *currentSection = NULL;
        int e = ExtractGameFile("types/Bodies.pck", m_sTempDir + "/Bodies.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.Open((e == -1) ? "Bodies.txt" : m_sTempDir + "/Bodies.txt") )
                {
                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                int entries = 0;
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        line.RemoveChar(' ');
                                        line.RemoveChar(9);
                                        if ( line.Empty() || line[0] == '/' )
                                                continue;
                                        if ( entries <= 0 )
                                        {
                                                entries = line.GetToken(";", 2, 2).ToInt();
                                                currentSection = new SBodies;
                                                currentSection->sSection = line.GetToken(";", 1, 1);
                                                bodiesList.push_back(currentSection);
                                        }
                                        else if ( currentSection )
                                        {
                                                int num;
                                                CyString *strs = line.SplitToken(";", &num);
                                                if ( num && strs )
                                                {
                                                        for ( int i = 0; i < num; i++ )
                                                        {
                                                                if ( strs[i].Empty() )
                                                                        continue;
                                                                currentSection->lEntries.PushBack(strs[i] + ";", true);
                                                                --entries;
                                                        }
                                                }
                                                CLEANSPLIT(strs, num);
                                        }
                                }

                                delete lines;
                        }
                        File.Remove();
                }
        }

        // lets now add any new entries
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                SGameShip *s = node->Data();
                if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                        continue;

                // no dummies to add?
                if ( !s->pPackage->AnyBodies() )
                        continue;

                // add each dummy to list
                for ( SStringList *strNode = s->pPackage->GetBodies()->Head(); strNode; strNode = strNode->next )
                {
                        CyString section = strNode->str.GetToken(";", 1, 1);
                        CyString body = strNode->str.GetToken(";", 2).Remove(' ');
                        if ( body.Right(1) != ";" )
                                body += ";";

                        // find the section to add into
                        SBodies *foundSection = NULL;
                        for ( CListNode<SBodies> *checkBody = bodiesList.Front(); checkBody; checkBody = checkBody->next() )
                        {
                                if ( checkBody->Data()->sSection.Compare(section) )
                                {
                                        foundSection = checkBody->Data();
                                        break;
                                }
                        }

                        if ( !foundSection )
                        {
                                foundSection = new SBodies;
                                foundSection->sSection = section;
                                bodiesList.push_back(foundSection);
                        }
                        foundSection->lEntries.PushBack(body, true);
                }
        }

        // now write the file
        CyStringList writeList;
        // the header first
        writeList.PushBack(CyString("// Bodies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
        writeList.PushBack("//body type;num bodies;");
        writeList.PushBack("//[body id/name]");

        // now our sections
        for ( SBodies *bSection = bodiesList.First(); bSection; bSection = bodiesList.Next() )
        {
                writeList.PushBack("");
                writeList.PushBack(CyString("// Section: ") + bSection->sSection);
                writeList.PushBack(bSection->sSection + ";" + CyString::Number(bSection->lEntries.Count()) + ";");
                for ( SStringList *strNode = bSection->lEntries.Head(); strNode; strNode = strNode->next )
                {
                        CyString str = strNode->str;
                        str.RemoveChar(9);
                        str.RemoveChar(' ');
                        if ( str.Right(1) != ";" )
                                str += ";";
                        writeList.PushBack(str);
                }
        }

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + "/Bodies.txt");
        if ( WriteFile.WriteFile(&writeList) )
        {
                this->PackFile(&WriteFile, "types\\Bodies.pck");
                WriteFile.Remove();
        }
}

void CPackages::CreateCustomStarts()
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                // find all spk files (only ones that can be custom starts
                if ( node->Data()->GetType() != TYPE_SPK )
                        continue;

                CSpkFile *p = (CSpkFile *)node->Data();

                // only use custom starts
                if ( !p->IsCustomStart() )
                        continue;

                // get the name of the start to use
                CyString name = p->GetCustomStartName();
                if ( name.Empty() )
                        continue;

                // find if maps file exists
                CyStringList createFiles;
                createFiles.PushBack(name, "maps/x3_universe");
                createFiles.PushBack(name, "types/Jobs");
                createFiles.PushBack(name, "types/JobWings");

                for ( SStringList *str = createFiles.Head(); str; str = str->next )
                {
                        CyString dir = CFileIO(str->data).GetDir();
                        int type = FILETYPE_EXTRA;
                        if ( dir.Compare("maps") )
                                type = FILETYPE_MAP;

                        if ( !p->FindFile(str->str + ".xml", type) && !p->FindFile(str->str + ".pck", type) )
                        {
                                // create a maps files
                                int e = this->ExtractGameFile(str->data + ".pck", m_sTempDir + "/" + str->data + ".pck");
                                if ( e )
                                {
                                        CFileIO File((e == -1) ? (str->data + ".pck") : (m_sTempDir + "/" + str->data + ".pck"));
                                        if ( File.Exists() )
                                        {
                                                File.Rename(m_sCurrentDir + "/" + dir + "/" + str->str + ".pck");
                                                this->AddCreatedFile(dir + "/" + str->str + ".pck");
                                        }
                                }
                        }
                }
        }
}

void CPackages::AddCreatedFile(CyString file)
{
        file = file.Remove(m_sCurrentDir);
        while ( file[0] == '/' )
                file.Erase(0, 1);
        while ( file[0] == '\\' )
                file.Erase(0, 1);

        m_lCreatedFiles.PushBack(file, true);
}

void CPackages::CreateComponants()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return;

        CLinkList<SComponantEntry> dummyList;

        // now extract the existing dummies
        int e = ExtractGameFile("types/Components.pck", m_sTempDir + "/Components.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.Open((e == -1) ? "Components.txt" : m_sTempDir + "/Components.txt") )
                {
                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                int insection = 0;
                                int insubsection = 0;
                                SComponantEntry *currentSection = NULL;
                                SComponantEntry2 *currentSubSection = NULL;
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        if ( line[0] == '/' )
                                                continue;
                                        line.RemoveChar('\r');
                                        line = line.RemoveFirstSpace();
                                        line = line.RemoveEndSpace();
                                        if ( line.Empty() )
                                                continue;


                                        // read the section, first entry is section, second is size
                                        while ( !line.Empty() )
                                        {
                                                line = line.RemoveFirstSpace();
                                                if ( line.Empty() )
                                                        break;

                                                if ( !insection && !insubsection )
                                                {
                                                        CyString section = line.GetToken(";", 1, 1);
                                                        insection = line.GetToken(";", 2, 2).ToInt();

                                                        // search for the sections
                                                        currentSection = NULL;
                                                        for ( CListNode<SComponantEntry> *node = dummyList.Front(); node; node = node->next() )
                                                        {
                                                                SComponantEntry *d = node->Data();
                                                                if ( d->sSection.Compare(section) )
                                                                {
                                                                        currentSection = node->Data();
                                                                        break;
                                                                }
                                                        }

                                                        if ( !currentSection )
                                                        {
                                                                currentSection = new SComponantEntry;
                                                                currentSection->sSection = section;
                                                                dummyList.push_back(currentSection);
                                                        }

                                                        // we have some more ?
                                                        line = line.DelToken(";", 1, 2);
                                                }
                                                else if ( !insubsection )
                                                {
                                                        --insection;
                                                        CyString section = line.GetToken(";", 1, 1);
                                                        insubsection = line.GetToken(";", 2, 2).ToInt();

                                                        currentSubSection = new SComponantEntry2;
                                                        currentSubSection->sSection = section;
                                                        currentSection->lEntries.push_back(currentSubSection);

                                                        line = line.DelToken(";", 1, 2);
                                                }
                                                else
                                                {
                                                        --insubsection;
                                                        currentSubSection->lEntries.PushBack(line.Remove(' '), true);
                                                        line = "";
                                                }
                                        }
                                }

                                delete lines;
                        }

                        File.Remove();
                }

                // add the new entries for the ships
                for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                {
                        SGameShip *s = node->Data();
                        if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                                continue;

                        // no dummies to add?
                        if ( !s->pPackage->AnyComponents() )
                                continue;

                        // add each dummy to list
                        for ( CListNode<SComponent> *dNode = s->pPackage->GetComponents()->Front(); dNode; dNode = dNode->next() )
                        {
                                SComponent *dummy = dNode->Data();
                                SComponantEntry *found = NULL;
                                SComponantEntry2 *found2 = NULL;
                                for ( CListNode<SComponantEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
                                {
                                        if ( eNode->Data()->sSection.Compare(dummy->sSection) )
                                        {
                                                found = eNode->Data();
                                                break;
                                        }
                                }
                                if ( !found )
                                {
                                        found = new SComponantEntry;
                                        found->sSection = dummy->sSection;
                                        dummyList.push_back(found);
                                        found2 = new SComponantEntry2;
                                        found2->sSection = dummy->sSection2;
                                        found->lEntries.push_back(found2);
                                }
                                // else check for the 2nd section
                                else
                                {
                                        for ( CListNode<SComponantEntry2> *cNode = found->lEntries.Front(); cNode; cNode = cNode->next() )
                                        {
                                                if ( cNode->Data()->sSection.Compare(dummy->sSection2) )
                                                {
                                                        found2 = cNode->Data();
                                                        break;
                                                }
                                        }

                                        if ( !found2 )
                                        {
                                                found2 = new SComponantEntry2;
                                                found2->sSection = dummy->sSection2;
                                                found->lEntries.push_back(found2);
                                        }
                                        else
                                        {
                                                bool f = false;
                                                for ( SStringList *strNode = found2->lEntries.Head(); strNode; strNode = strNode->next )
                                                {
                                                        if ( dummy->sData.Remove(' ').Compare(strNode->str) )
                                                        {
                                                                f = true;
                                                                break;
                                                        }
                                                }

                                                if ( f )
                                                        continue;
                                        }
                                }

                                found2->lEntries.PushBack(dummy->sData.Remove(' '));
                        }
                }

                // finally, write the file
                std::vector<CyString> lines;
                lines.push_back(CyString("// Components file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
                for ( SComponantEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
                {
                        lines.push_back("");
                        lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.size());
                        lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.size()) + ";");
                        for ( CListNode<SComponantEntry2> *comp = dummy->lEntries.Front(); comp; comp = comp->next() )
                        {
                                lines.push_back(comp->Data()->sSection + ";" + (long)comp->Data()->lEntries.Count() + ";");
                                for ( SStringList *str = comp->Data()->lEntries.Head(); str; str = str->next )
                                {
                                        CyString cStr = str->str;
                                        cStr.RemoveEndSpace();
                                        cStr.RemoveChar(9);
                                        cStr.RemoveChar('\r');
                                        if ( cStr.Right(1) != ";" )
                                                cStr += ";";
                                        lines.push_back(cStr);
                                }
                        }
                }

                // write the file to disk
                CFileIO WriteFile(m_sTempDir + "/Components.txt");
                if ( WriteFile.WriteFile(&lines) )
                {
                        this->PackFile(&WriteFile, "types\\Components.pck");
                        WriteFile.Remove();
                }
        }
}

bool CPackages::ReadGlobals(CyStringList &globals)
{
        int e = ExtractGameFile("types/Globals.pck", m_sTempDir);
        if ( e )
        {
                CFileIO File(((e == -1) ? "." : m_sTempDir) + "/Globals.txt");
                if ( File.Exists() )
                {
                        CyStringList *lines = File.ReadLinesStr();
                        if ( lines )
                        {
                                int entries = -1;
                                for ( SStringList *str = lines->Head(); str; str = str->next )
                                {
                                        str->str.RemoveChar('\r');
                                        str->str.RemoveChar(9);
                                        str->str.RemoveFirstSpace();

                                        if ( str->str.Empty() )
                                                continue;
                                        if ( str->str[0] == '/' )
                                                continue;

                                        // remove comments
                                        CyString l = str->str;
                                        if ( l.IsIn("/") ) 
                                                l = l.GetToken("/", 1, 1);

                                        if ( entries == -1 )
                                                entries = l.GetToken(";", 1, 1).ToInt();
                                        else
                                                globals.PushBack(l.GetToken(";", 1, 1), l.GetToken(";", 2, 2));
                                }

                                delete lines;

                                return true;
                        }
                }
        }

        return false;
}

void CPackages::CreateGlobals()
{
        if ( m_lGlobals.Empty() )
                return; // no global settings

        CyStringList globals;
        if ( ReadGlobals(globals) )
        {
                // apply out settings
                for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
                {
                        SStringList *found = globals.FindString(str->str);
                        if ( found )
                                found->data = str->data;
                }

                // now write it
                CyStringList writeList;
                for ( SStringList *str = globals.Head(); str; str = str->next )
                        writeList.PushBack(str->str + ";" + str->data + ";");
                
                // finally, write the file
                writeList.PushFront(CyString::Number(writeList.Count()) + "; /globals amount", "");
                writeList.PushFront(CyString("// Globals file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");

                CFileIO WriteFile(m_sTempDir + "/Globals.txt");
                if ( WriteFile.WriteFile(&writeList) )
                {
                        this->PackFile(&WriteFile, "types/Globals.pck");
                        WriteFile.Remove();
                }
        }
}

void CPackages::CreateTShips()
{
        // no ships ?
        if ( m_lGameShips.empty() )
                return;

        // get the cockpit list to match with ships turrets
        CyStringList Cockpits;
        CyStringList *cockpitList = this->CreateCockpits();
        if ( cockpitList )
        {
                for ( SStringList *str = cockpitList->Head(); str; str = str->next )
                {
                        CyString id = str->str.GetToken(";", 19, 19);
                        Cockpits.PushBack(id);
                }

                delete cockpitList;
        }

        CLinkList<SGameShip> shipOverrides;
        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
        {
                if ( node->Data()->iType != WARETYPE_ADDED || !node->Data()->pPackage )
                        continue;               
                if ( !node->Data()->pPackage->IsExistingShip() )
                        continue;
                shipOverrides.push_back(node->Data());
        }

        // read the existing tships file
        int e = ExtractGameFile("types/TShips.pck", m_sTempDir + "/TShips.txt");
        if ( e )
        {
                int fileType = 51;
                CyStringList tshipsList;

                // if we have no buffer, lets create one
                CFileIO File;
                if ( File.Open((e == -1) ? "TShips.txt" : m_sTempDir + "/TShips.txt") )
                {
                        int shiptext = SHIPSTARTTEXT;

                        std::vector<CyString> *lines = File.ReadLines();
                        if ( lines )
                        {
                                int count = -1;
                                for ( int j = 0; j < (int)lines->size(); j++ )
                                {
                                        CyString line(lines->at(j));
                                        if ( line[0] == '/' )
                                                continue;
                                        line.RemoveChar('\r');
                                        line.RemoveChar(9);
                                        line = line.RemoveFirstSpace();
                                        line = line.RemoveEndSpace();
                                        if ( line.Empty() )
                                                continue;

                                        if ( count == -1 )
                                        {
                                                fileType = line.GetToken(";", 1, 1).ToInt();
                                                count = line.GetToken(";", 2, 2).ToInt();
                                        }
                                        else
                                        {
                                                if ( line.Right(1) != ";" )
                                                        line += ";";

                                                // check for any ship overrides
                                                bool added = false;
                                                if ( !shipOverrides.empty() )
                                                {
                                                        CShipData shipData;
                                                        if ( shipData.ReadShipData(line) )
                                                        {
                                                                for ( CListNode<SGameShip> *node = shipOverrides.Front(); node; node = node->next() )
                                                                {
                                                                        SGameShip *s = node->Data();
                                                                        if ( !s->pPackage->GetShipID().Compare(shipData.sID.ToString()) )
                                                                                continue;
                                                                        s->iText = shiptext;
                                                                        if ( !s->pPackage->GetOriginalDescription() )
                                                                                shiptext += 2;
                                                                        s->iPos = tshipsList.Count();
                                                                        added = true;
                                                                        tshipsList.PushBack(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame));
                                                                        shipOverrides.remove(node);
                                                                        break;
                                                                }
                                                        }
                                                }

                                                if ( !added )
                                                        tshipsList.PushBack(line);
                                                --count;
                                                if ( count < 0 )
                                                        break;
                                        }
                                }

                                delete lines;

                        }

                        File.Remove();

                        // assign the ship buffer
                        if ( !m_iShipBuffer )
                                m_iShipBuffer = tshipsList.Count() + 15;
                        // there seems to be too many additional entries, we have no choise but to change the buffer
                        else if ( m_iShipBuffer <= tshipsList.Count() )
                                m_iShipBuffer = tshipsList.Count() + 15;

                        CyString bufferStart;
                        if ( m_iGame == GAME_X3 )
                                bufferStart = "0;0;0;0;0;2;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;2092;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
                        else
                                bufferStart = "0;0;0;0;0;SG_SH_M5;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;OBJ_BEACON;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
                        // add the buffers now
                        for ( int i = tshipsList.Count(); i < m_iShipBuffer; i++ )
                                tshipsList.PushBack(bufferStart + "SHIP_BUFFER;");

                        // now lets add our tships line
                        for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                        {
                                SGameShip *s = node->Data();
                                if ( s->pPackage && s->pPackage->IsExistingShip() )
                                        continue;
                                s->iPos = tshipsList.Count();
                                if ( s->iType == WARETYPE_ADDED && s->pPackage )
                                {
                                        s->iText = shiptext;
                                        if ( !s->pPackage->GetOriginalDescription() )
                                                shiptext += 2;

                                        tshipsList.PushBack(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame));
                                }
                                else if ( s->iType == WARETYPE_DELETED )
                                        tshipsList.PushBack(bufferStart + "SHIP_DELETED;");
                                else if ( s->iType == WARETYPE_DISABLED )
                                        tshipsList.PushBack(bufferStart + "SHIP_DISABLED;");
                                else
                                        tshipsList.PushBack(bufferStart + "SHIP_SPACER;");
                        }

                        // finally, write the file
                        tshipsList.PushFront(CyString::Number(fileType) + ";" + CyString::Number(tshipsList.Count()) + ";", "");
                        tshipsList.PushFront(CyString("// TShips file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");

                        CFileIO WriteFile(m_sTempDir + "/TShips.txt");
                        if ( WriteFile.WriteFile(&tshipsList) )
                        {
                                this->PackFile(&WriteFile, "types/TShips.pck");
                                WriteFile.Remove();
                        }
                }
        }

}

bool CPackages::PackFile(CyString filename)
{
        // compress the file
        CFileIO File(filename);
        size_t fileSize;
        char *fileData = File.ReadToData(&fileSize);

        if ( fileData && fileSize)
        {
                size_t newFileSize;
                unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
                if ( pckData )
                {
                        CyString ext = "pck";
                        if ( File.CheckFileExtension("bob") )
                                ext = "pbb";
                        else if ( File.CheckFileExtension("bod") )
                                ext = "pbd";
                        CFileIO pckFile(File.ChangeFileExtension(ext));
                        if ( !CDirIO(pckFile.GetDir()).Exists() )
                                CDirIO(pckFile.GetDir()).Create();
                        pckFile.WriteData((char *)pckData, newFileSize);
                        return true;
                }
        }

        return false;
}

bool CPackages::UnPackFile(CyString filename, bool checkxml)
{
        // compress the file
        CFileIO File(filename);
        size_t fileSize;
        char *fileData = File.ReadToData(&fileSize);

        if ( fileData && fileSize)
        {
                size_t newFileSize;
                unsigned char *pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
                if ( pckData )
                {
                        CyString ext = "txt";
                        if ( File.CheckFileExtension("pbb") )
                                ext = "bob";
                        else if ( File.CheckFileExtension("pbd") )
                                ext = "bod";
                        CFileIO pckFile(File.ChangeFileExtension(ext));
                        if ( !CDirIO(pckFile.GetDir()).Exists() )
                                CDirIO(pckFile.GetDir()).Create();
                        pckFile.WriteData((char *)pckData, newFileSize);

                        // check for xml and rename
                        if ( checkxml )
                        {
                                int readmaxlines = 20;
                                bool isxml = false;
                                do {
                                        CyString line = pckFile.ReadToEndLine(true);
                                        if ( line.IsIn("<language id=") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        else if ( line.IsIn("<page id=") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        else if ( line.IsIn("<?xml") || line.IsIn("<script>") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        --readmaxlines;
                                        if ( readmaxlines <= 0 )
                                                break;
                                } while (pckFile.IsOpened());

                                if ( pckFile.IsOpened() )
                                        pckFile.StopRead();

                                if ( isxml )
                                        pckFile.Rename(pckFile.ChangeFileExtension("xml"));
                        }

                        return true;
                }
        }

        return false;
}

bool CPackages::PackFile(CFileIO *File, CyString filename)
{
        filename = filename.FindReplace("\\", "/");
        if ( m_iGame == GAME_X3 )
        {
                CCatFile catFile;
                int error = catFile.Open(m_sCurrentDir + "/mods/PluginManager.cat", this->GetAddonDir(), CATREAD_CATDECRYPT, true);
                if ( error == CATERR_NONE || error == CATERR_CREATED )
                {
                        // it it wrote ok, remove the old ones
                        if ( !catFile.AppendFile(File->GetFullFilename(), filename, true, true) )
                                return false;
                        return true;
                }
        }
        else
        {
                // compress the file
                size_t fileSize;
                char *fileData = CFileIO(File->GetFullFilename()).ReadToData(&fileSize);

                if ( fileData && fileSize)
                {
                        size_t newFileSize;
                        unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
                        if ( pckData )
                        {
//                              if ( !this->GetAddonDir().Empty() && CCatFile::IsAddonDir(filename) )
//                                      filename = this->GetAddonDir() + "/" + filename;
                                CFileIO pckFile(m_sCurrentDir + "/" + filename);
                                if ( !CDirIO(pckFile.GetDir()).Exists() )
                                        CDirIO(pckFile.GetDir()).Create();
                                pckFile.WriteData((char *)pckData, newFileSize);
                                this->AddCreatedFile(pckFile.GetFullFilename());
                                return true;
                        }
                }
        }

        return false;
}

CyStringList *CPackages::CreateCockpits()
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
                return NULL;

        CyStringList *cockpitList = new CyStringList;

        // now extract the existing cockpits
        int fileType = 51;
        int e = ExtractGameFile("types/TCockpits.pck", m_sTempDir + "/TCockpits.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.Open((e == -1) ? "TCockpits.txt" : m_sTempDir + "/TCockpits.txt") )
                {
                        CyStringList *lines = File.ReadLinesStr();
                        if ( lines )
                        {
                                int count = -1;
                                for ( SStringList *str = lines->Head(); str; str = str->next )
                                {
                                        CyString line(str->str);
                                        line.RemoveChar('\r');
                                        line.RemoveChar(9);
                                        line = line.RemoveFirstSpace();
                                        line = line.RemoveEndSpace();
                                        if ( line.Empty() )
                                                continue;
                                        if ( line[0] == '/' )
                                                continue;

                                        if ( count == -1 )
                                        {
                                                fileType = line.GetToken(";", 1, 1).ToInt();
                                                count = line.GetToken(";", 2, 2).ToInt();
                                        }
                                        else
                                        {
                                                while ( !line.Empty() )
                                                {
                                                        CyString data = line.GetToken(";", 1, 19);
                                                        cockpitList->PushBack(data + ";");
                                                        line = line.DelToken(";", 1, 19);

                                                        --count;
                                                        if ( count < 1 )
                                                                break;
                                                }
                                        }
                                }

                                delete lines;
                        }

                        File.Remove();
                }

                // now add the new ones
                for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
                {
                        SGameShip *s = node->Data();
                        if ( s->iType != WARETYPE_ADDED || !s->pPackage )
                                continue;

                        if ( !s->pPackage->AnyCockpits() )
                                continue;

                        bool found = false;
                        for ( CListNode<SCockpit> *cn = s->pPackage->GetCockpits()->Front(); cn; cn = cn->next() )
                        {
                                bool foundEntry = false;
                                CyString cockpitStr = cn->Data()->sCockpit;
                                // search for matching game entry
                                for ( CListNode<SWeaponMask> *wm = cn->Data()->lWeaponMask.Front(); wm; wm = wm->next() )
                                {
                                        if ( wm->Data()->iGame == (m_iGame - 1) )
                                        {
                                                if ( wm->Data()->iMask != -1 )
                                                        cockpitStr = cockpitStr.RepToken(";", 9, CyString::Number(wm->Data()->iMask));
                                                foundEntry = true;
                                                break;
                                        }
                                }
                                for ( SStringList *str = cockpitList->Head(); str; str = str->next )
                                {
                                        if ( str->str.GetToken(";", 19, 19).Compare(cn->Data()->sCockpit.GetToken(";", 19, 19)) )
                                        {
                                                // only replace existing entry if we have sepeperate weapon masks set
                                                if ( foundEntry )
                                                        str->str = cockpitStr;
                                                found = true;
                                                break;
                                        }
                                }

                                if ( !found )
                                        cockpitList->PushBack(cockpitStr);
                        }
                }

                // finally, write the file
                cockpitList->PushFront(CyString::Number(fileType) + ";" + CyString::Number(cockpitList->Count()) + ";", "");
                cockpitList->PushFront(CyString("// TCockpits file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");

                CFileIO WriteFile(m_sTempDir + "/TCockpits.txt");
                if ( WriteFile.WriteFile(cockpitList) )
                {
                        this->PackFile(&WriteFile, "types\\TCockpits.pck");
                        WriteFile.Remove();
                }

                // remove those entrys
                cockpitList->PopFront();
                cockpitList->PopFront();
        }

        return cockpitList;
}

CBaseFile *CPackages::FindScriptByAuthor(CyString author, CBaseFile *prev)
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                CBaseFile *p = node->Data();
                if ( prev )
                {
                        if ( p == prev )
                                prev = NULL;
                        continue;
                }
                if ( p->GetAuthor().Compare(author) )
                        return p;
        }

        return NULL;
}

CBaseFile *CPackages::LoadPackagerScript(CyString filename, int compression, CyString (*askFunc)(CyString), CyStringList *malformedLines, CyStringList *unknownCommands, CyStringList *variables)
{
        // check the file exists
        if ( !CFileIO(filename).Exists() )
                return NULL;

        // read all the lines
        CFileIO File(filename);
        std::vector<CyString> *lines = File.ReadLines();

        if ( !lines )
                return NULL;

        CBaseFile *package = NULL;

        // filter out blank lines and comments
        CyStringList fileData;
        for ( int i = 0; i < (int)lines->size(); i++ )
        {
                CyString line(lines->at(i));

                // dont include empty lines (whitespace)
                if ( line.Empty() )
                        continue;

                // filter out any spaces, tabs in front
                line.RemoveChar('\t');
                line.RemoveChar('\r');
                CyString linenospace = line;
                linenospace.RemoveFirstSpace();
                if ( linenospace.Empty() )
                        continue;

                // check for any comments
                if ( linenospace.Left(2) == "//" )
                        continue;
                if ( linenospace[0] == '#' )
                        continue;

                // all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
                if ( !line.IsIn(':') )
                {
                        // there are some exeptions, and these are one word entrys only
                        line.RemoveEndSpace();
                        if ( line.IsIn(" ") )
                        {
                                if ( malformedLines )
                                        malformedLines->PushBack(line, CyString::Number(i));
                                continue;
                        }
                }

                // check for the type line
                if ( !package && line.GetToken(":", 1, 1).Compare("FileType") )
                {
                        CyString sFileType = line.GetToken(":", 2).RemoveFirstSpace();
                        if ( sFileType.Compare("Ship") )
                                package = new CXspFile();
                        else if ( sFileType.Compare("Script") )
                                package = new CSpkFile();
                        else if ( sFileType.Compare("Base") )
                                package = new CBaseFile();
                }

                fileData.PushBack(line, CyString::Number(i));
        }

        delete lines;

        // assume its a script if no type is set (all old versions are scripts)
        if ( !package )
                package = new CSpkFile();

        if ( compression != -1 )
                package->SetDataCompression(compression);

        CyString ftpaddr;
        CyString ftpuser;
        CyString ftppass;
        CyString ftpdir;
        // now lets read the rest of the day
        CyStringList listVaribles;
        for ( SStringList *strNode = fileData.Head(); strNode; strNode = strNode->next )
        {
                CyString first = strNode->str.GetToken(":", 1, 1);
                CyString rest = strNode->str.GetToken(":", 2).RemoveFirstSpace();

                if ( first.Compare("Varible") || first.Compare("Variable") )
                        listVaribles.PushBack(rest.GetToken(" ", 1, 1), rest.GetToken(" ", 2), true);
                else
                {
                        // replace variables
                        if ( rest.IsIn("$") )
                        {
                                for ( SStringList *strVar = listVaribles.Head(); strVar; strVar = strVar->next )
                                {
                                        if ( rest.IsIn(strVar->str) )
                                                rest.FindReplace(strVar->str, strVar->data);
                                }

                                if ( variables )
                                {
                                        for ( SStringList *strVar = variables->Head(); strVar; strVar = strVar->next )
                                        {
                                                if ( rest.IsIn(strVar->str) )
                                                        rest.FindReplace(strVar->str, strVar->data);
                                        }
                                }
                        }

                        //check for the built in varibles
                        if ( rest.IsIn("$ASK") )
                        {
                                CyString replace = "$ASK";
                                CyString result;

                                if ( askFunc )
                                        result = askFunc(first);

                                if ( rest.IsIn("$ASK(") )
                                {
                                        replace = rest.GetToken("$ASK(", 2).GetToken(")", 1, 1);
                                        if ( result.Empty() )
                                                result = replace;
                                        replace = CyString("$ASK(") + replace + ")";
                                }


                                if ( !result.Empty() )
                                        rest.FindReplace(replace, result);
                        }
                        // todays date
                        if ( rest.IsIn("$DATE") )
                        {
                                time_t now;
                                time(&now);
                                struct tm *timeinfo = localtime(&now);
                                CyString result = CyString::Number(timeinfo->tm_mday) + "." + CyString::Number(timeinfo->tm_mon + 1) + "." + CyString::Number(timeinfo->tm_year + 1900);
                                if ( !result.Empty() )
                                        rest.FindReplace("$DATE", result);
                        }
                        // mydocuments
                        if ( rest.IsIn("$MYDOCUMENTS") )
                        {
                                if ( !m_sMyDoc.Empty() )
                                        rest.FindReplace("$MYDOCUMENTS", m_sMyDoc);
                        }

                        // current path
                        if ( rest.IsIn("$PATH") )
                        {
                                CyString currentDir = CFileIO(filename).GetDir();
                                if ( !currentDir.Empty() )
                                        rest.FindReplace("$PATH", currentDir);
                        }

                        // now parse the rest of the values
                        if ( first.Compare("FtpUpload") )
                                ftpaddr = rest.GetToken(" ", 1, 1) + ":" + rest.GetToken(" ", 2, 2);
                        else if ( first.Compare("FtpUser") )
                                ftpuser = rest;
                        else if ( first.Compare("FtpPass") )
                                ftppass = rest;
                        else if ( first.Compare("FtpDir") )
                                ftpdir = rest;
                        else if ( !package->LoadPackageData(first.ToString(), rest.ToString()) )
                        {
                                if ( unknownCommands )
                                        unknownCommands->PushBack(first, rest);
                        }
                }
        }

        if ( package->GetFilename().Empty() )
                package->LoadPackageData("AutoSave", "$AUTOSAVE");

        if ( !ftpaddr.Empty() )
        {
                if ( !ftpuser.Empty() )
                {
                        if ( !ftppass.Empty() )
                                ftpaddr = ftpuser + ":" + ftppass + "@" + ftpaddr;
                        else
                                ftpaddr = ftpuser + "@" + ftpaddr;
                }

                if ( !ftpdir.Empty() )
                        ftpaddr += ftpdir;

                package->SetFtpAddr(ftpaddr);
        }

        return package;
}

CyString CPackages::GetLanguageName()
{
        return CPackages::ConvertLanguage(m_iLanguage);
}

CyString CPackages::ConvertLanguage(int lang)
{
        switch ( lang )
        {
                case 44:
                        return "English";
                case 49:
                        return "German";
                case 7:
                        return "Russian";
                case 33:
                        return "French";
                case 30:
                        return "Greek";
                case 31:
                        return "Dutch";
                case 32:
                        return "Belgian";
                case 34:
                        return "Spanish";
                case 36:
                        return "Hungarian";
                case 39:
                        return "Italian";
                case 40:
                        return "Romanian";
                case 41:
                        return "Swiss";
                case 42:
                        return "Czech";
                case 43:
                        return "Austrian";
                case 45:
                        return "Danish";
                case 46:
                        return "Swedish";
                case 47:
                        return "Norweigen";
                case 48:
                        return "Polish";
        }

        return CyString::Number(lang);
}

bool CPackages::CheckAccessRights(CyString dir)
{
        if ( dir.Empty() )
                dir = m_sCurrentDir;

        // write a file, then read the contents
        CFileIO File(dir + "/accessrightscheck.dat");

        // check if file exists and remove it
        if ( File.Exists() )
        {
                // if we cant remove it, we dont have enough rights
                if ( !File.Remove() )
                        return false;

                // if its still there, we dont have enough rights
                if ( File.Exists() )
                        return false;
        }

        // now create the file
        if ( !File.WriteString("testing access rights") )
                return false;

        // now check it exists
        if ( !File.Exists() )
                return false;

        // now read the file for the correct contents
        CyStringList *lines = File.ReadLinesStr();
        if ( !lines )
                return false;

        // check that one of the lines is correct
        for ( SStringList *s = lines->Head(); s; s = s->next )
        {
                if ( s->str == "testing access rights" )
                {
                        delete lines;
                        return true;
                }
        }

        delete lines;

        return false;
}

bool CPackages::LoadShipData(CyString file, CyStringList *list)
{
        CFileIO File;
        bool deleteFile = false;

        // load from cat file
        if ( CFileIO(file).CheckFileExtension("cat") )
        {
                CCatFile cat;
                if ( cat.Open(file, this->GetAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
                        return false;

                if ( !cat.ExtractFile("types\\TShips.pck", m_sTempDir + "/tships.txt") )
                        return false;

                File.Open(m_sTempDir + "/tships.txt");
                deleteFile = true;
        }
        // otherwise its a normal file
        else if ( CFileIO(file).CheckFileExtension("pck") )
        {
                C_File f(file);
                if ( !f.ReadFromFile() )
                        return false;
                f.UnPCKFile();

                f.SetFilename(m_sTempDir + "/tships.txt");
                if ( !f.WriteFilePointer() )
                        return false;

                File.Open(m_sTempDir + "/tships.txt");
                deleteFile = true;
        }
        else
                File.Open(file);

        if ( !File.Exists() )
                return false;

        bool ret = false;
        CyStringList *lines = File.ReadLinesStr();
        if ( lines )
        {
                bool readFirst = false;
                for ( SStringList *str = lines->Head(); str; str = str->next )
                {
                        if ( str->str.Empty() )
                                continue;
                        str->str.RemoveChar('\r');
                        str->str.RemoveChar(9);
                        str->str.RemoveFirstSpace();
                        if ( str->str.Empty() )
                                continue;
                        if ( str->str[0] == '/' || str->str[0] == '#' )
                                continue;

                        if ( !readFirst )
                                readFirst = true;
                        else
                        {
                                CyString t = str->str.GetToken(";", -2);
                                while ( t.Right(1) == ";" )
                                        t.Truncate((int)t.Length() - 1);
                                list->PushBack(t, str->str);
                        }
                }

                delete lines;
                ret = true;
        }

        if ( deleteFile )
                File.Remove();

        return ret;
}

CyString CPackages::ReadShipData(CyString file, CyString id)
{
        CyStringList *list = this->LoadShipData(file);
        if ( !list )
                return NullString;

        CShipData data;
        for ( SStringList *str = list->Head(); str; str = str->next )
        {
                if ( str->str.Compare(id) )
                {
                        delete list;
                        return str->data;
                }
        }

        delete list;
        return NullString;
}

CyStringList *CPackages::LoadShipData(CyString file)
{
        CyStringList *list = new CyStringList;
        if ( this->LoadShipData(file, list) )
                return list;

        delete list;
        return NULL;
}

bool CPackages::ReadTextPage(CyString file, CyStringList *list, bool search, int page)
{
        CFileIO File;
        bool deleteFile = false;

        // read all text files from mod
        if ( CFileIO(file).CheckFileExtension("cat") )
        {
                bool done = false;

                CCatFile cat;
                if ( cat.Open(file, this->GetAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
                        return false;

                // extract 1 at a time
                for ( int i = 0; i < cat.GetNumFiles(); i++ )
                {
                        SInCatFile *f = cat.GetFile(i);
                        CyString sF = f->sFile;
                        // is a text file
                        sF = sF.FindReplace("\\", "/");
                        if ( !sF.GetToken("/", 1, 1).Compare("t") )
                                continue;

                        CyString baseFile = CFileIO(sF).GetBaseName();
                        // check language
                        int lang = 0;
                        if ( baseFile.FindPos("-L") != -1 ) // new language file
                                lang = baseFile.Right(3).ToInt();
                        else
                        {
                                baseFile.Truncate((int)baseFile.Length() - 4);
                                lang = baseFile.ToInt();
                        }

                        if ( lang != m_iLanguage )
                                continue;

                        // now extract and parse
                        if ( cat.ExtractFile(f->sFile, m_sTempDir + "/" + CFileIO(f->sFile).GetBaseName() + ".xml") )
                        {
                                if ( this->ReadTextPage(m_sTempDir + "/" + CFileIO(f->sFile).GetBaseName() + ".xml", list, search, page) )
                                        done = true;
                        }
                }

                return done;
        }
        // otherwise its a normal file
        else if ( CFileIO(file).CheckFileExtension("pck") )
        {
                C_File f(file);
                if ( !f.ReadFromFile() )
                        return false;
                f.UnPCKFile();

                f.SetFilename(m_sTempDir + "/textfile.xml");
                if ( !f.WriteFilePointer() )
                        return false;

                File.Open(m_sTempDir + "/textfile.xml");
                deleteFile = true;
        }
        else
                File.Open(file);

        if ( !File.Exists() )
                return false;

        // open and read file
        CyStringList *lines = File.ReadLinesStr();
        if ( !lines )
                return false;

        bool inPage = false;
        for ( SStringList *str = lines->Head(); str; str = str->next )
        {
                // search for page
                if ( !inPage )
                {
                        if ( str->str.FindPos("<page") > -1 )
                        {
                                // find the page id
                                int pos = str->str.FindPos("\"");
                                if ( pos > -1 )
                                {
                                        int endpos = str->str.FindPos("\"", pos + 1);
                                        if ( endpos > -1 )
                                        {
                                                CyString p = str->str.Mid(pos + 2, endpos - pos - 1);
                                                if ( p.Length() > 4 )
                                                        p = p.Right(4);
                                                int checkPage = p.ToInt();
                                                if ( checkPage == page )
                                                        inPage = true;
                                        }
                                }
                        }

                }
                // add each id
                else
                {
                        if ( str->str.FindPos("</page") > -1 )
                                break;

                        if ( str->str.FindPos("<t id") > -1 )
                        {
                                int pos = str->str.FindPos("\"");
                                if ( pos > -1 )
                                {
                                        int endpos = str->str.FindPos("\"", pos + 1);
                                        if ( endpos > -1 )
                                        {
                                                int id = str->str.Mid(pos + 2, endpos - pos - 1).ToInt();
                                                pos = str->str.FindPos(">", endpos);
                                                if ( pos > -1 )
                                                {
                                                        ++pos;
                                                        endpos = str->str.FindPos("</", pos);
                                                        if ( endpos > -1 )
                                                        {
                                                                CyString text = str->str.Mid(pos + 1, endpos - pos);
                                                                while ( text.FindPos('(') != -1 && text.FindPos(')') != -1 )
                                                                {
                                                                        int s = text.FindPos('(');
                                                                        text = text.Erase(s, text.FindPos(')') - s + 1);
                                                                }
                                                                list->PushBack(CyString::Number(id), text, search);
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }

        delete lines;

        return true;
}

CyStringList *CPackages::ReadTextPage(CyString file, bool search, int page)
{
        CyStringList *list = new CyStringList;
        if ( this->ReadTextPage(file, list, search, page) )
                return list;

        delete list;
        return NULL;
}

int CPackages::AdjustFileType(CyString file, int filetype)
{
        CFileIO File(file);
        CyString dir = File.GetDirIO().TopDir();
        CyString basename = File.GetBaseName();

        // mod files
        if ( File.CheckFileExtension("cat") || File.CheckFileExtension("dat") )
                return FILETYPE_MOD;
        // check for text files
        if ( File.GetFilename().IsIn("-L") && File.GetFilename().Left(4).ToInt() )
                return FILETYPE_TEXT;
        if ( basename.Length() <= 4 && basename.IsNumber() && (File.CheckFileExtension("xml") || File.CheckFileExtension("pck")) )
                return FILETYPE_TEXT;
        // X2/X3 text file
        if ( basename.Length() >= 5 && basename.Length() <= 8 && File.GetBaseName().ToInt() )
                return FILETYPE_TEXT;
        if ( filetype == FILETYPE_TEXT ) // should no longer be anything text
                return FILETYPE_SCRIPT;
        if ( File.CheckFileExtension("wav") || File.CheckFileExtension("mp3") )
                return FILETYPE_SOUND;
        return filetype;
}

void CPackages::RemoveFailedFiles()
{
        for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
        {
                if ( CFileIO(str->str).Remove() )
                        str->remove = true;
        }

        m_lNonRemovedFiles.RemoveMarked();
}

CXspFile *CPackages::extractShip(const Utils::String &sCatFile, const Utils::String &sId, CProgressInfo *progress)
{
        CVirtualFileSystem *pVfs = new CVirtualFileSystem();
        if ( !pVfs->addMod(sCatFile) ) {
                delete pVfs;
                return NULL;
        }

        CXspFile *newShip = new CXspFile;
        if ( !newShip->extractShip(pVfs, sId, progress) ) {
                delete newShip;
                newShip = NULL;
        }

        delete pVfs;

        return newShip;
}

CyStringList *CPackages::GetMergedFiles(CCatFile *cat1, CCatFile *cat2)
{
        CyStringList *list = new CyStringList;

        // first add all files from the "primary" mod
        for ( SInCatFile *f = cat1->GetFiles()->First(); f; f = cat1->GetFiles()->Next() )
                list->PushBack(f->sFile.findreplace("\\", "/"), "1");

        // now add the ones from the secondary
        for ( SInCatFile *f = cat2->GetFiles()->First(); f; f = cat2->GetFiles()->Next() )
        {
                CyString sFile = f->sFile.findreplace("\\", "/");
                // if its found on the 2nd list, dont add, just adjust the type
                SStringList *found = list->FindString(sFile);
                if ( found )
                        found->data = "-1"; // found in both
                else
                        list->PushBack(sFile, "2");
        }

        return list;
}

bool CPackages::CanWeMerge(CyString file)
{
        return CModDiff::CanBeDiffed(file);
}

bool CPackages::NeedToMerge(CyString file)
{
        CyString firstDir = file.GetToken("/", 1, 1);
        if ( firstDir.Compare("t") )
                return true;
        if ( firstDir.Compare("types") )
                return true;
        if ( firstDir.Compare("maps") )
                return true;

        return false;
}

bool CPackages::MergeMods(CCatFile *mod1, CCatFile *mod2, CyString outFile, CyStringList *cantMerge)
{
        CyStringList *list = this->GetMergedFiles(mod1, mod2);
        if ( !list )
                return false;

        CCatFile newCat;
        if ( newCat.Open(outFile, this->GetAddonDir()) != CATERR_CREATED )
                return false;

        // add all the files to the new mod first
        CyStringList conflicts;
        for ( SStringList *str = list->Head(); str; str = str->next )
        {
                int status = str->data.ToInt();
                if ( status == 1 )
                {
                        if ( !newCat.WriteFromCat(mod1, str->str) )
                        {
                                if ( cantMerge )
                                        cantMerge->PushBack(str->str, "1");
                        }
                }
                else if ( status == 2 )
                {
                        if ( !newCat.WriteFromCat(mod2, str->str) )
                        {
                                if ( cantMerge )
                                        cantMerge->PushBack(str->str, "2");
                        }
                }
                else if ( status == -1 )
                {
                        if ( this->NeedToMerge(str->str) )
                        {
                                if ( this->CanWeMerge(str->str) )
                                        conflicts.PushBack(str->str);
                                else if ( cantMerge )
                                        cantMerge->PushBack(str->str, "-1");
                        }
                        else
                        {
                                if ( !newCat.WriteFromCat(mod1, str->str) )
                                {
                                        if ( cantMerge )
                                                cantMerge->PushBack(str->str, "1");
                                }
                        }
                }
        }

        delete list;

        /* 
                Merging Files

                * Text Files: Join all text entries into a single file (excluding page 17)
                * Weapons: TBullets and TLaser (grab matching entrys from text files and adjust ids)
        */
        // new merge the conflicting files
        // first the text files
//      CyStringList *text = this->MergeTextFiles(conflicts, mod1, mod2);
//      delete text;

        // write the cat file when we're done
        if ( !newCat.WriteCatFile() )
                return false;

        return true;
}

/**
 * Gets the file list from a mod that might have compatability problems
 *
 * This includes all types and text files
 *
 * Returns true if it finds any files
 */
bool CPackages::GetModCompatabilityList(C_File *file, CyStringList *list)
{
        // not a valid file
        if ( !file ) return false;
        if ( file->GetFileType() != FILETYPE_MOD ) return false;
        if ( !file->GetFileExt().Compare("cat") ) return false;

        // we need to read the file list for the mod
        CCatFile cat;
        if ( cat.Open(file->GetFilePointer(), this->GetAddonDir(), CATREAD_JUSTCONTENTS, false) == CATERR_NONE )
        {
                for ( int i = 0; i < cat.GetNumFiles(); i++ )
                {
                        SInCatFile *f = cat.GetFile(i);
                        CyString filename = f->sFile;
                        filename = filename.FindReplace("\\", "/");
                        if ( filename.Left(2).Compare("t/") || filename.Left(6).Compare("types/") )
                        {
                                if ( list )
                                        list->PushBack(filename, CyString(f->lSize));
                                else
                                        return true;
                        }
                }
        }

        if ( list && !list->Empty() )
                return true;

        return false;
}

/**
 * Gets the files that are not compatable with each other
 *
 * Returns true if theres any files that are not compatable
 *
 * If list is specified, fills up with all files that were found
 */
bool CPackages::CheckCompatabilityBetweenModFiles(C_File *from, C_File *to, CyStringList *list)
{
        // not a valid file
        if ( !from || !to ) return false;
        if ( from->GetFileType() != FILETYPE_MOD ) return false;
        if ( to->GetFileType() != FILETYPE_MOD ) return false;
        if ( !from->GetFileExt().Compare("cat") ) return false;
        if ( !to->GetFileExt().Compare("cat") ) return false;

        // get file lists from each file
        CyStringList fromList;
        if ( GetModCompatabilityList(from, &fromList) )
        {
                CyStringList toList;
                if ( GetModCompatabilityList(to, &toList) )
                {
                        // both have files we need to check, compare them
                        for ( SStringList *str = fromList.Head(); str; str = str->next )
                        {
                                CyString fromFile = str->str;
                                fromFile = fromFile.FindReplace("\\", "/");
                                fromFile = fromFile.FindReplace("//", "/");
                                for ( SStringList *toStr = toList.Head(); toStr; toStr = toStr->next )
                                {
                                        CyString toFile = toStr->str;
                                        toFile = toFile.FindReplace("\\", "/");
                                        toFile = toFile.FindReplace("//", "/");
                                        if ( fromFile.Compare(toFile) )
                                        {
                                                if ( list )
                                                        list->PushBack(from->GetFilename() + "::" + str->str, to->GetFilename() + "::" + toStr->str);
                                                else
                                                        return true;
                                        }
                                }
                        }
                }
        }

        if ( list && !list->Empty() )
                return true;

        return false;
}

bool CPackages::CheckCompatabilityBetweenMods(CBaseFile *from, CBaseFile *to, CyStringList *list)
{
        if ( !from || !to ) return false;
        if ( !from->IsEnabled() || !to->IsEnabled() ) return false;
        if ( !from->AnyFileType(FILETYPE_MOD) ) return false;
        if ( !to->AnyFileType(FILETYPE_MOD) ) return false;

        if ( from == to ) return false; // cant have incompatabilities to itself

        int count = 0;
        for ( C_File *f = from->GetFirstFile(FILETYPE_MOD); f; f = from->GetNextFile(f) )
        {
                if ( !f->IsFakePatch() ) continue;
                if ( f->GetFileExt().Compare("dat") ) continue;

                for ( C_File *compareFile = to->GetFirstFile(FILETYPE_MOD); compareFile; compareFile = to->GetNextFile(compareFile) )
                {
                        if ( compareFile == f ) continue; // same file we're checking against
                        if ( !compareFile->IsFakePatch() ) continue;
                        if ( compareFile->GetFileExt().Compare("dat") ) continue;

                        // now we have to files to compare
                        if ( CheckCompatabilityBetweenModFiles(f, compareFile, list) )
                                ++count;
                }
        }

        if ( count )
                return true;

        return false;
}

int CPackages::CheckCompatabilityAgainstPackages(CBaseFile *newFile, CyStringList *list, CLinkList<CBaseFile> *packages)
{
        if ( !newFile->IsEnabled() ) return 0;
        if ( !newFile->AnyFileType(FILETYPE_MOD) ) return 0;

        // we need to extract all mod files
        for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
        {
                C_File *f = fNode->Data();
                if ( f->GetFileType() != FILETYPE_MOD ) continue;
                if ( !f->IsFakePatch() ) continue;
                if ( !f->CheckFileExt("cat") ) continue;

                if ( newFile->ExtractFile(f, m_sTempDir) )
                        f->SetFullDir(m_sTempDir);
        }

        // compare mod files against all installed packages
        int count = 0;
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if ( !node->Data() ) continue;
                CBaseFile *p = node->Data();
                if ( !p->IsEnabled() ) continue;
                if ( !p->AnyFileType(FILETYPE_MOD) ) continue;

                if ( this->IsSamePackage(p, newFile) ) continue; // dont include self

                if ( CheckCompatabilityBetweenMods(newFile, p, list) )
                {
                        ++count;
                        if ( packages && !packages->FindData(p) )
                                packages->push_back(p);
                }
        }
        
        for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
        {
                C_File *f = fNode->Data();
                CFileIO(f->GetFilePointer()).Remove();
                f->SetFullDir("");
        }

        return count;
}

bool CPackages::IsSamePackage(CBaseFile *p1, CBaseFile *p2)
{
        if ( !p1 || !p2 ) return false;
        if ( p1 == p2 ) return true;

        if ( p1->GetName().Compare(p2->GetName()) && p1->GetAuthor().Compare(p2->GetAuthor()) )
                return true;
        return false;
}

void CPackages::ApplyFakePatchOrder(CyStringList *list)
{
        if ( !list ) return;
        m_lFakePatchOrder.Clear();
        for ( SStringList *str = list->Head(); str; str = str->next )
                m_lFakePatchOrder.PushBack(str->str, str->data);
}

SAvailablePackage *CPackages::CreateAvailablePackageData(CBaseFile *package)
{
        if ( !package ) return NULL;

        SAvailablePackage *p = new SAvailablePackage;

        for ( CListNode<SGameCompat> *node = package->GetGameCompatabilityList()->Front(); node; node = node->next() ) {
                SGameCompat *gc = new SGameCompat;
                gc->iGame = node->Data()->iGame;
                gc->iVersion = node->Data()->iVersion;
                gc->sVersion = node->Data()->sVersion;
                p->lGames.push_back(gc);
        }
        p->bSigned = package->IsSigned();
        p->iChanging = package->GetGameChanging();
        p->iEase = package->GetEaseOfUse();
        p->iPluginType = package->GetPluginType();
        p->iRec = package->GetRecommended();
        p->iScriptType = -1;
        if ( package->GetType() == TYPE_XSP )
                p->iType = PACKAGETYPE_SHIP;
        else if ( package->IsMod() )
                p->iType = PACKAGETYPE_MOD;
        else 
        {
                p->iType = ((CSpkFile *)package)->GetPackageType();
                p->iScriptType = ((CSpkFile *)package)->GetScriptType();
        }
        p->sAuthor = package->GetAuthor();
        p->sDesc = package->GetDescription().findreplace("\n", "::newline::");
        p->sName = package->GetName();
        p->sUpdated = package->GetCreationDate();
        p->sVersion = package->GetVersion();
        p->sFilename = CFileIO(package->GetFilename()).GetFilename();

        return p;
}

CyString CPackages::FormatAvailablePackageData(CBaseFile *package)
{
        SAvailablePackage *p = CPackages::CreateAvailablePackageData(package);
        CyString ret = CPackages::FormatAvailablePackageData(p);
        delete p;
        return ret;
}

CyString CPackages::FormatAvailablePackageData(SAvailablePackage *package)
{
        CyString ret = (long)package->iType;

        CyString gameCompat;
        for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
                if ( !gameCompat.Empty() )
                        gameCompat += "!";
                gameCompat += CyString::Number(node->Data()->iGame);
        }

        if ( gameCompat.Empty() )
                gameCompat = "0";

        ret.AddToken("::", gameCompat);
        ret.AddToken("::", package->sName);
        ret.AddToken("::", package->sAuthor);
        ret.AddToken("::", package->sVersion);
        ret.AddToken("::", package->sUpdated);
        ret.AddToken("::", package->sFilename);
        ret.AddToken("::", CyString::Number(package->iEase));
        ret.AddToken("::", CyString::Number(package->iChanging));
        ret.AddToken("::", CyString::Number(package->iRec));
        ret.AddToken("::", CyString::Number(package->iPluginType));
        ret.AddToken("::", CyString::Number(package->iScriptType));
        ret.AddToken("::", (package->bSigned) ? "1" : "0");
        ret.AddToken("::", package->sDesc);

        return ret;
}

void CPackages::ParseAvailablePackage(CyString str, CyString webaddress)
{
        // first check game
        int game = str.GetToken("::", 2, 2).ToInt();
        if ( game && m_iGame && m_iGame != game )
                return;

        int num = 0;
        CyString *tok = str.SplitToken("::", &num);
        if ( !num || !tok ) return;
        if ( num < 7 ) { CLEANSPLIT(tok, num); return; }

        SAvailablePackage *p = new SAvailablePackage;
        p->iType = tok[0].ToInt();
        p->bSigned = false;

        CyString gameCompat = tok[1];
        if ( gameCompat.IsIn("!") ) {
                int maxSplit = 0;
                CyString *games = gameCompat.SplitToken("!", &maxSplit);
                if ( games && maxSplit ) {
                        for ( int i = 0; i < maxSplit; i++ ) {
                                SGameCompat *gc = new SGameCompat;
                                gc->iVersion = 0;
                                gc->iGame = games[i].ToInt();
                                p->lGames.push_back(gc);
                        }
                }
                CLEANSPLIT(games, maxSplit);
        }
        else {
                SGameCompat *gc = new SGameCompat;
                gc->iVersion = 0;
                gc->iGame = gameCompat.ToInt();
                p->lGames.push_back(gc);
        }
        p->sName = tok[2];
        p->sAuthor = tok[3];
        p->sVersion = tok[4];
        p->sUpdated = tok[5];
        p->sFilename = tok[6];

        if ( !webaddress.Empty() )
                p->sFilename = webaddress + "/" + p->sFilename;

        p->iChanging = p->iEase = p->iPluginType = p->iRec = p->iScriptType = -1;

        if ( num >= 12 )
        {
                p->iEase = tok[7].ToInt();
                p->iChanging = tok[8].ToInt();
                p->iRec = tok[9].ToInt();
                p->iPluginType = tok[10].ToInt();
                p->iScriptType = tok[11].ToInt();
                if ( num > 12 ) {
                        if ( num > 13 ) {
                                p->sDesc = str.GetToken("::", 14); 
                                p->bSigned = str.GetToken("::", 13).ToBool();
                        }
                        else
                                p->sDesc = str.GetToken("::", 13); 
                }
        }
        else if ( num > 7 )
                p->sDesc = str.GetToken("::", 8); 

        if ( !p->sDesc.Empty() )
                p->sDesc.FindReplace("::newline::", "\\n");

        AddAvailablePackage(p);

        CLEANSPLIT(tok, num);
}

SAvailablePackage *CPackages::FindAvailablePackage(CyString &filename)
{
        for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
        {
                if ( node->Data()->sFilename.Compare(filename) )
                        return node->Data();
        }
        return NULL;
}

bool CPackages::AddAvailablePackage(SAvailablePackage *package) 
{ 
        if ( !package->lGames.empty() ) {
                bool found = false;
                for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
                        if ( !node->Data()->iGame || node->Data()->iGame == m_iGame ) {
                                found = true;
                                break;
                        }
                }

                if ( !found )
                        return false;
        }

        SAvailablePackage *p = FindAvailablePackage(package->sFilename);
        if ( p )
                m_lAvailablePackages.remove(p);
        m_lAvailablePackages.push_back(package); 

        return true;
}

bool CPackages::AnyAvailablePackages(int type)
{
        if ( type == -1 ) return !m_lAvailablePackages.empty();

        for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
        {
                if ( node->Data()->iType == type )
                        return true;
        }

        return false;
}

int CPackages::FindAllServers(CyStringList *list)
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if ( !node->Data()->GetWebAddress().Empty() )
                        list->PushBack(node->Data()->GetWebAddress(), true);
                if ( node->Data()->AnyWebMirrors() )
                {
                        for ( SStringList *str = node->Data()->GetWebMirrors()->Head(); str; str = str->next )
                                list->PushBack(str->str, true);
                }
        }

        return list->Count();
}

void CPackages::ReadArchiveData(CyString filename, CBaseFile *archive)
{
        size_t size;
        char *data = CFileIO(filename).ReadToData(&size);
        if ( size && data )
                ReadArchiveData(data, size, archive);
}

void CPackages::ReadArchiveData(const char *buf, size_t len, CBaseFile *archive)
{
        CyString data(buf);
        int max;
        CyString *str = data.SplitToken("\n", &max);
        if ( str && max )
        {
                for ( int i = 0; i < max; i++ )
                {
                        CyString line = str[i];
                        if ( line.Empty() )
                                continue;

                        // filter out any spaces, tabs in front
                        line.RemoveChar('\t');
                        line.RemoveChar('\r');
                        CyString linenospace = line;
                        linenospace.RemoveFirstSpace();
                        if ( linenospace.Empty() )
                                continue;

                        // check for any comments
                        if ( linenospace.Left(2) == "//" )
                                continue;
                        if ( linenospace[0] == '#' )
                                continue;

                        // all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
                        if ( !line.IsIn(':') )
                                continue;

                        CyString first = line.GetToken(":", 1, 1);
                        CyString rest = line.GetToken(":", 2).RemoveFirstSpace();

                        CyString checkType = first;
                        bool shared = false;
                        if ( checkType.Left(6).Compare("Shared") )
                        {
                                checkType = first.Right(-6);
                                shared = true;
                        }

                        // now check type name
                        int filetype = GetFileTypeFromString(checkType);
                        if ( filetype == -1 )
                                archive->LoadPackageData(first.ToString(), rest.ToString());
                }
        }

        CLEANSPLIT(str, max)
}

CBaseFile *CPackages::CreateFromArchive(CyString filename, bool toInstall )
{
        // make sure we can open the zip file
        CBaseFile *archive = NULL;
        if ( CFileIO(filename).CheckFileExtension("rar") )
        {
#ifdef _RAR
                HANDLE hArcData;
                int RHCode,PFCode;
                char CmtBuf[16384];
                struct RARHeaderDataEx HeaderData;
                struct RAROpenArchiveDataEx OpenArchiveData;

                if ( toInstall )
                {
                        memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
                        OpenArchiveData.ArcName=(char *)filename.c_str();
                        OpenArchiveData.CmtBuf=CmtBuf;
                        OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
                        OpenArchiveData.OpenMode=RAR_OM_LIST;
        //              OpenArchiveData.UserData=LIST;
                        hArcData=RAROpenArchiveEx(&OpenArchiveData);

                        if (OpenArchiveData.OpenResult!=0)
                                return NULL;

                        HeaderData.CmtBuf=NULL;
                        memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));

                        while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
                        {
                                if ( CyString(HeaderData.FileName).Compare("pluginmanager.txt") )
                                {
                                        toInstall = false;
                                        break;
                                }
                        }
                        RARCloseArchive(hArcData);
                }

                if ( toInstall )
                        archive = new CArchiveFile(); // just installing an archive file
                else
                        archive = new CSpkFile(); // converting to a spk file

                memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
                OpenArchiveData.ArcName=(char *)filename.c_str();
                OpenArchiveData.CmtBuf=CmtBuf;
                OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
                OpenArchiveData.OpenMode=RAR_OM_EXTRACT;
                OpenArchiveData.UserData=EXTRACT;
                hArcData=RAROpenArchiveEx(&OpenArchiveData);

                if (OpenArchiveData.OpenResult!=0)
                        return NULL;

                HeaderData.CmtBuf=NULL;
                memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));

                bool error = false;
                CyString extractedFile = CDirIO(m_sTempDir).File("extracted.tst").findreplace("/", "\\").findreplace("\\\\", "\\");
                while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
                {
                        CyString fileName = HeaderData.FileName;

                        if ( HeaderData.FileAttr == 16 )
                                continue;
                        wchar_t wText[200];
                        ::MultiByteToWideChar(CP_ACP, NULL, (char *)extractedFile.c_str(), -1, wText, extractedFile.Length() + 1);
                        PFCode=RARProcessFileW(hArcData, RAR_EXTRACT, NULL, NULL);
                        if (PFCode!=0)
                        {
                                error = true;
                                break;
                        }

                        CFileIO File(fileName);
                        if ( File.Exists() )
                        {
                                if ( fileName.Compare("pluginmanager.txt") )
                                        this->ReadArchiveData(File.GetFullFilename(), archive);
                                else
                                {
                                        CyString extradir;
                                        int type = SPK::GetAutomaticFiletype(fileName, &extradir, true);
                                        // check for special file types
                                        C_File *f = NULL;

                                        if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
                                                f = archive->AddFile(CFileIO(fileName).GetFilename(), CFileIO(fileName).GetDir(), FILETYPE_SCRIPT);
                                                if ( f ) {
                                                        f->ReadFromFile(File.GetFullFilename());
                                                }
                                                type = FILETYPE_UNINSTALL;
                                        }

                                        if ( type == -1 )
                                                f = archive->AddFile(CFileIO(fileName).GetFilename(), CFileIO(fileName).GetDir(), FILETYPE_EXTRA);
                                        else
                                                f = archive->AddFile(CFileIO(fileName).GetFilename(), extradir, type);
                                        f->ReadFromFile(File.GetFullFilename());
                                }

                                File.Remove();
                        }
                }

                RARCloseArchive(hArcData);

                if ( error )
                {
                        delete archive;
                        archive = NULL;
                }
#endif
        }
        else if ( CFileIO(filename).CheckFileExtension("zip") )
        {
                TCHAR buf[5000];
                wsprintf(buf, L"%hs", filename.c_str());
                HZIP hz = OpenZip(buf, 0);
                if ( !hz ) 
                        return NULL;

                int index;
                // move the files from the zip to the package
                ZIPENTRY ze;
                if ( FindZipItem(hz, L"pluginmanager.txt", true, &index, &ze) == Z_OK )
                        toInstall = false;
                CloseZip(hz);

                hz = OpenZip(buf, 0);
                if ( !hz ) 
                        return NULL;

                // create the correct package
                if ( toInstall )
                        archive = new CArchiveFile(); // just installing an archive file
                else
                        archive = new CSpkFile(); // converting to a spk file

                GetZipItem(hz, -1, &ze);
                int numitems = ze.index;

                bool error = false;
                for ( int zi = 0; zi < numitems; zi++ )
                {
                        ZIPENTRY ze;
                        if ( GetZipItem(hz, zi, &ze) != Z_OK )
                        {
                                error = true;
                                break;
                        }

                        if ( ze.attr & FILE_ATTRIBUTE_DIRECTORY )
                                continue; // dont do directories


                        char *iBuf = new char[ze.unc_size];
                        UnzipItem(hz, zi, iBuf, ze.unc_size);

                        CyString Name(ze.name);

                        // if its the data file, dont add it, but extract to get settings from
                        if ( Name.Compare("pluginmanager.txt") )
                        {
                                this->ReadArchiveData(iBuf, ze.unc_size, archive);
                                delete[] iBuf;
                        }
                        else
                        {
                                CyString extradir;
                                int type = SPK::GetAutomaticFiletype(Name, &extradir, true);

                                C_File *f = NULL;

                                // check for special file types
                                if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
                                        f = archive->AddFile(CFileIO(Name).GetFilename(), CFileIO(Name).GetDir(), FILETYPE_SCRIPT);
                                        if ( f ) {
                                                f->SetData((const unsigned char *)iBuf, ze.unc_size);
                                        }
                                        type = FILETYPE_UNINSTALL;
                                }

                                if ( type == -1 )
                                        f = archive->AddFile(CFileIO(Name).GetFilename(), CFileIO(Name).GetDir(), FILETYPE_EXTRA);
                                else
                                        f = archive->AddFile(CFileIO(Name).GetFilename(), extradir, type);

                                if ( f )
                                        f->SetData((const unsigned char *)iBuf, ze.unc_size);
                                else
                                        delete[] iBuf;
                        }
                }

                CloseZip(hz);

                if ( error )
                {
                        delete archive;
                        archive = NULL;
                }
        }

        if ( archive )
        {
                archive->SetFilename(CFileIO(filename).ChangeFileExtension("spk"));
                if ( toInstall )
                        archive->SetName(CFileIO(filename).GetFilename());
                else
                        archive->SetName(CFileIO(filename).GetBaseName());
        }

        return archive;
}

CyString CPackages::CreateFromPackagerScript(CyString filename)
{
        CyString curDir = CFileIO(filename).GetDir();
        CyStringList variables;
        variables.PushBack("$PATH", curDir);
        CPackages p;
        CBaseFile *package = p.LoadPackagerScript(filename, NULL, NULL, NULL, &variables);

        if ( !package )
                return NullString;

        CyString saveto = package->GetFilename();
        saveto = saveto.FindReplace("$DEFAULTDIR", curDir + "/");
        saveto = saveto.FindReplace("$PATH", curDir);
        saveto = saveto.FindReplace("\\", "/");
        saveto = saveto.FindReplace("//", "/");
        if ( !saveto.Right(4).Compare(".spk") && package->GetType() != TYPE_XSP )
                saveto += ".spk";
        else if ( !saveto.Right(4).Compare(".xsp") && package->GetType() == TYPE_XSP )
                saveto += ".xsp";

        // write script
        if ( package->WriteFile(saveto) )
        {
                if ( package->AutoGenerateUpdateFile() )
                        package->CreateUpdateFile(CFileIO(saveto).GetDir());
                return saveto;
        }

        return NullString;
}

int CPackages::GeneratePackageUpdateData(CyString dir, bool includeSingle)
{
        CyStringList filedata;

        CPackages packages;

        CDirIO Dir(dir);
        for ( int i = 0; i < 2; i++ )
        {
                CyString pattern;
                if ( i == 0 ) pattern = "*.spk";
                else if ( i == 1 ) pattern = ".xsp";
                else break;

                CyStringList *files = Dir.DirList("", pattern);
                if ( files )
                {
                        for ( SStringList *node = files->Head(); node; node = node->next )
                        {
                                int error = 0;
                                CBaseFile *p = packages.OpenPackage(Dir.File(node->str), &error, 0, SPKREAD_NODATA);
                                if ( !p )
                                        continue;

                                if ( includeSingle )
                                        p->CreateUpdateFile(dir);
                                filedata.PushBack(CPackages::FormatAvailablePackageData(p));
                                delete p;
                        }
                        delete files;
                }
        }

        if ( !filedata.Empty() )
        {
                CFileIO File(dir + "/xpackagedata.dat");
                if ( File.WriteFile(&filedata) )
                        return filedata.Count();
        }

        return 0;
}

int CPackages::VerifyInstalledFiles(CyStringList *missingFiles, bool getPackages)
{
        int count = 0;
        for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
        {
                C_File *f = fn->Data();
                bool exists = false;
                if ( f->GetFilePointer().IsIn("::") ) {
                        CyString modFile = f->GetFilePointer().GetToken("::", 1, 1);
                        CyString file = f->GetFilePointer().GetToken("::", 2, 2);

                        if ( CFileIO(modFile).Exists() ) {
                                CCatFile catFile;
                                if ( catFile.Open(modFile, "", CATREAD_CATDECRYPT, false) == CATERR_NONE ) {
                                        if ( catFile.FindData(file) )
                                                exists = true;
                                }
                        }
                }
                else {
                        exists = CFileIO(f->GetFilePointer()).Exists();
                }

                if ( !exists )
                {
                        ++count;
                        if ( missingFiles )
                        {
                                CyString packages;
                                if ( getPackages )
                                {
                                        for ( CListNode<CBaseFile> *p = m_lPackages.Front(); p; p = p->next() )
                                        {
                                                CBaseFile *package = p->Data();
                                                if ( package->IsFileAdded(f) )
                                                {
                                                        if ( !packages.Empty() )
                                                                packages += "\n";
                                                        packages += package->GetFullPackageName(m_iLanguage);
                                                }
                                        }
                                }
                                CyString filename = f->GetFilePointer();
                                filename = filename.Remove(m_sCurrentDir);
                                missingFiles->PushBack(filename, packages, false);
                        }
                }
        }
        return count;
}