Subversion Repositories spk

Rev

Rev 226 | Rev 233 | 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>

#include "OriginalFiles.h"
#include "spkdef.h"
#include <set>

#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 {
        Utils::WString          sSection;
        Utils::WStringList      lEntries;
} SDummyEntry;

typedef struct SComponantEntry2 {
        Utils::WString          sSection;
        Utils::WStringList      lEntries;
} SComponantEntry2;

typedef struct SComponantEntry {
        Utils::WString          sSection;
        CLinkList<SComponantEntry2>     lEntries;
} SComponantEntry;

Utils::WString CPackages::m_sTempDir;

//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////

CPackages::CPackages() : m_pCurrentGameExe(NULL),
        _pOriginalFiles(NULL),
        _pCurrentDir(NULL)      
{
        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::setCurrentDir(const Utils::WString &dir)
{ 
        m_sCurrentDir = dir;
        if (_pCurrentDir) delete _pCurrentDir;
        _pCurrentDir = new GameDirectory();
        _pCurrentDir->dir = dir;
        _pCurrentDir->id = -1;

        m_gameExe.getDirectoryData(_pCurrentDir);
        _pCurrentDir->langid = this->getGameLanguage(_pCurrentDir->dir);
        if (_pCurrentDir->langid > 0)
                _pCurrentDir->langname = this->ConvertLanguage(_pCurrentDir->langid);

        updateFoundPackages(L"");
}


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::setMod(const Utils::WString &mod)
{
        if (mod != L"PluginManager")
                m_sSetMod = mod;
}

void CPackages::setSaveGameManager(bool managed)
{
        m_iSaveGameManager = managed ? 1 : 0;
        
        if (managed)
        {
                // find new save directory
                CDirIO dir(this->saveDirectory());
                if (dir.exists())
                {
                        int id = 1;
                        while (dir.exists(Utils::WString::PadNumber(id, 4)))
                                ++id;
                        Utils::WString d = Utils::WString::PadNumber(id, 4);
                        dir.create(d);
                        _sSaveDir = d;

                        CDirIO destDir1(dir.dir(d));
                        destDir1.create(L"Vanilla");
                        destDir1.cd(L"Vanilla");
                        CDirIO destDir2(dir.dir(d));
                        destDir2.create(L"Modified");
                        destDir2.cd(L"Modified");

                        Utils::WStringList files;
                        if (dir.dirList(files, Utils::WString::Null(), L"*.sav"))
                        {
                                for (auto itr = files.begin(); itr != files.end(); ++itr)
                                {
                                        Utils::WString f = dir.file((*itr)->str);
                                        CFileIO(f).copy(destDir1.file((*itr)->str));
                                        CFileIO(f).copy(destDir2.file((*itr)->str));
                                }
                        }
                }
        }
        else
        {
                _sSaveDir.clear();
        }
}


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();

        SafeDelete(_pOriginalFiles);

        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 = L"";

        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_lFoundPackages.MemoryClear();
        m_lInstallList.MemoryClear();
        m_lEnableList.MemoryClear();
        m_lDisableList.MemoryClear();

        _lCreatedFiles.clear();
        _lNonRemovedFiles.clear();
        _lGlobals.clear();
        _lFakePatchOrder.clear();
        if (_pCurrentDir)
                delete _pCurrentDir;
        _pCurrentDir = NULL;
}

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

        m_pGameVFS.setAddon(this->getAddonDir());
        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(L"cat")) break;
                }

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

bool CPackages::isOldDir(const Utils::WString &dir)
{
        bool oldPlugin = false;

        CFileIO datFile(dir + L"/PluginManager/pluginmanager.dat");
        if ( datFile.exists() )
        {
                std::vector<Utils::WString> readFile;
                if(datFile.readLines(readFile))
                {
                        for ( int i = 0; i < (int)readFile.size(); i++ )
                        {
                                Utils::WString line(readFile.at(i));
                                Utils::WString cmd = line.token(L":", 1);
                                if ( cmd.Compare(L"<script>") || cmd.Compare(L"</scripts>") )
                                        break;
                                else if ( cmd.Compare(L"spkinstaller") || cmd.Compare(L"globalfiles") )
                                        break;
                                else if ( cmd.Compare(L"pluginmanager") )
                                {
                                        oldPlugin = true;
                                        break;
                                }
                        }
                }
        }

        return oldPlugin;
}

bool CPackages::read(const Utils::WString& dir, CProgressInfo* progress)
{
        m_sCurrentDir = dir;
        m_sCurrentDir = m_sCurrentDir.findReplace(L"\\", L"/");
        this->setCurrentDir(dir);
        m_bOldPlugin = false;
        _lCreatedFiles.clear();
        m_bRemoveDir = false;
        m_bSurpressProtectedWarning = false;
        m_iSaveGame = -1;
        m_iSaveGameManager = -1;
        _lNonRemovedFiles.clear();
        _lGlobals.clear();
        _lFakePatchOrder.clear();

        if ( _pOriginalFiles ) delete _pOriginalFiles;
        _pOriginalFiles = new COriginalFiles(m_sCurrentDir);

        m_bVanilla = true;

        // check the pluginmanager data file exists
        CFileIO datFile(m_sCurrentDir + L"/PluginManager/pluginmanager.dat");
        if ( datFile.exists() )
        {
                std::vector<Utils::WString> readFile;
                if(datFile.readLines(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++ )
                        {
                                Utils::WString line(readFile.at(i));

                                Utils::WString cmd = line.token(L":", 1).lower();
                                Utils::WString rest = line.tokens(L":", 2).removeFirstSpace();

                                if ( iStatus == READ_GLOBAL )
                                {
                                        if ( cmd == L"<script>" )
                                        {
                                                packageFile = new CSpkFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"<ship>" )
                                        {
                                                packageFile = new CXspFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"<base>" )
                                        {
                                                packageFile = new CBaseFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"<archive>" )
                                        {
                                                packageFile = new CArchiveFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd != L"</scripts>" )
                                        {
                                                int game = 0;
                                                bool disabled = false;
                                                Utils::WString fileName = line.tokens(L":", 5);
                                                Utils::WString origName;
                                                if ( fileName.left(2) == L"D#" )
                                                {
                                                        fileName.erase(0, 2);
                                                        disabled = true;
                                                }
                                                if ( fileName.left(2) == L"G#" ) {
                                                        fileName.erase(0, 2);
                                                        game = fileName.token(L"#", 1).toLong();
                                                        fileName = fileName.tokens(L"#", 2);
                                                }
                                                if ( fileName.isin(L"O#") )
                                                {
                                                        origName = fileName.token(L"O#", 2);
                                                        fileName = fileName.token(L"O#", 1);
                                                }

                                                C_File *newFile = new C_File(m_sCurrentDir + fileName);
                                                newFile->setGame(game);
                                                newFile->SetOriginalName(origName);
                                                newFile->SetDisabled(disabled);
                                                newFile->setFileType(static_cast<FileType>(line.token(L":", 1).toLong()));
                                                newFile->SetCreationTime(static_cast<time_t>(line.token(L":", 2).toLong()));
                                                newFile->setDir(line.token(L":", 3));
                                                if ( line.token(L":", 4).toLong() )
                                                        newFile->SetShared(true);
                                                newFile->UpdateSigned();

                                                m_lFiles.push_back(newFile);
                                        }
                                }
                                else if ( iStatus == READ_SCRIPT )
                                {
                                        if ( cmd == L"installspk" )
                                                packageFile->setFilename(rest);
                                        else if ( cmd == L"files" )
                                        {
                                                iStatus = READ_SCRIPTFILE;
                                                if ( spkinstaller )
                                                {
                                                        std::vector<Utils::WString> files;
                                                        rest.tokenise(L" ", files);
                                                        for (size_t i = 0; i < files.size(); i++ )
                                                        {
                                                                int fileNum = files[i].toLong();
                                                                if ( fileNum >= 0 && fileNum < m_lFiles.size() )
                                                                        packageFile->AddFile(m_lFiles[fileNum]);
                                                        }
                                                }
                                        }
                                        else if ( cmd == L"disabled" )
                                                packageFile->SetEnabled(false);
                                        else if ( cmd == L"modifieddisabled" )
                                                packageFile->SetModifiedEnabled(false);
                                        else if ( cmd == L"icon" )
                                        {
                                                C_File *icon = new C_File(rest.tokens(L" ", 2));
                                                packageFile->setIcon(icon, rest.token(L" ", 1));
                                        }
                                        else
                                                packageFile->parseValueLine(line);
                                }
                                else if ( iStatus == READ_SCRIPTFILE )
                                {
                                        if ( cmd == L"<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 == L"<ship>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CXspFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"<base>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CBaseFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"<archive>" )
                                        {
                                                m_lPackages.push_back(packageFile);
                                                this->ConvertOldPackage(packageFile);
                                                packageFile = new CArchiveFile();
                                                iStatus = READ_SCRIPT;
                                        }
                                        else if ( cmd == L"</scripts>" )
                                        {
                                                if ( packageFile->IsMod() && packageFile->IsEnabled() )
                                                        m_pEnabledMod = packageFile;

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

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

                                        SGameShip *gm = new SGameShip;
                                        gm->iType = line.token(L" ", 1).toLong();
                                        if ( line.token(L" ", 2).left(4) == L"$#C:" )
                                        {
                                                gm->sShipClass = line.token(L" ", 2).right(-4);
                                                gm->sShipID = line.tokens(L" ", 3);
                                        }
                                        else
                                        {
                                                gm->sShipID = line.tokens(L" ", 2);
                                                gm->sShipClass = L"OBJ_SHIP_M5";
                                        }
                                        gm->pPackage = NULL;
                                        m_lGameShips.push_back(gm);
                                        if ( iCount <= 0 )
                                                iShip = -1;
                                }
                                else if ( cmd.Compare(L"savegamemanager") )
                                        m_iSaveGameManager = rest.toLong();
                                else if ( cmd.Compare(L"savegame") )
                                        m_iSaveGame = rest.toLong();
                                else if ( cmd == L"fakepatch" )
                                        m_iFakePatch = rest.toLong();
                                else if ( cmd == L"updatetime" )
                                        m_iLastUpdated = rest.toLong();
                                else if ( cmd == L"surpressprotectedwarning" )
                                        m_bSurpressProtectedWarning = true;
                                else if ( cmd == L"pluginmanager" )
                                {
                                        fVersion = rest.toFloat();
                                        m_bOldPlugin = true;
                                }
                                else if ( cmd == L"setmod" )
                                        m_sSetMod = rest;
                                else if ( cmd == L"shipbuffer" )
                                        m_iShipBuffer = rest.toLong();
                                else if(cmd == L"savedir")
                                        _sSaveDir = rest;
                                else if ( cmd == L"warebuffers" )
                                {
                                        int max = rest.countToken(L" ");
                                        for ( int i = 0; i < WAREBUFFERS; i++ )
                                        {
                                                if ( i > max )
                                                        m_iWareBuffer[i] = 0;
                                                else
                                                        m_iWareBuffer[i] = rest.token(L" ", i + 1).toLong();
                                        }
                                }
                                else if ( cmd == L"createdfile" )
                                        _lCreatedFiles.pushBack(rest);
                                else if ( cmd == L"wares" )
                                {
                                        iWare = rest.token(L" ", 1).toLong();
                                        iCount = rest.token(L" ", 2).toLong();
                                }
                                else if ( cmd == L"ships" )
                                {
                                        iShip = 1;
                                        iCount = rest.token(L" ", 1).toLong();
                                }
                                else if ( cmd == L"modified" )
                                        m_bVanilla = false;
                                else if ( cmd == L"spkinstaller" )
                                {
                                        fVersion = rest.toFloat();
                                        spkinstaller = true;
                                }
                                else if ( cmd == L"betaversion" )
                                        fBeta = rest.toFloat();
                                else if ( cmd == L"uninstall" )
                                {
                                        C_File *uf = new C_File();
                                        uf->setFileType(FILETYPE_SCRIPT);
                                        uf->SetCreationTime(rest.token(L" ", 1).toLong());
                                        uf->setFilename(m_sCurrentDir + L"/Scripts/" + rest.tokens(L" ", 2));
                                        m_lUninstallFiles.push_back(uf);
                                }
                                else if ( cmd == L"nonremovedfile" )
                                {
                                        if ( !CFileIO::Remove(rest) )
                                                _lNonRemovedFiles.pushBack(rest);
                                }
                                else if ( cmd == L"original" )
                                        _pOriginalFiles->parse(rest);
                                else if ( cmd.Compare(L"GlobalSetting") )
                                        _lGlobals.pushBack(rest.token(L":", 1), rest.tokens(L":", 2));
                                else if ( cmd.Compare(L"FakePatchOrder") )
                                        _lFakePatchOrder.pushBack(rest.token(L":", 1), rest.tokens(L":", 2));
                                else if ( cmd.Compare(L"EMPPriceOverride") )
                                        this->addEMPPriceOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
                                else if ( cmd.Compare(L"EMPNotoOverride") )
                                        this->addEMPNotoOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
                                else if ( cmd.Compare(L"BuiltInWarePriceOverride") )
                                        this->addBuiltInWarePriceOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
                                else if ( cmd.Compare(L"BuiltInWareNotoOverride") )
                                        this->addBuiltInWareNotoOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
                                else if ( cmd.Compare(L"CustomWarePriceOverride") )
                                        this->addCustomWarePriceOverride(rest.token(L";", 1), rest.token(L";", 2).toLong());
                                else if ( cmd.Compare(L"CustomWareNotoOverride") )
                                        this->addCustomWareNotoOverride(rest.token(L";", 1), rest.token(L";", 2).toLong());
                                else if ( cmd == L"globalfiles" )
                                        iStatus = READ_GLOBAL;

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

                        if ( !spkinstaller )
                                m_bRedo = true;
                }
        }

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

        m_iGame = m_gameExe.getGameType(m_sCurrentDir) + 1;
        m_pCurrentGameExe = m_gameExe.game(m_gameExe.getGameType(m_sCurrentDir));
        Utils::WString sGameVersion;
        m_iGameVersion = m_gameExe.getGameVersion(m_sCurrentDir, &sGameVersion) + 1;
        _sGameVersion = sGameVersion;
        m_iGameFlags = m_gameExe.gameFlags(m_iGame - 1);
        m_iMaxPatch = m_gameExe.maxPatch(m_iGame - 1);

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

        this->RemoveCreatedFiles();
        this->createPluginManagerOpenText();

        // 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 )
                                        {
                                                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->shipID().Compare(gNode->Data()->sShipID) )
                                {
                                        gNode->Data()->pPackage = p;
                                        break;
                                }
                        }
                }
        }

        readAvailablePackages();

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

        m_bLoaded = true;

        return true;
}

void CPackages::RemoveCreatedFiles()
{
        for(auto itr = _lCreatedFiles.begin(); itr != _lCreatedFiles.end(); itr++)
        {
                if ( CFileIO::Exists((*itr)->str) )
                        CFileIO::Remove((*itr)->str);
                else if ( CFileIO::Exists(m_sCurrentDir + L"/" + (*itr)->str) )
                        CFileIO::Remove(m_sCurrentDir + L"/" + (*itr)->str);
        }

        _lCreatedFiles.clear();
}

void CPackages::PurgeGameObjects()
{
        // check for the log file
        Utils::WString logDir = logDirectory();
        CFileIO LogFile(logDir + L"/log0" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L".txt");
        if ( LogFile.exists() )
        {
                // read the log file to memory
                std::vector<Utils::WString> lines;
                if(LogFile.readLines(lines))
                {
                        for ( int i = 0; i < (int)lines.size(); i++ )
                        {
                                Utils::WString line(lines.at(i));
                                Utils::WString start = line.token(L":", 1).toLower();
                                Utils::WString rest = line.token(L":", 2).removeFirstSpace();
                                if ( start.Compare(L"purged") )
                                {
                                        long time = rest.toLong();
                                        if ( time == m_iLastUpdated )
                                        {
                                                this->PurgeWares();
                                                this->PurgeShips();
                                                this->removeUninstallScripts();
                                        }
                                }
                        }
                }
                // 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();
}

Utils::WString CPackages::logDirectory()
{
        Utils::WString logDir = m_sCurrentDir;
        if ( m_iGameFlags & EXEFLAG_MYDOCLOG ) {
                SGameExe *exe = m_gameExe.game(m_iGame - 1);
                if ( exe ) {
                        if ( !exe->sMyDoc.empty() )
                                logDir = m_sMyDoc + L"/" + exe->sMyDoc;
                }
        }

        return logDir;
}

Utils::WString CPackages::logDirectory(const Utils::WString &gameExe)
{
        Utils::WString logDir = m_sCurrentDir;
        if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
        {
                SGameExe *exe = m_gameExe.gameExe(CFileIO(gameExe).filename());
                if ( exe )
                {
                        if ( !exe->sMyDoc.empty() )
                                logDir = m_sMyDoc + L"/" + exe->sMyDoc;
                }
        }

        return CFileIO(logDir).fullFilename();
}

Utils::WString CPackages::saveDirectory()
{
        Utils::WString logDir = this->logDirectory();
        if ( m_iGameFlags & EXEFLAG_NOSAVESUBDIR )
                return logDir;

        return logDir + L"/save";
}

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

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

        // fake patch is not being used
        if ( m_iFakePatch < 0 ) return true;
        // now lets find the fake patch
        Utils::WString useFile;
        if ( m_iFakePatch > 0 )
        {
                Utils::WString file = Utils::WString::PadNumber(m_iFakePatch, 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 )
                {
                        Utils::WString file = Utils::WString::PadNumber(nextfree, 2);
                        if (checkValidPluginManagerFile(file))
                        {
                                useFile = file;
                                break;
                        }
                        --nextfree;
                }
        }

        Utils::WString 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 )
                {
                        Utils::WString file = Utils::WString::PadNumber(nextfree, 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 + L"/" + useFile + L".cat"), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE)
                {
                        std::vector<SInCatFile *> *files = openCat.GetFiles();
                        bool found = false;
                        if ( files )
                        {
                                Utils::WString useAddonDir = addonDir;
                                if ( !useAddonDir.empty() ) useAddonDir += L"\\";
                                for (auto itr = files->cbegin(); itr != files->cend(); itr++)
                                {
                                        if ((*itr)->sFile.Compare(L"PlugMan\\TFake.pck") )
                                                continue;
                                        if ((*itr)->sFile.Compare(useAddonDir + L"t\\44" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L".pck"))
                                                continue;
                                        if ((*itr)->sFile.Compare(useAddonDir + L"t\\" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L"-L044.pck"))
                                                continue;

                                        found = true;
                                        break;
                                }
                        }

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

                                        if ( datFile.Rename(m_sCurrentDir + L"/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;
        }
        // no files found, but we have a fake patch ? restore from backup
        else if ( m_iFakePatch > 0 ) {
                CLog::log(CLog::Log_Directory, 1, L"PlugMan FakePatch seems to be missing (" + Utils::WString::Number(m_iFakePatch) + L"), Restoring from backup");
                CFileIO catFile(m_sCurrentDir + L"/PluginManager/Backup/PlugMan_Fake.cat");
                if ( catFile.exists() ) {
                        catFile.copy(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat");
                        CFileIO datFile(m_sCurrentDir + L"/PluginManager/Backup/PlugMan_Fake.dat");
                        if ( datFile.exists() ) datFile.copy(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.dat");
                }
        }

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


bool CPackages::checkIfPluginManagerFile(const Utils::WString &filename) const
{
        bool found = false;

        CFileIO catFile(m_sCurrentDir + L"/" + filename + L".cat");
        if ( catFile.exists() && CFileIO::Exists(m_sCurrentDir + L"/" + filename + L".dat"))
        {
                CCatFile openFile;
                if ( openFile.open(catFile.fullFilename(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
                {
//                      if ( openFile.internaldatFilename().Compare("PlugMan_Fake.dat") ) return true;
                        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()->fullDir().contains(L"::") ) {
                                        if (CFileIO(node->Data()->fullDir().token(L"::", 1)).filename().Compare(L"PlugMan_Fake.cat") ) {
                                                Utils::WString filename = node->Data()->filePointer().token(L"::", 2);
                                                filename = filename.findReplace(L"/", L"\\");
                                                if ( openFile.findData(filename) )
                                                        ++count;
                                                else
                                                        ++noncount;
                                        }
                                }
                        }

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

        return found;
}

bool CPackages::checkValidPluginManagerFile(const Utils::WString &filename) const
{
        // both the cat file and dat file exists, lets open it
        CFileIO catFile(m_sCurrentDir + L"/" + filename + L".cat");
        if ( catFile.exists() && CFileIO::Exists(m_sCurrentDir + L"/" + filename + L".dat"))
        {
                CCatFile openFile;
                if ( openFile.open(catFile.fullFilename(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
                {
                        if ( openFile.findData(L"PlugMan\\TFake.pck") )
                                return true;
                        if ( openFile.findData(L"pluginmanagerfake.pck") )
                                return true;
                }
        }

        return false;
}

/**
 * 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->forumLink().empty() && package->webSite().contains(L"forum.egosoft")) {
                package->setForumLink(package->webSite());
                package->setWebSite(L"");
        }

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

                        if ( spk->GetScriptType() != CSpkFile::SCRIPTTYPE_CUSTOM )
                                spk->setScriptType(L"");
                }
        }
        else if ( package->GetType() == TYPE_XSP )
        {
                CXspFile *xsp = (CXspFile *)package;
                Utils::WString data = xsp->shipData();

                for ( int i = 17; i <= 18; i++ )
                {
                        Utils::WString model = data.token(L";", i);
                        Utils::WString modelExt = model.right(4);
                        // check file extension
                        if ( modelExt.Compare(L".bod") || modelExt.Compare(L".pbd") )
                                data = data.replaceToken(L";", 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->dir().Compare(L"director") )
                                continue;
                        if (f->checkFileExt(L"xml") || f->checkFileExt(L"pck"))
                        {
                                f->setDir(L"");
                                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->otherName(), package->otherAuthor()));
}

/**
 * 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 )
        {
                _pOriginalFiles->update(m_bRedo, &m_lFiles);
        }

        // 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 )
        {
        }

        m_bRedo = false;
        return true;
}

int CPackages::checkOpenPackage(const Utils::WString &file, int *error)
{
        // first check if it exists
        if (!CFileIO::Exists(file))
        {
                *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(const Utils::WString &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(const Utils::WString &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(const Utils::WString &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(const Utils::WString &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).isFileExtension(L"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, Utils::WStringList *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->filename().Compare(checkFile->filename()) )
                        {
                                if (removeUninstallFile(uf, errors) )
                                        fNode->DeleteData();
                                break;
                        }
                }
        }

        m_lUninstallFiles.RemoveEmpty();

        this->WriteData();
}

int CPackages::installPreparedPackages(Utils::WStringList *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;
}

void CPackages::_addToFakePatch(CBaseFile *pPackage)
{
        CCatFile cat;
        if ( CCatFile::Opened(cat.open((m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat"), this->getAddonDir(), CATREAD_DAT))) {
                for ( CListNode<C_File> *f = pPackage->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(pPackage)) ) {
                                continue;
                        }
                        // check if its already in the fake patch
                        if ( f->Data()->fullDir().contains(L"::") ) {
                                continue;
                        }
                        Utils::WString toFile;
                        if ( cat.appendFile(f->Data()->filePointer(), f->Data()->getNameDirectory(pPackage), true, (m_iGameFlags & EXEFLAG_NOXOR) ? false : true, &toFile) ) {
                                CLog::logf(CLog::Log_Install, 2, L"Adding file: %s into the fake patch", f->Data()->getNameDirectory(pPackage).c_str());
                                CFileIO::Remove(f->Data()->filePointer());
                                f->Data()->setFilename(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat::" + toFile);
                        }
                }
        }
}

bool CPackages::_checkForDisable(CBaseFile *package, bool disabled, CBaseFile *oldPackage)
{
        bool prevDisabled = disabled;

        // first check if we are installed a mod
        if ( package->IsMod() && m_pEnabledMod && !m_bForceModInstall ) {
                disabled = true;
                CLog::log(CLog::Log_Install, 2, L"Package is a mod and another mod is already enabled, setting to disabled");
        }
        // if vanilla nad package aint signed
        if ( m_bVanilla && !package->IsSigned() ) {
                disabled = true;
                CLog::log(CLog::Log_Install, 2, L"Package is a not signed and are we in vanilla mode, setting to disabled");
        }

        // check any depancies
        if ( !disabled && !this->CheckEnabledDependacy(package) ) {
                disabled = true;
                CLog::log(CLog::Log_Install, 2, L"Dependacies missing for package, setting to disabled");
        }

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

        return disabled;
}

Utils::WString CPackages::getCurrentDirectory() const
{ 
        return (_pCurrentDir) ? _pCurrentDir->dir : Utils::WString::Null();
}


bool CPackages::installPackage(CBaseFile *package, Utils::WStringList *errors, CProgressInfo *progress, bool disabled)
{
        CLog::logf(CLog::Log_Install, 1, L"Starting to install new package, %s by %s (Version: %s)", package->name().c_str(), package->author().c_str(), package->version().c_str());
        
        CBaseFile *oldPackage = findPackage(package);
        disabled = _checkForDisable(package, disabled, oldPackage);

        // update packages must have an old package installed already (should have been checked for already)
        if ( package->GetType() == TYPE_SPK )
        {
                if ( ((CSpkFile *)package)->IsPackageUpdate() )
                {
                        CLog::log(CLog::Log_Install, 3, L"Package is an Update, checking for existing package installed");
                        if ( !oldPackage ) {
                                CLog::log(CLog::Log_Install, 2, L"Package is an Update but no existing package found, cancelling install");
                                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(L"temp");
                                if ( f->Data()->IsFakePatch() ) {
                                        CLog::logf(CLog::Log_Install, 2, L"Moving fake package to temporary location to preper for update, %s", f->Data()->filePointer().c_str());
                                        f->Data()->setName(L"Fake_" + f->Data()->name());
                                }
                        }
                }
        }

        // no need to backup if we're disabling them
        if ( !disabled )
        {
                // find any uninstall files and remove them
                CLog::log(CLog::Log_Install, 3, L"Purging uninstall scripts");
                this->purgeUninstallScripts(package, errors);

                // backup any original files before installing
                _pOriginalFiles->backup(package, errors);
        }

        // install all the files
        CLog::log(CLog::Log_Install, 3, L"Checking for any existing files");
        if ( oldPackage )
        {
                CLog::logf(CLog::Log_Install, 3, L"Excluding existing package (%s) from file check list", oldPackage->version().c_str());
                CLinkList<CBaseFile> excludeList;
                excludeList.push_back(oldPackage);
                this->UpdateUsedFiles(&excludeList, true);
        }
        else
                this->UpdateUsedFiles(0, true);

        CLog::log(CLog::Log_Install, 3, L"Reading all files into memory");
        package->ReadAllFilesToMemory();

        CLog::log(CLog::Log_Install, 3, L"Starting to install files");
        if ( !package->installFiles (m_sCurrentDir, progress, &m_lFiles, errors, !disabled, this))
        {
                CLog::log(CLog::Log_Install, 2, L"There was an error installing files!!");

                // TODO: clear up installed files
                return false;
        }

        _pOriginalFiles->installed(package);

        // if we're installing an addon, lets use the fake patch method for object files
        if ( m_iGameFlags & EXEFLAG_ADDON ) this->_addToFakePatch(package);

        bool shuffle = package->anyFakePatchOrder();

        // 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(L"cat"))
                                        continue;

                                // if fake patch, find first fake patch in package
                                C_File *findMatching = NULL;
                                if ( f->Data()->baseName().left(5).Compare(L"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(L"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(L"cat"))
                                                        continue;

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

                                if ( findMatching )
                                {
                                        // copy accross all mods
                                        CCatFile catTo, catFrom;
                                        if ( catFrom.open(f->Data()->filePointer(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
                                        {
                                                if ( catTo.open(findMatching->filePointer(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
                                                {
                                                        for (unsigned int i = 0; i < catFrom.GetNumFiles(); i++ )
                                                        {
                                                                SInCatFile *c = catFrom.GetFile(i);                                                              
                                                                catTo.appendFile(f->Data()->filePointer() + L"::" + 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()->baseName().left(5).Compare(L"fake_") )
                                        {
                                                shuffle = true;
                                                C_File *match = package->findMatchingMod(f->Data());
                                                Utils::WString next = Utils::WString::PadNumber(this->findNextFakePatch(), 2);
                                                
                                                Utils::WString oldFilePointer = f->Data()->filePointer();
                                                f->Data()->setDir(L"");
                                                f->Data()->changeBaseName(next);
                                                if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package)))
                                                        f->Data()->setFilename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package));

                                                if ( match )
                                                {
                                                        Utils::WString oldFilePointer = match->filePointer();
                                                        match->setDir(L"");
                                                        match->changeBaseName(next);
                                                        if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + L"/" + match->getNameDirectory(package)))
                                                                match->setFilename(m_sCurrentDir + L"/" + match->getNameDirectory(package));
                                                }
                                        }
                                        else
                                        {
                                                C_File *match = package->findMatchingMod(f->Data());

                                                f->Data()->setDir(L"");
                                                if ( CFileIO(f->Data()->filePointer()).Rename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package)))
                                                        f->Data()->setFilename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package));
                                                if ( match )
                                                {
                                                        match->setDir(L"");
                                                        if ( CFileIO(match->filePointer()).Rename(m_sCurrentDir + L"/" + match->getNameDirectory(package)))
                                                                match->setFilename(m_sCurrentDir + L"/" + match->getNameDirectory(package));
                                                }
                                        }
                                }
                        }

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

                        CDirIO(m_sCurrentDir).removeDir(L"temp");
                        CDirIO(m_sCurrentDir).removeDir(L"Mods/temp");
                }
        }

        // if theres an icon, write it
        if (package->icon())
        {
                CLog::log(CLog::Log_Install, 3, L"Checking to install icon display file");
                C_File *icon = package->icon();
                if ( !icon->GetData() || !icon->GetDataSize() ) {
                        package->setIcon(NULL, L"");
                        CLog::log(CLog::Log_Install, 2, L"Unable to extract icon, clearing");
                }
                else
                {
                        CDirIO Dir(m_sCurrentDir);
                        bool ready = true;
                        if ( !Dir.exists(L"PluginManager") )
                        {
                                if ( !Dir.create(L"PluginManager") )
                                        ready = false;
                        }
                        if ( ready && !Dir.exists(L"PluginManager/Icons") )
                        {
                                if ( !Dir.create(L"PluginManager/Icons") )
                                        ready = false;
                        }

                        if ( ready )
                        {
                                if ( !icon->UncompressData() )
                                        package->setIcon(NULL, L"");
                                else
                                {
                                        CFileIO iconFile(m_sCurrentDir + L"/PluginManager/Icons/" + package->author() + L"_" + package->name() + L"." + package->iconExt());
                                        if ( iconFile.WriteData((const char *)icon->GetData(), icon->GetDataSize()) )
                                        {
                                                icon->setFilename(package->author() + L"_" + package->name() + L"." + package->iconExt());
                                                icon->setFullDir(m_sCurrentDir + L"/PluginManager/Icons");
                                        }
                                        else
                                                package->setIcon(NULL, L"");
                                }
                        }

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

        // remove all data
        CLog::log(CLog::Log_Install, 3, L"Clearing all unneeded file data");
        package->ClearFileData();

        // add to list
        if ( !dontAdd )
        {
                CLog::log(CLog::Log_Install, 1, L"Adding package into main list");
                if ( oldPackage )
                {
                        // find all other packages that has the old package as parent and switch to new
                        for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
                                if ( node->Data()->GetParent() == oldPackage ) {
                                        node->Data()->SetParent(package);
                                }
                        }

                        // remove olld package from list, and replace it with the new one
                        m_lPackages.insert(oldPackage, package);
                        m_lPackages.remove(oldPackage, false);
                }
                else
                        m_lPackages.push_back (package);
        }

        CLog::log(CLog::Log_Install, 2, L"Updating file used count");
        UpdateUsedFiles();

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

        // remove any files no longer used by old package
        if ( oldPackage )
        {
                CLog::log(CLog::Log_Install, 3, L"Removing any unused files from previous package");

                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);

                                // if its a readme file, then check if its in the new package
                                bool dontRemove = false;
                                if ( f->GetFileType() == FILETYPE_README ) {
                                        if ( package->findFile(f->filename(), FILETYPE_README) )
                                                dontRemove = true;
                                }

                                // remove from hard drive
                                if ( !dontRemove && removeFile(f, errors) )
                                {
                                        CLog::logf(CLog::Log_Install, 1, L"Removed unused file: %s", f->filePointer().c_str());
                                        // if a fake patch, we need to shufle
                                        if ( f->IsFakePatch() )
                                                shuffle = true;
                                        else if ( f->isAutoTextFile() )
                                                shuffle = true;
                                }
                        }

                        fnode = fnode->next();
                }

        }

        if ( shuffle )
        {
                CLog::log(CLog::Log_Install, 2, L"Shuffling Fake patches");
                shuffleFakePatches(errors);
                CLog::log(CLog::Log_Install, 2, L"Shuffling Text Files");
                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->otherName(), spk->otherAuthor()));
                        CLog::logf(CLog::Log_Install, 2, L"Linking to parent package: %s by %s (Version: %s)", spk->name().c_str(), spk->author().c_str(), spk->version().c_str());
                }
        }

        // store enabled mod
        if ( package->IsMod() && !disabled ) {
                m_pEnabledMod = package;
                CLog::log(CLog::Log_Install, 1, L"Setting package as primary mod");
        }

        package->updateTextDB();

        m_bRemoveDir = true;

        CLog::log(CLog::Log_Install, 1, L"Saving data to file");
        this->WriteData();

        CLog::log(CLog::Log_Install, 1, L"Installation Finished");
        return true;
}

bool CPackages::uninstallPreparedPackages(Utils::WStringList *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);

        Utils::WStringList 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 )
                {
                        Utils::WString dir = f->getDirectory(NULL);
                        if(!removeDirs.contains(dir))
                                removeDirs.pushBack(dir);
                        dir = dir.findReplace(L"\\", L"/");
                        if ( dir.contains(L"/") )
                        {
                                for ( int i = dir.countToken(L"/"); i; i-- )
                                {
                                        Utils::WString remDir = dir.tokens(L"/", 1, i);
                                        if(!removeDirs.contains(remDir))
                                                removeDirs.pushBack(remDir);
                                }
                        }
                }

                if (f->GetFileType() == FILETYPE_EXTRA && f->dir().left(6).lower() == L"extras")
                {
                        if (!removeDirs.contains(L"Extras"))
                                removeDirs.pushBack(L"Extras");
                }

                if (removeFile(f, errors))
                        original = _pOriginalFiles->restoreFile(f, errors);
                else // problem removeing (try when the program closes)
                        _lNonRemovedFiles.pushBack(f->filePointer());

                // 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(L"PluginManager/Uninstall");

                // make sure the scripts directory is created, even thou it should always be there anyways
                CDirIO scriptDir(m_sCurrentDir);
                if ( !scriptDir.exists(L"scripts") )
                {
                        if ( scriptDir.create(L"Scripts") )
                                this->addLogEntry(SPKINSTALL_CREATEDIRECTORY, L"Scripts", errors);
                        else
                                this->addLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, L"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->filename());
                        newFile->SetCreationTime(uf->GetCreationTime());

                        // other installed packages use this file as well, copy it
                        Utils::WString newFilename = m_sCurrentDir + L"/" + newFile->getNameDirectory(NULL);
                        CFileIO file(uf->filePointer());

                        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->filename().Compare(newFile->filename()) )
                                        {
                                                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(L"PluginManager/Readme");
                removeDirs.pushBack(L"Readme");
        }
        if ( original ) {
                removeDirs.pushBack(L"PluginManager/Original/Replacements");
                removeDirs.pushBack(L"PluginManager/Original");
        }
        removeDirs.pushBack(L"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->name(), package->author()))
                        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->otherName().Compare(p->name()) && spk->otherAuthor().Compare(p->author()) )
                                                {
                                                        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->otherName().Compare(package->name()) && spk->otherAuthor().Compare(package->author()) )
                                        {
                                                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);
                }
        }
}

wchar_t CPackages::ConvertWareTypeBack(int w)
{
        switch ( w )
        {
                case WARES_BIO:
                        return L'b';
                case WARES_ENERGY:
                        return L'e';
                case WARES_FOOD:
                        return L'f';
                case WARES_MINERAL:
                        return L'm';
                case WARES_TECH:
                        return L't';
                case WARES_NATURAL:
                        return L'n';
        }
        return L't';
}
int CPackages::ConvertWareType(wchar_t w)
{
        switch (std::tolower(w))
        {
                case L'b':
                        return WARES_BIO;
                case L'e':
                        return WARES_ENERGY;
                case L'f':
                        return WARES_FOOD;
                case L'm':
                        return WARES_MINERAL;
                case L't':
                        return WARES_TECH;
                case L'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->shipID()) )
                        {
                                if ( !p->IsEnabled() )
                                        wNode->Data()->iType = WARETYPE_DISABLED;
                                else
                                        wNode->Data()->iType = WARETYPE_ADDED;
                                found = true;
                                wNode->Data()->pPackage = p;
                                wNode->Data()->sShipClass = p->shipClass();
                                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->shipID()) )
                        {
                                gw = wNode->Data();
                                break;
                        }
                }

                // none found, create one
                if ( !gw )
                {
                        gw = new SGameShip;
                        gw->sShipID = p->shipID();
                        gw->sShipClass = p->shipClass();
                        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(Utils::WStringList *errors, CProgressInfo *progress, bool removedir)
{
        if ( m_sCurrentDir.empty() )
                return true;
        if ( !m_bLoaded )
                return true;

        CLog::log(CLog::Log_Directory, 1, L"closing directory: " + m_sCurrentDir);

        if ( m_bRenameText ) {
                CLog::log(CLog::Log_Directory, 2, L"Creating other language files for game");
                CreateLanguageTextFiles(errors);
        }

        CLog::log(CLog::Log_Directory, 2, L"Backing up save game files");

        if ( CFileIO::Exists(m_sCurrentDir + L"/mods/PluginManager.dat") ) {
                CLog::log(CLog::Log_IO, 3, L"Removing old PluginManager.dat file");
                CFileIO::Remove(m_sCurrentDir + L"/mods/PluginManager.dat");
        }
        if ( CFileIO::Exists(m_sCurrentDir + L"/mods/PluginManager.cat") ) {
                CLog::log(CLog::Log_IO, 3, L"Removing old PluginManager.cat file");
                CFileIO::Remove(m_sCurrentDir + L"/mods/PluginManager.cat");
        }

        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(L"dat"))
                                fDat = m_pEnabledMod->GetNextFile(fDat);

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

                                if ( fCat )
                                {
                                        CFileIO(fDat->filePointer()).copy(m_sCurrentDir + L"/mods/PluginManager.dat");
                                        CFileIO(fCat->filePointer()).copy(m_sCurrentDir + L"/mods/PluginManager.cat");
                                }
                        }
                }
                else if ( m_iGame == GAME_X3 && !m_sSetMod.empty() && CFileIO::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat") && CFileIO::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".dat"))
                {
                        CLog::log(CLog::Log_Directory, 2, L"Copying mod file: " + m_sSetMod + L", to PluginManager.cat");
                        CFileIO(m_sCurrentDir + L"/mods/" + m_sSetMod + L".dat").copy(m_sCurrentDir + L"/mods/PluginManager.dat");
                        CFileIO(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat").copy(m_sCurrentDir + L"/mods/PluginManager.cat");
                }

                if ( !CDirIO(m_sCurrentDir).exists(L"mods") )
                        CDirIO(m_sCurrentDir).create(L"mods");

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

        RemoveFailedFiles();

        WriteData();
        if ( removedir && m_bRemoveDir )
        {
                m_bRemoveDir = false;
                Utils::WStringList removeDirs;
                removeDirs.pushBack(L".");
                removeUnusedDirectories(removeDirs, errors);
        }

        this->setCurrentDir(L"");
                 
        m_bLoaded = false;
        return true;
}

Utils::WString CPackages::getModKey() const
{
        return m_gameExe.getModKey(m_iGame - 1);
}
Utils::WString CPackages::selectedModName() const
{
        if ( !m_pEnabledMod )
        {
                if ( m_sSetMod.empty() && m_iGame == GAME_X3 )
                        return L"PluginManager";
                return m_sSetMod;
        }

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

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

        Utils::WString name = f->filename();
        name = name.left(-4);
        return name;

}

bool CPackages::RestoreFakePatch()
{
        CLog::log(CLog::Log_Directory, 1, L"Restoring PluginManager fake patch into game");
        m_iFakePatch = -1;

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

        // if only 1 exists, remove it
        if ( catFile.exists() && !datFile.exists() ) {
                CLog::log(CLog::Log_Directory, 1, L"WARNING: cat/dat file mismatch, dat file seems to be missing, removing cat file");
                if ( !catFile.remove() ) return false;
        }
        else if ( !catFile.exists() && datFile.exists() ) {
                CLog::log(CLog::Log_Directory, 1, L"WARNING: cat/dat file mismatch, cat file seems to be missing, removing dat file");
                if ( !datFile.remove() ) return false;
        }

        // if both exists, lets rename them
        if ( catFile.exists() && datFile.exists() )
        {
                CLog::log(CLog::Log_Directory, 3, L"Creating pluginmanagerfake.txt file to add to Fake Patch");
                // we need to add the plugin manager file in
                Utils::WString file = m_sTempDir;
                if ( !file.empty() )
                        file += L"/";
                file += L"pluginmanagerfake.txt";
                file = file.findReplace(L"\\", L"/");
                CFileIO fakeFile(file);
                std::vector<Utils::WString> lines;
                lines.push_back(L"//pluginmanager fake patch");
                CLog::log(CLog::Log_Directory, 3, L"Writing pluginmanagerfake.txt file to add to Fake Patch");
                if ( !fakeFile.writeFile(lines) ) {
                        CLog::log(CLog::Log_Directory, 3, L"Writing pluginmanagerfake.txt failed!!");
                }
                else {
/*
                        CLog::log(CLog::Log_Directory, 2, "Adding TFake.pck file into FakePatch");
                        CCatFile fakePatch;
                        if ( fakePatch.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE )
                        {
                                if ( fakePatch.AppendFile(file.ToString(), "PlugMan\\TFake.pck") )
                                        fakePatch.WriteCatFile();
                        }       */              
                }

                // backup existing mod files incase something happens to them
                if ( !datFile.exists() || !catFile.exists() ) {
                        CLog::log(CLog::Log_Directory, 2, Utils::WString("ERROR: ") + (!catFile.exists() ? L"cat" : L"dat") + L" file appears to be missing");
                }
                else {
                        catFile.GetDirIO().create(L"Backup");
                        catFile.copy(catFile.dir() + L"/Backup/" + catFile.filename());
                        datFile.copy(datFile.dir() + L"/Backup/" + datFile.filename());
                }


                // find next available fake patch
                m_iFakePatch = this->findNextFakePatch();
                CLog::log(CLog::Log_Directory, 2, L"Finding next available fake patch number: " + (long)m_iFakePatch);

                Utils::WString filename = Utils::WString::PadNumber(m_iFakePatch, 2);
                CLog::log(CLog::Log_Directory, 2, L"Renaming cat file to: " + filename + L".cat");
                if ( catFile.Rename(m_sCurrentDir + L"/" + filename + L".cat") )
                {
                        CLog::log(CLog::Log_Directory, 2, L"Renaming dat file to: " + filename + L".dat");
                        if ( datFile.Rename(m_sCurrentDir + L"/" + filename + L".dat") ){
                                CLog::log(CLog::Log_Directory, 3, L"Deleting pluginmanagerfake.txt temporary file");
                                fakeFile.remove();
                                return true;
                        }
                        else {
                                CLog::log(CLog::Log_Directory, 2, L"ERROR: failed to rename dat file");
                        }

                        // TODO: restore cat file
                }
                else {
                        CLog::log(CLog::Log_Directory, 2, L"ERROR: failed to rename cat file");
                }

                CLog::log(CLog::Log_Directory, 3, L"Deleting pluginmanagerfake.txt temporary 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;

        CLog::log(CLog::Log_Directory, 1, L"Writing data file for current directory: " + m_sCurrentDir);

        Utils::WStringList lines;
        Utils::WString version = L"SpkInstaller: " + Utils::WString::FromFloat(GetLibraryVersion(), 2);
        lines.pushBack(version);

        lines.pushBack(Utils::WString(L"UpdateTime: ") + (long)m_iLastUpdated);
        if ( m_iFakePatch != -1 )
                lines.pushBack(Utils::WString(L"FakePatch: ") + (long)m_iFakePatch);
        if ( m_iSaveGame != -1 )
                lines.pushBack(Utils::WString(L"SaveGame: ") + (long)m_iSaveGame);
        lines.pushBack(Utils::WString(L"SaveGameManager: ") + (long)m_iSaveGameManager);
        if ( !m_bVanilla )
                lines.pushBack(L"Modified");
        if ( m_bUsedWare )
                lines.pushBack(L"UsedWare");
        if ( m_bSurpressProtectedWarning )
                lines.pushBack(L"SurpressProtectedWarning");
        if ( !m_sSetMod.empty() )
                lines.pushBack(L"SetMod: " + m_sSetMod);
        if (!_sSaveDir.empty())
                lines.pushBack(L"SaveDir: " + _sSaveDir);
        lines.pushBack(L"ShipBuffer: " + (long)m_iShipBuffer);
        Utils::WString wareBuffer = L"WareBuffers:";
        for ( int i = 0; i < WAREBUFFERS; i++ )
                wareBuffer += Utils::WString(L" ") + (long)m_iWareBuffer[i];
        lines.pushBack(wareBuffer);
        for ( int i = 0; i < WAREBUFFERS; i++ )
        {
                if ( !m_lGameWares[i].size() )
                        continue;
                lines.pushBack(Utils::WString(L"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(Utils::WString((long)gm->iPos) + L" " + (long)gm->iType + L" " + Utils::WString((char)gm->cType) + L" " + gm->sWareName);
                }
        }

        for(auto itr = _lNonRemovedFiles.begin(); itr != _lNonRemovedFiles.end(); itr++)
                lines.pushBack(L"NonRemovedFile: " + (*itr)->str);

        if ( m_lGameShips.size() )
        {
                lines.pushBack(Utils::WString(L"Ships: ") + (long)m_lGameShips.size());

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

        // write created Files
        if ( !_lCreatedFiles.empty() )
        {
                for(auto itr = _lCreatedFiles.begin(); itr != _lCreatedFiles.end(); itr++)
                        lines.pushBack(L"CreatedFile: " + (*itr)->str.findRemove(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();
                        Utils::WString uString = L"Uninstall: ";
                        uString += Utils::WString::Number((long)uf->GetCreationTime()) + L" ";
                        uString += uf->filename();
                        lines.pushBack(uString);
                }
        }

        // write the original file data
        _pOriginalFiles->writeData(lines);

        // write the global changes
        if ( !_lGlobals.empty() )
        {
                for(auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
                        lines.pushBack(L"GlobalSetting: " + (*itr)->str + L":" + (*itr)->data.findRemove(L";"));
        }

        // write the fake patch ordering
        if ( !_lFakePatchOrder.empty() )
        {
                for(auto itr = _lFakePatchOrder.begin(); itr != _lFakePatchOrder.end(); itr++)
                        lines.pushBack(L"FakePatchOrder: " + (*itr)->str + L":" + (*itr)->data);
        }

        for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
                SWarePriceOverride *ware = node->Data();
                switch(ware->type) {
                        case Ware_EMP:
                                lines.pushBack(Utils::WString(L"EMPPriceOverride:") + (long)ware->pos + L" " + (long)ware->relval);
                                if ( ware->bNotority )
                                        lines.pushBack(Utils::WString(L"EMPNotoOverride:") + (long)ware->pos + L" " + (long)ware->notority);
                                break;
                        case Ware_Custom:
                                lines.pushBack(Utils::WString(L"CustomWarePriceOverride:") + ware->id + L";" + (long)ware->relval);
                                if ( ware->bNotority )
                                        lines.pushBack(Utils::WString(L"CustomWareNotoOverride:") + ware->id + L";" + (long)ware->notority);
                                break;
                        case Ware_BuiltIn:
                                lines.pushBack(Utils::WString(L"BuiltInWarePriceOverride:") + (long)ware->pos + L" " + (long)ware->relval);
                                if ( ware->bNotority )
                                        lines.pushBack(Utils::WString(L"BuiltInWareNotoOverride:") + (long)ware->pos + L" " + (long)ware->notority);
                                break;
                }
        }

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

                Utils::WString line = Utils::WString::Number(f->GetFileType()) + L":" + Utils::WString::Number((long)f->GetCreationTime()) + L":" + f->dir() + L":";
                if ( f->IsShared() && !f->IsFakePatch() )
                        line += L"1:";
                else
                        line += L"0:";

                Utils::WString filename = f->filePointer().findRemove(m_sCurrentDir);

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

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

                line += filename;

                if ( !f->originalName().empty() )
                {
                        line += L"O#";
                        line += f->originalName();
                }

                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(L"<script>");
                else if ( package->GetType() == TYPE_XSP )
                        lines.pushBack(L"<ship>");
                else if ( package->GetType() == TYPE_ARCHIVE )
                        lines.pushBack(L"<archive>");
                else if ( package->GetType() == TYPE_BASE )
                        lines.pushBack(L"<base>");
                else
                        continue;

                if ( !package->filename().empty() )
                        lines.pushBack(L"Installspk: " + package->filename());

                Utils::WString valuesline = package->createValuesLine();
                if ( valuesline.back() == '\n')
                        valuesline.truncate((int)valuesline.length() - 1);
                lines.pushBack(valuesline);

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

                if (package->icon())
                        lines.pushBack(L"Icon: " + package->iconExt() + L" " + package->icon()->filePointer() );

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

        lines.pushBack(L"</scripts>");

        CFileIO datFile(m_sCurrentDir + L"/PluginManager/PluginManager.new");

        CDirIO Dir(m_sCurrentDir);
        if ( !Dir.exists(L"PluginManager") ) {
                CLog::log(CLog::Log_IO, 2, L"Creating PluginManager directory");
                Dir.create(L"PluginManager");
        }

        CLog::log(CLog::Log_IO, 2, L"Writing data file: " + m_sCurrentDir + L"/PluginManager/PluginManager.new");
        if ( !datFile.writeFileUTF(lines) )
                CLog::log(CLog::Log_IO, 1, L"ERROR: Failed to write data file");
        else {
                CLog::log(CLog::Log_IO, 2, L"Removing old data file: " + m_sCurrentDir + L"/PluginManager/PluginManager.dat");
                if ( !CFileIO::Exists(m_sCurrentDir + L"/PluginManager/PluginManager.dat") || CFileIO::Remove(m_sCurrentDir + L"/PluginManager/PluginManager.dat") ) {
                        CLog::log(CLog::Log_IO, 2, L"Renaming data file: PluginManager.new => PluginManager.dat");
                        datFile.Rename(m_sCurrentDir + L"/PluginManager/PluginManager.dat");
                }
        }
}


/**
 * 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, const Utils::WString &args, Utils::WStringList *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, Utils::WStringList *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(Utils::WStringList *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
        Utils::WStringList fakePatches;
        for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if (!f->isForGame(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
                _pOriginalFiles->doBackup(f, errors);

                Utils::WString newFilename = f->getNameDirectory(package);

                // fake patches need to be renamed
                if ( f->IsFakePatch() )
                {
                        // first check if the matching file has been done already
                        if (fakePatches.contains(f->baseName()))
                                newFilename = fakePatches.findString(f->baseName()) + L"." + f->fileExt();
                        // we need to find the next available number instead
                        else
                        {
                                Utils::WString newPos = Utils::WString::PadNumber(findNextFakePatch(), 2);
                                newFilename = newPos + L"." + f->fileExt();
                                // and wee need to push this onto the string list so we can adjust the matchign pair
                                fakePatches.pushBack(f->baseName(), newPos);
                        }
                }

                // lets actually move the file back now
                // !!error checking!!
                CFileIO currentFile(f->filePointer());
                CFileIO newFile(m_sCurrentDir + L"/" + 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.fullFilename()) )
                        {
                                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 + L"/" + 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);

                _pOriginalFiles->installed(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
        Utils::WStringList removeDirs;
        removeDirs.pushBack(L"PluginManager/Disabled");
        removeUnusedDirectories(removeDirs, errors);

        m_lEnableList.clear();

        this->WriteData();

        return true;
}


bool CPackages::disablePreparedPackages(Utils::WStringList *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
        Utils::WStringList 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->dir().left(5).lower() == L"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->filename().Compare(f->filename()) )
                                {
                                        found = true;
                                        break;
                                }
                        }

                        if ( found )
                                continue;
                }

                if ( f->GetFileType() == FILETYPE_MOD && !f->IsFakePatch() && f->checkFileExt(L"cat") )
                {
                        if ( f->baseName().Compare(m_sSetMod) )
                                m_sSetMod = Utils::WString::Null();
                }

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

                // fake patches have thier own special directory
                if ( f->IsFakePatch() )
                        newFilename += L"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 + L"/PluginManager/Disabled/FakePatches/FakePatch_" + checkPackage->getNameValidFile() + L"_" + checkPackage->author() + L"_" + f->name();
                                shuffle = true;
                        }
                }
                else if ( f->isAutoTextFile() )
                {
                        if ( checkPackage )
                        {
                                newFilename = m_sCurrentDir + L"/PluginManager/Disabled/TextFiles/Text_" + checkPackage->getNameValidFile() + L"_" + checkPackage->author() + L"_" + f->name();
                                shuffle = true;
                        }
                }
                // otherwise we can just use the standard filename
                else
                        newFilename = m_sCurrentDir + L"/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->filePointer());
                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);

                original = _pOriginalFiles->restoreFile(f, errors);

                // extra file thats not in the extras directory
                if (f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND)
                {
                        if(!removeDirs.contains(f->getDirectory(checkPackage)))
                                removeDirs.pushBack(f->getDirectory(checkPackage));
                }

                // 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(L"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, Utils::WStringList *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) const
{
        // 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->name(), package->author());
        else if ( package->GetType() == TYPE_XSP )
                return findXspPackage(((CXspFile *)package)->shipID());
        else if ( package->GetType() == TYPE_ARCHIVE )
                return findArchivePackage(package->name());

        // nothing found obviously
        return 0;
}

CBaseFile *CPackages::findFirstPackageWithFile(C_File *f) const
{
        return findNextPackageWithFile(NULL, f);
}

CBaseFile *CPackages::findNextPackageWithFile(CBaseFile *p, C_File *f) const
{
        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(FileType filetype, const Utils::WString &filename, const Utils::WString &dir) const
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() != filetype )
                        continue;

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

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

                return f;
        }

        return NULL;
}

CArchiveFile *CPackages::findArchivePackage(const Utils::WString &name) const
{
        // 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->name().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(const Utils::WString &name, const Utils::WString &author) const
{
        // 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->name().Compare(name) && file->author().Compare(author) )
                        return file;
        }

        // nothing found
        return 0;
}

CBaseFile* CPackages::findPackage(const Utils::WString &name, const Utils::WString &author) const
{
        // 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->name().Compare(name) && file->author().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(const Utils::WString &id) const
{
        // 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)->shipID().Compare(id) )
                        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(const Utils::WStringList& dirs, Utils::WStringList* errors)
{
        CDirIO Dir(m_sCurrentDir);
        for(auto itr = dirs.begin(); itr != dirs.end(); itr++)
        {
                Utils::WString dir = (*itr)->str;

                Utils::WStringList removedDir;
                if (Dir.removeDir(dir, false, true, &removedDir) || !removedDir.empty())
                {
                        for(auto rItr = removedDir.begin(); rItr != removedDir.end(); rItr++)
                        {
                                Utils::WString displayName = (*rItr)->str;
                                displayName = displayName.findRemove(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->version().compareVersion(package->version()) == 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, _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->otherName(), spk->otherAuthor()) )
                        {
                                // 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->otherName().Compare(node->Data()->name()) && spk->otherAuthor().Compare(node->Data()->author()) )
                                        {
                                                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)->otherName(), ((CSpkFile *)p)->otherAuthor());
                        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(L"<package>") )
                                continue;
                        if ( !this->checkInstalledDependacy(dNode->Data()->sName, dNode->Data()->sAuthor, dNode->Data()->sMinVersion, true, true))
                                return false;
                }
        }

        return true;
}

bool CPackages::checkInstalledDependacy(const Utils::WString &name, const Utils::WString &author, const Utils::WString &version, bool onlyEnabled, bool includePrepared) const
{
        CBaseFile *p = this->findSpkPackage(name, author);
        if ( p )
        {
                // now check version
                if (version.compareVersion(p->version()) == 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->name().Compare(name) && p->author().Compare(author) )
                        {
                                if (version.compareVersion(p->version()) == COMPARE_OLDER)
                                        continue;

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

                                return true;
                        }
                }
        }

        return false;
}

bool CPackages::findAllNeededDependacies(CBaseFile *p, const CLinkList<CBaseFile> &packages, CLinkList<CBaseFile> *foundPackages, bool onlyEnabled, bool includePrepared) const
{
        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(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared))
                        {
                                bool found = false;
                                for (auto itr = packages.Front(); itr; itr = itr->next())
                                {
                                        if (itr->Data()->name().Compare(nl->sName) && itr->Data()->author().Compare(nl->sAuthor) && nl->sMinVersion.compareVersion(itr->Data()->version()) >= 0)
                                        {
                                                if (!findAllNeededDependacies(itr->Data(), packages, foundPackages, onlyEnabled, includePrepared))
                                                        return false;
                                                if (foundPackages)
                                                {
                                                        bool added = false;
                                                        for (auto itr2 = foundPackages->Front(); itr2; itr2 = itr2->next())
                                                        {
                                                                if (itr->Data() == itr2->Data() || (itr->Data()->name().Compare(itr2->Data()->name()) && itr->Data()->author().Compare(itr2->Data()->author())))
                                                                {
                                                                        added = true;
                                                                        break;
                                                                }
                                                        }
                                                        if(!added)
                                                                foundPackages->push_front(itr->Data());
                                                }
                                                found = true;
                                                break;
                                        }
                                }
                                if (!found)
                                        return false;
                        }
                }
        }

        if (p->GetType() == TYPE_SPK)
        {
                CSpkFile *spk = (CSpkFile *)p;
                if (spk->isAnotherMod())
                {
                        bool found = true;
                        if (!this->findSpkPackage(spk->otherName(), spk->otherAuthor()))
                        {
                                if (includePrepared)
                                {
                                        found = false;
                                        for (CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next())
                                        {
                                                CBaseFile *checkP = pNode->Data();
                                                if (p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()))
                                                {
                                                        found = true;
                                                        break;
                                                }
                                        }
                                }
                                else
                                        found = false;
                        }

                        if (!found)
                        {
                                for (auto itr = packages.Front(); itr; itr = itr->next())
                                {
                                        if (itr->Data()->name().Compare(spk->otherName()) && itr->Data()->author().Compare(spk->otherAuthor()))
                                        {
                                                if (!findAllNeededDependacies(itr->Data(), packages, foundPackages, onlyEnabled, includePrepared))
                                                        return false;
                                                if(foundPackages)
                                                {
                                                        bool added = false;
                                                        for (auto itr2 = foundPackages->Front(); itr2; itr2 = itr2->next())
                                                        {
                                                                if (itr->Data() == itr2->Data() || (itr->Data()->name().Compare(itr2->Data()->name()) && itr->Data()->author().Compare(itr2->Data()->author())))
                                                                {
                                                                        added = true;
                                                                        break;
                                                                }
                                                        }
                                                        if (!added)
                                                                foundPackages->push_front(itr->Data());
                                                }
                                                found = true;
                                                break;
                                        }
                                }

                                if(!found)
                                        return false;
                        }
                }
        }

        return true;

}

size_t CPackages::getDownloadableDependacies(CBaseFile* p, std::vector<const SAvailablePackage*>& list, bool onlyEnabled, bool includePrepared) const
{
        size_t 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(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared))
                        {
                                const SAvailablePackage *available = findAvailablePackage((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor);
                                if (available)
                                        list.push_back(available);
                                ++count;
                        }
                }
        }

        if (p->GetType() == TYPE_SPK)
        {
                CSpkFile* spk = (CSpkFile*)p;
                if (spk->IsAnotherMod())
                {
                        bool found = true;
                        if (!this->findSpkPackage(spk->otherName(), spk->otherAuthor()))
                        {
                                if (includePrepared)
                                {
                                        found = false;
                                        for (CListNode<CBaseFile>* pNode = m_lInstallList.Front(); pNode; pNode = pNode->next())
                                        {
                                                CBaseFile* checkP = pNode->Data();
                                                if (p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()))
                                                {
                                                        found = true;
                                                        break;
                                                }
                                        }
                                }
                                else
                                        found = false;
                        }

                        if (!found)
                        {
                                const SAvailablePackage* available = findAvailablePackage(spk->otherName(), spk->otherAuthor());
                                if (available)
                                        list.push_back(available);
                                ++count;
                        }
                }
        }

        return count;
}

int CPackages::getMissingDependacies(CBaseFile *p, Utils::WStringList *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(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared) )
                        {
                                if ( list )
                                        list->pushBack(((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName) + L"|" + nl->sMinVersion, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor);
                                ++count;
                        }
                }
        }

        if ( p->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)p;
                if ( spk->IsAnotherMod() )
                {
                        bool found = true;
                        if ( !this->findSpkPackage(spk->otherName(), spk->otherAuthor()) )
                        {
                                if ( includePrepared )
                                {
                                        found = false;
                                        for ( CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next() )
                                        {
                                                CBaseFile *checkP = pNode->Data();
                                                if ( p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()) )
                                                {
                                                        found = true;
                                                        break;
                                                }
                                        }
                                }
                                else
                                        found = false;
                        }

                        if ( !found )
                        {
                                if ( list )
                                        list->pushBack(spk->otherName(), spk->otherAuthor());
                                ++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, Utils::WStringList *errors)
{
        CFileIO fio(file->filePointer());
        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(Utils::WStringList *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->filename().Compare(file->filename()) )
                        {
                                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, Utils::WStringList *errors)
{
        CFileIO fio(file->filePointer());
        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(Utils::WStringList *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;

}

void CPackages::shuffleTextFiles(Utils::WStringList *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->textFileID() )
                {
                        CFileIO moveFile(f->filePointer());
        
                        Utils::WString newName = SPK::FormatTextName(current, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT)) + L"." + moveFile.extension();
                        if ( moveFile.Rename(m_sCurrentDir + L"/t/" + newName) )
                        {
                                this->addLogEntry(SPKINSTALL_AUTOTEXT, f->name() + L"~" + newName, errors);
                                f->setName(newName);
                        }
                        else
                                this->addLogEntry(SPKINSTALL_AUTOTEXT_FAIL, f->name() + L"~" + 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(Utils::WStringList *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

        // find all packages that need to go before or after other packages
        std::set<CBaseFile*> packagesBefore;
        std::map<CBaseFile*, std::set<CBaseFile*>> packagesAfter;
        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;

                if (p->AnyDependacies())
                {
                        for (SNeededLibrary* nl = p->GetNeededLibraries()->First(); nl; nl = p->GetNeededLibraries()->Next())
                        {
                                auto package = findPackage(nl->sName, nl->sAuthor);
                                if (package)
                                {
                                        packagesBefore.insert(package);
                                        packagesAfter[p].insert(package);
                                }
                        }
                }
                CSpkFile* spk = dynamic_cast<CSpkFile*>(p);
                if (spk)
                {
                        if (spk->IsAnotherMod())
                        {
                                auto package = findPackage(spk->otherName(), spk->otherAuthor());
                                if (package)
                                {
                                        packagesBefore.insert(package);
                                        packagesAfter[p].insert(package);
                                }
                        }
                }

                if (!p->getFakePatchBeforeOrder().empty())
                {
                        packagesBefore.insert(p);
                        auto& list = p->getFakePatchBeforeOrder();
                        for (auto itr = list.begin(); itr != list.end(); itr++)
                        {
                                auto package = findPackage((*itr)->str, (*itr)->data);
                                if (package)                            
                                        packagesAfter[package].insert(p);
                        }
                }
                if (!p->getFakePatchAfterOrder().empty())
                {
                        auto& list = p->getFakePatchAfterOrder();
                        for (auto itr = list.begin(); itr != list.end(); itr++)
                        {
                                auto package = findPackage((*itr)->str, (*itr)->data);
                                if (package)
                                {
                                        packagesBefore.insert(package);
                                        packagesAfter[p].insert(package);
                                }
                        }
                }
        }

        auto addOrderedList = [](const std::vector<CBaseFile*>& addList, const Utils::WStringList& orderList, std::vector<CBaseFile*>& addTo, const CPackages *packages)
        {
                // add all the items in the ordered list first
                for (auto itr = orderList.begin(); itr != orderList.end(); itr++)
                {
                        auto p = packages->findPackage((*itr)->str, (*itr)->data);
                        if (p)
                        {
                                auto findItr = std::find(addList.begin(), addList.end(), p);
                                if (findItr != addList.end())
                                {
                                        auto checkItr = std::find(addTo.begin(), addTo.end(), p);
                                        if(checkItr == addTo.end())
                                                addTo.push_back(p);
                                }
                        }
                }

                // now add all the others
                for (auto itr = addList.begin(); itr != addList.end(); itr++)
                {
                        auto checkItr = std::find(addTo.begin(), addTo.end(), *itr);
                        if (checkItr == addTo.end())
                                addTo.push_back(*itr);
                }
        };

        auto removeList = [](const std::vector<CBaseFile*>& removeList, std::set<CBaseFile*>& list)
        {
                for (auto itr = removeList.begin(); itr != removeList.end(); itr++)
                {
                        auto findItr = std::find(list.begin(), list.end(), *itr);
                        if (findItr != list.end())
                                list.erase(findItr);
                }
        };

        std::vector<CBaseFile*> order;
        while (!packagesBefore.empty())
        {
                std::vector<CBaseFile*> packagesOrder;

                // all packages that go before, but not after should go first
                for (auto itr = packagesBefore.begin(); itr != packagesBefore.end(); itr++)
                {
                        CBaseFile* p = *itr;
                        auto findItr = packagesAfter.find(p);
                        if (findItr == packagesAfter.end())
                                packagesOrder.push_back(p);
                        else
                        {
                                // check if any packages that we go after have been added
                                bool notAdded = false;
                                for (auto checkItr = findItr->second.begin(); checkItr != findItr->second.end(); checkItr++)
                                {
                                        auto findItr2 = std::find(order.begin(), order.end(), *checkItr);
                                        if (findItr2 == order.end())
                                        {
                                                notAdded = true;
                                                break;
                                        }
                                }

                                if (!notAdded)
                                        packagesOrder.push_back(p);
                        }
                }


                // no valid packages left ? lets just add the rest
                if (packagesOrder.empty())
                {
                        for (auto itr = packagesBefore.begin(); itr != packagesBefore.end(); itr++)
                                packagesOrder.push_back(*itr);
                }

                addOrderedList(packagesOrder, _lFakePatchOrder, order, this);
                removeList(packagesOrder, packagesBefore);
        }

        // now add the remaining list ordered list
        for (auto itr = _lFakePatchOrder.begin(); itr != _lFakePatchOrder.end(); itr++)
        {
                auto p = findPackage((*itr)->str, (*itr)->data);
                if (p)
                {
                        auto findItr = std::find(order.begin(), order.end(), p);
                        if (findItr == order.end())
                                order.push_back(p);
                }
        }

        for(auto itr = order.begin(); itr != order.end(); itr++)
        {
                CBaseFile *package = *itr;
                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(L"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->baseName().toInt() < lowestFile->baseName().toInt() )
                                                        lowestFile = file;
                                        }
                                }

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

                                // check its filename, it might already be in the correct place
                                if ( lowestFile->baseName().toInt() != lowest )
                                {
                                        // if the file already exists, we need to move it elsewhere
                                        Utils::WString nextName = Utils::WString::PadNumber(lowest, 2);
                                        if ( CFileIO::Exists(m_sCurrentDir + L"/" + nextName + L".cat") )
                                        {
                                                // find the file in our internal file list
                                                C_File *moveFile = findFile(FILETYPE_MOD, nextName + L".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(L"cat"))
                        continue;

                // now lets check if its greater than our gap
                int check = findNextFakePatch();
                int patchNum = f->filename().token(L".", 1).toInt();
                if ( patchNum <= check )
                        continue;

                shufflePatchTo(f, check, errors);
        }
}

void CPackages::shufflePatchTo(C_File *file, int to, Utils::WStringList *errors)
{
        // it is, we need to shift this to fill the gap
        Utils::WString newName = Utils::WString::PadNumber(to, 2) + L"." + file->fileExt();

        // now rename the file
        CFileIO moveFile(file->filePointer());
        if ( moveFile.Rename(m_sCurrentDir + L"/" + newName) )
        {
                // display moveing
                this->addLogEntry(SPKINSTALL_FAKEPATCH, file->name() + L"~" + 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->fileExt()))
                                continue;

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

                        Utils::WString newName2 = Utils::WString::PadNumber(to, 2) + L"." + f2->fileExt();
                        CFileIO moveFile(f2->filePointer());
                        if ( moveFile.Rename(m_sCurrentDir + L"/" + newName2) )
                        {
                                this->addLogEntry(SPKINSTALL_FAKEPATCH, f2->name() + L"~" + newName2, errors);
                                f2->setName(newName2);
                        }
                        else
                                this->addLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->name() + L"~" + newName2, errors);
                }

                // finally make sure the internal name matches the new one
                file->setName(newName);
        }
        else
                this->addLogEntry(SPKINSTALL_FAKEPATCH_FAIL, file->name() + L"~" + 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(L"dat")) continue;
                        int check = file->baseName().toInt();
                        if ( check < lowest )
                                lowest = check;
                }
        }

        return lowest;
}

int CPackages::findLastFakePatch(int start, const Utils::WString &dir) const
{
        CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);

        int check = start;
        while ( check > 0 )
        {
                Utils::WString checkStr = Utils::WString::PadNumber(check, 2);

                // check if a cat file exists
                if ( !Dir.exists(checkStr + L".cat") )
                {
                        // it doen't, check if theres a dat file (incase of package error)
                        if ( !Dir.exists(checkStr + L".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, const Utils::WString &dir) const
{
        CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);

        int check = start;
        while ( check < 99 )
        {
                ++check;
                Utils::WString checkStr = Utils::WString::PadNumber(check, 2);

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

        return check;
}

/**
 * Find next text file
 *
 * Searching for the next gap in automatic text files, start with 0004-LXXX or XX0004
 */
unsigned int CPackages::findNextTextFile(unsigned int start) const
{
        return findNextTextFile(Utils::WString::Null(), start);
}
unsigned int CPackages::findNextTextFile(const Utils::WString &dir, unsigned int start) const
{
        int check = start;
        if ( check < 2 ) check = 2;
        while ( check < 9999 )
        {
                ++check;
                Utils::WString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));

                // check the vfs
                if ( m_pGameVFS.isFileAvailable(L"t/" + newFilename + L".pck") ) continue;
                if ( m_pGameVFS.isFileAvailable(L"t/" + newFilename + L".xml") ) continue;

                break;
        }

        return check;
}

int CPackages::findLastTextFile(int start, const Utils::WString& dir) const
{
        CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);
        Dir.cd(L"t");

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

                // check if a packed file exists
                if ( !Dir.exists(newFilename + L".pck") )
                {
                        // it doen't, check if theres an unpacked file
                        if ( !Dir.exists(newFilename + L".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(L"lang.dat") )
        {
                CFileIO File(Dir.file(L"lang.dat"));

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

                if ( data )
                {
                        Utils::WString str(data);
                        m_iLanguage = str.token(L"\n", 1).token(L" ", 1).toInt();
                }
        }
}

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

        // find all text files
        Utils::WStringList 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;

                Utils::WString id, lang;
                if ( m_iGameFlags & EXEFLAG_TCTEXT )
                {
                        id = f->baseName().token(L"-", 1);
                        lang = f->baseName().token(L"-", 2);
                        if ( lang.empty() )
                                lang = L"NULL";
                        else
                                lang = lang.erase(0, 1);  // remove the "L"
                }
                else
                {
                        lang = Utils::WString::PadNumber(f->baseName().left((int)f->baseName().length() - 4), 3);
                        id = f->baseName().mid(((int)f->baseName().length() - 4) + 1, 4);
                }


                // not added, add a new one
                if (!ids.contains(id))
                        ids.pushBack(id, lang);
                else
                {
                        Utils::WString data = ids.findString(id);
                        if (data.empty())
                                data = lang;
                        else
                        {
                                data += L":";
                                data += lang;
                        }
                        ids.changeData(id, data);
                }
        }

        // we should now have a list of all text files, we need to remove those that have a matching language
        for(auto itr = ids.begin(); itr != ids.end(); itr++)
        {
                int size = 0;
                std::vector<Utils::WString> data;
                if(!(*itr)->data.tokenise(L":", data))                  
                        continue; // huh ? we shouldn't have this

                // lets search for a matching id
                int useId = 0;
                bool found = false;
                for ( int i = 0; i < size; i++ )
                {
                        if ( data[i] == L"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;

                renameTextFile((*itr)->str, useId, errors);
        }
}

/**
 * Rename a text file
 *
 * Creates a new text file for the selected langage by copying an existing one
 */
bool CPackages::renameTextFile(const Utils::WString &textid, int languageid, Utils::WStringList *errors)
{
        // lets check if the file already exists
        Utils::WString newFilename = SPK::FormatTextName(textid.toInt(), m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
        C_File *addFile = findFile(FILETYPE_TEXT, newFilename + L".xml");
        if ( !addFile )
                addFile = findFile(FILETYPE_TEXT, newFilename + L".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
        Utils::WString filename;
        if ( languageid != -1 )
                filename = SPK::FormatTextName(textid.toInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));
        else
                filename = textid;

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

        // now lets create out new text file
        CFileIO readFile(textFile->filePointer());

        std::vector<Utils::WString> lines;
        readFile.readLines(lines);

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

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

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

        for(std::vector<Utils::WString>::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 + L"/" + addFile->getNameDirectory(NULL));
        if ( writeFile.writeFile(lines) )
        {
                this->addLogEntry(SPKINSTALL_WRITEFILE, addFile->getNameDirectory(NULL), errors);

                addFile->setFilename(m_sCurrentDir + L"/" + 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);
                return false;
        }

        return true;
}

/**
 * Add file to scripts
 *
 * Adds a file to all scripts that need it
 */
void CPackages::addTextFileToScripts(C_File *file, const Utils::WString &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;

                        Utils::WString id = tFile->baseName().token(L"-", 1);
                        if ( id == textid )
                        {
                                found = true;
                                break;
                        }
                }

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

bool CPackages::removeFile(C_File *file, Utils::WStringList *errors)
{
        if ( !file )
                return true;

        Utils::WString remFileStr = file->filePointer().findReplace(m_sCurrentDir, L"");

        if ( file->filePointer().contains(L"::")) {
                CFileIO CatFile(file->filePointer().token(L"::", 1));
                if ( CatFile.exists() ) {
                        CCatFile cat;
                        if ( cat.open(CatFile.fullFilename(), this->getAddonDir(), CATREAD_DAT, false) == CATERR_NONE) {
                                Utils::WString fileName = file->filePointer().token(L"::", 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->filePointer());
                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(Utils::WStringList *errors, CProgressInfo *progress)
{
        int files = 0;

        // remove all files
        int max = m_lFiles.size() + _pOriginalFiles->count() + 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
        //TODO: find a better way to do the progress
        files = _pOriginalFiles->restoreAll(progress, files, max);

        // 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
 */
Utils::WString CPackages::getInstallBeforeText(CBaseFile *package) const
{
        return package->installText(m_iLanguage, true);
}
Utils::WString CPackages::getInstallAfterText(CBaseFile *package) const
{
        return package->installText(m_iLanguage, false);
}
Utils::WString CPackages::getUninstallBeforeText(CBaseFile *package) const
{
        return package->uninstallText(m_iLanguage, true);
}
Utils::WString CPackages::getUninstallAfterText(CBaseFile *package) const
{
        return package->uninstallText(m_iLanguage, false);
}

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++);
}

Utils::WString CPackages::findDataDir(const Utils::WString &dir, const Utils::WString &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(L"Data/" + file))
                return Dir.file(L"Data/" + file);
        else if (Dir.exists(L"../" + file))
                return Dir.file(L"../" + file);
        else if (Dir.exists(L"../Data/" + file))
                return Dir.file(L"../Data/" + file);

        return Utils::WString::Null();
}

void CPackages::startup(const Utils::WString &dir, const Utils::WString &tempDir, const Utils::WString &myDoc)
{
        this->setTempDirectory(tempDir);
        this->setMyDocuments(myDoc);

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

        Utils::WString exeFile = this->findDataDir(dir, L"exe");

        // if file exists, read it, otherwise, just add
        if (!exeFile.empty() && CFileIO::Exists(exeFile))
                m_gameExe.readFile(exeFile);
        else
        {
                m_gameExe.parseExe(L"x2.exe|0: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(L"x3.exe|30: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(L"x3tc.exe|35: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(L"x3ap.exe|38:2:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3AP/ModName:X3 Albion Prelude:Egosoft/X3AP:addon!x3tc.exe:3:-1:2.0 The War Continues:-1:2.5 Operation Loose Ends:-1:3.0 Shady Business");
                m_gameExe.parseExe(L"x3fl.exe|39:3:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3FL/ModName:X3 Farnham's Legacy:Egosoft/X3FL:addon2!x3tc.exe:0");
        }
}
void CPackages::startup(const Utils::WString &dir, const Utils::WString &tempDir, const Utils::WString &myDoc, const Utils::WString &mod)
{
        startup(dir, tempDir, myDoc);
        m_sSetMod = mod;
}

int CPackages::getGameLanguage() const
{
        return this->getGameLanguage(m_sCurrentDir);
}
int CPackages::getGameLanguage(const Utils::WString &sDir) const
{
        Utils::WString dir = sDir;
        if (dir.empty())
                dir = m_sCurrentDir;
        else
                dir = this->getProperDir(dir);

        CDirIO Dir(dir);

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

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

                if ( data )
                {
                        Utils::WString str(data);
                        return str.token(L"\n", 1).token(L" ", 1).toLong();
                }
        }

        return 0;
}
Utils::WString CPackages::getGameRunExe(const Utils::WString &dir) const
{
        return m_gameExe.gameRunExe(dir);
}
Utils::WString CPackages::getGameRunExe() const
{
        return m_gameExe.gameRunExe(m_sCurrentDir);
}

Utils::WString CPackages::getGameNameFromType(int game) const
{
        return m_gameExe.gameNameFromType(game - 1);
}
Utils::WString CPackages::getGameName() const
{
        return getGameName(m_sCurrentDir);
}

Utils::WString CPackages::getGameName(const Utils::WString &dir) const
{
        return m_gameExe.gameName(dir.empty() ? m_sCurrentDir : dir);
}

Utils::WString CPackages::getProperDir() const
{
        return getProperDir(m_sCurrentDir);
}
Utils::WString CPackages::getProperDir(const Utils::WString &dir) const
{
        return m_gameExe.properDir((dir.empty()) ? m_sCurrentDir : dir);
}
Utils::WString CPackages::getAddonDir() const
{
        return getAddonDir(m_sCurrentDir);
}
Utils::WString CPackages::getAddonDir(const Utils::WString &dir) const
{
        return m_gameExe.addonDir((dir.empty()) ? m_sCurrentDir : dir);
}
int CPackages::getGameAddons(Utils::WStringList &exes, const Utils::WString &dir) const
{
        return m_gameExe.getGameAddons((dir.empty()) ? m_sCurrentDir : dir, exes);
}
int CPackages::getGameAddons(Utils::WStringList &exes) const
{
        return m_gameExe.getGameAddons(m_sCurrentDir, exes);
}

Utils::WString CPackages::getGameTypesString(CBaseFile *package, bool includeVersion) const
{
        if ( !package->AnyGameCompatability() )
                return L"";

        Utils::WString sGames;
        for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
                if ( !sGames.empty() )
                        sGames += L", ";
                sGames += m_gameExe.gameNameFromType(gNode->Data()->iGame - 1);
                if ( includeVersion ) {
                        Utils::WString version = m_gameExe.gameVersionFromType(gNode->Data()->iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
                        if ( !version.empty() ) {
                                sGames += L" (";
                                sGames += version;
                                sGames += L")";
                        }
                }
        }
        return sGames;
}

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

        return Utils::WString::Null();
}

Utils::WString CPackages::getGameVersionFromType(int game, int version, const Utils::WString &sVersion) const
{
        return m_gameExe.gameVersionFromType(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()->author().Compare(L"PluginManager") )
                        ++count;
        }

        return count;
}

int CPackages::countPackages(int type, bool onlyEnabled) const
{
        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(const Utils::WString &aFilename, const Utils::WString &aTo, const Utils::WString &aDir, const Utils::WString &addon) const
{
        // first check the enabled mod
        Utils::WString dir = aDir;
        if (dir.empty())
                dir = m_sCurrentDir;

        Utils::WString 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(L"cat"))
                                continue;

                        CCatFile catFile;
                        if (catFile.open(file->filePointer(), 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::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat"))
                {
                        CCatFile catFile;
                        if (catFile.open(m_sCurrentDir + L"/mods/" + m_sSetMod + L".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::Exists(dir + L"/" + Utils::WString::PadNumber(catNumber, 2) + L".cat") && 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 + L"/" + Utils::WString::PadNumber(catNumber, 2) + L".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
                wchar_t wareType = CPackages::ConvertWareTypeBack(i);
                Utils::WString wareFile = L"TWare";
                wareFile += (wchar_t)std::toupper(wareType);

                Utils::WString openFile;

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

                if ( !openFile.empty() )
                {
                        // read the file into memory
                        Utils::WStringList wareLines;
                        size_t oldSize = -1;
                        int version = -1;

                        // read first number
                        CFileIO readFile(m_sTempDir + L"/" + wareFile + L".txt");
                        std::vector<Utils::WString> lines;
                        if(readFile.readLines(lines))
                        {
                                for(auto itr = lines.begin(); itr != lines.end(); itr++)
                                {
                                        Utils::WString line = *itr;
                                        line.removeFirstSpace();
                                        line.removeChar(9);
                                        line.removeChar('\r');
                                        if ( line.empty() )
                                                continue;
                                        if ( line[0] == '/' )
                                                continue;

                                        if ( oldSize == -1 )
                                        {
                                                version = line.token(L";", 1).toInt();
                                                oldSize = line.token(L";", 2).toInt();
                                        }
                                        else
                                        {
                                                line.removeEndSpace();
                                                if ( line.back() != L';')
                                                        line += L";";
                                                wareLines.pushBack(line);
                                                if ( wareLines.size() >= oldSize )
                                                        break;
                                        }
                                }

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

                                // add the buffers
                                while ( wareLines.size() < static_cast<size_t>(m_iWareBuffer[i]) )
                                        wareLines.pushBack(L"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(L"27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;" + w->sWareName + L"_DISABLED;");
                                        }
                                        else if ( w->iType == WARETYPE_DELETED || !w->pWare )
                                        {
                                                create = true;
                                                wareLines.pushBack(L"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.size();
                                                long price = this->customWareOveridePrice(w->pWare->sID);
                                                int notority = w->pWare->iNotority;
                                                if ( !this->customWareOverideNoto(w->pWare->sID, &notority) ) notority = w->pWare->iNotority;
                                                if ( !price ) price = w->pWare->iPrice;

                                                wareLines.pushBack(Utils::WString("28;0;0;0;0;") + (long)wareLines.size() + L";" + (long)(w->iText + 3) + L";" + (long)w->pWare->iVolumn + L";" + price + L";1;1;" + (long)w->pWare->iSize + L";" + price + L";" + (long)notority + L";0;0;" + w->sWareName.upper() + L";");
                                        }
                                }

                                if ( create )
                                {
                                        wareLines.pushFront(Utils::WString::Number(version) + L";" + Utils::WString::Number(wareLines.size()) + L";", Utils::WString::Null());
                                        Utils::WString strV = Utils::WString::FromFloat(GetLibraryVersion(), 2);
                                        wareLines.pushFront("// Created by SPKInstaller Libraries V" + strV, Utils::WString::Null());
                                        if ( readFile.writeFile(&wareLines) )
                                                this->packFile(&readFile, L"types\\" + wareFile + L".pck");
                                }
                        }
                        readFile.remove();
                }
        }
}

Utils::WString CPackages::empWaresForGame(size_t *maxsize)
{
        if ( maxsize ) (*maxsize) = 0;

        if ( m_iGame == GAME_X3TC )
        {
                if ( maxsize ) (*maxsize) = EMP_X3TC;
                return GetX3TCEmp();
        }
        else if (m_iGame == GAME_X3AP)
        {
                if (maxsize) (*maxsize) = EMP_X3AP;
                return GetX3TCEmp();
        }
        else if (m_iGame == GAME_X3FL)
        {
                if (maxsize) (*maxsize) = EMP_X3FL;
                return GetX3TCEmp();
        }
        else if ( m_iGame == GAME_X3 )
        {
                if ( maxsize ) (*maxsize) = EMP_X3;
                return GetX3Emp();
        }

        return Utils::WString::Null();
}

void CPackages::_addWareOverride(enum WareTypes type, int pos, const Utils::WString &id, int value, bool noto)
{
        for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
                SWarePriceOverride *wp = node->Data();
                if ( wp->type == type ) {
                        if ( wp->type == Ware_Custom && !wp->id.Compare(id) ) continue;
                        else if ( wp->type != Ware_Custom && wp->pos != pos ) continue;
                        if ( noto ) {
                                wp->notority  = value;
                                wp->bNotority = true;
                        }
                        else wp->relval = value;
                        return;
                }
        }

        SWarePriceOverride *ware = new SWarePriceOverride;
        ware->bNotority = noto;
        ware->notority = (noto) ? value : 0;
        ware->relval = (noto) ? 0 : value;
        ware->type = type;
        ware->pos = pos;
        ware->id = id;

        m_lWarePrices.push_back(ware);
}

void CPackages::addEMPPriceOverride(int empId, int price)
{
        _addWareOverride(Ware_EMP, empId, Utils::WString::Null(), price, false);
}

void CPackages::addEMPNotoOverride(int empId, int noto)
{
        _addWareOverride(Ware_EMP, empId, Utils::WString::Null(), noto, true);
}

void CPackages::addBuiltInWarePriceOverride(int empId, int price)
{
        _addWareOverride(Ware_BuiltIn, empId, Utils::WString::Null(), price, false);
}

void CPackages::addBuiltInWareNotoOverride(int empId, int noto)
{
        _addWareOverride(Ware_BuiltIn, empId, Utils::WString::Null(), noto, false);
}

void CPackages::addCustomWarePriceOverride(const Utils::WString &id, int price)
{
        _addWareOverride(Ware_Custom, 0, id, price, false);
}

void CPackages::addCustomWareNotoOverride(const Utils::WString &id, int noto)
{
        _addWareOverride(Ware_Custom, 0, id, noto, true);
}


void CPackages::createEMPFile(const Utils::WString &progDir)
{
        // do emp wares
        size_t maxsize = 0;
        Utils::WString empWares = empWaresForGame(&maxsize);

        if ( maxsize )
        {
                int e = extractGameFile(L"types/TWareT.pck", m_sTempDir + L"/TWareT.txt");
                if (e)
                {
                        // read the file into memory
                        Utils::WStringList wareLines;
                        size_t oldSize = -1;
                        int version = -1;

                        // read first number
                        CFileIO readFile((e == -1) ? L"TWareT.txt" : m_sTempDir + L"/TWareT.txt");
                        std::vector<Utils::WString> lines;
                        if(readFile.readLines(lines))
                        {
                                for ( int i = 0; i < (int)lines.size(); i++ )
                                {
                                        Utils::WString line(lines.at(i));
                                        line.removeFirstSpace();
                                        line.removeChar('\r');
                                        line.removeChar(9);
                                        if ( line[0] == '/' )
                                                continue;

                                        if ( oldSize == -1 )
                                        {
                                                version = line.token(L";", 1).toLong();
                                                oldSize = line.token(L";", 2).toLong();
                                        }
                                        else
                                        {
                                                line.removeEndSpace();
                                                if ( line.right(1) != L";" )
                                                        line += L";";

                                                // check for any override values for built in wares
                                                if ( (i >= 62 && i <= 81) || (i >= 88 && i <= 93) ) {
                                                        int pos = i - ((i >= 88) ? 88 : 62);
                                                        int price = this->builtInWareOveridePrice(pos);
                                                        if ( price ) {
                                                                line = line.replaceToken(L";", 9, Utils::WString::Number(price));
                                                                line = line.replaceToken(L";", 13, Utils::WString::Number(price));
                                                        }

                                                        int noto = 0;
                                                        if ( this->builtInWareOverideNoto(pos, &noto) ) {
                                                                line = line.replaceToken(L";", 14, Utils::WString::Number(noto));
                                                        }
                                                }

                                                // check for any override values for EMP
                                                if ( i >= 116 ) {
                                                        int price = this->empOveridePrice(i - 116);
                                                        if ( price ) {
                                                                line = line.replaceToken(L";", 9, Utils::WString::Number(price));
                                                                line = line.replaceToken(L";", 13, Utils::WString::Number(price));
                                                        }

                                                        int noto = 0;
                                                        if ( this->empOverideNoto(i - 116, &noto) ) {
                                                                line = line.replaceToken(L";", 14, Utils::WString::Number(noto));
                                                        }
                                                }

                                                wareLines.pushBack(line);
                                                if ( wareLines.size() >= oldSize )
                                                        break;
                                        }
                                }
                        }

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

                        std::vector<Utils::WString> empStr;
                        if(empWares.tokenise(L"\n", empStr))
                        {
                                // apply any price overrides
                                size_t empEntries = empStr.size();
                                for(size_t i = 0; i < empEntries; i++) {
                                        int price = this->empOveridePrice(i);
                                        if ( price ) {
                                                empStr[i] = empStr[i].replaceToken(L";", 9, Utils::WString::Number(price));
                                                empStr[i] = empStr[i].replaceToken(L";", 13, Utils::WString::Number(price));
                                        }

                                        int noto = 0;
                                        if ( this->empOverideNoto(i, &noto) ) {
                                                empStr[i] = empStr[i].replaceToken(L";", 14, Utils::WString::Number(noto));
                                        }
                                }
                                // remove any empty end entries
                                while ( empStr[empEntries - 1].empty() )
                                        --empEntries;

                                Utils::WStringList addAfter;
                                if ( wareLines.size() > maxsize )
                                {
                                        // force emp, remove entries to allow them to be added
                                        if ( m_bForceEMP )
                                        {
                                                // more after emp
                                                if ( wareLines.size() > (maxsize + empEntries) )
                                                {
                                                        for (size_t i = (maxsize + empEntries); i < wareLines.size(); i++)
                                                        {
                                                                auto node = wareLines.get(i);
                                                                addAfter.pushBack(node->str);
                                                        }
                                                }

                                                // no remove them all
                                                while (wareLines.size() > maxsize)
                                                        wareLines.removeAt(wareLines.size() - 1);
                                        }
                                        else if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL) // check if old emp is included, and convert it
                                        {
                                                if ( wareLines.size() > 128 )
                                                {
                                                        Utils::WString test = wareLines.get(128)->str;
                                                        if ( test.tokens(L";", -2).Compare(L"SS_WARE_SW_CUSTOM16_1;") )
                                                        {
                                                                // if theres any at the end, remove the last emp entry
                                                                if ( wareLines.size() > (maxsize + empEntries - 1) )
                                                                {
                                                                        wareLines.removeAt(maxsize + empEntries - 2);
                                                                }
                                                                wareLines.insertAt(128, L"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;128;");
                                                        }
                                                }
                                        }
                                }

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

                                for ( ; i < empEntries; i++ )
                                {
                                        Utils::WString str = empStr[i];
                                        str.removeEndSpace();
                                        str.removeChar(9);
                                        str.removeChar('\r');
                                        if ( str.empty() )
                                                continue;
                                        if ( str.right(1) != L";")
                                                str += L";";
                                        wareLines.pushBack(str);
                                }

                                for(auto afterItr = addAfter.begin(); afterItr != addAfter.end(); afterItr++)
                                        wareLines.pushBack((*afterItr)->str);

                                // finally we write the whole file
                                wareLines.pushFront(Utils::WString::Number(version) + ";" + Utils::WString::Number(wareLines.size()) + L";", Utils::WString::Null());
                                wareLines.pushFront(L"// Created by SPKInstaller Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2), Utils::WString::Null());
                                if ( readFile.writeFile(&wareLines) )
                                        this->packFile(&readFile, L"types\\TWareT.pck");
                        }
                }
        }
}

Utils::WString parseXmlText(const Utils::WString &str)
{
        Utils::WString newStr(str);
        Utils::WStringList changes;

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

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

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

        // restore the commands
        for(auto itr = changes.begin(); itr != changes.end(); itr++)
                newStr = newStr.findReplace((*itr)->data, (*itr)->str);

        return newStr;
}

Utils::WString CPackages::convertTextString(const Utils::WString &sText)
{
        //process any &
        Utils::WString text = parseXmlText(sText);

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

int CPackages::_gameTextNumber() const
{
        int gameNumber = (m_pCurrentGameExe) ? m_pCurrentGameExe->iTextNum : -1;
        if ( gameNumber != -1 ) return gameNumber;

        switch(m_iGame) {
                case GAME_X3: return 30;
                case GAME_X3TC: return 35;
                case GAME_X3AP: return 38;
                case GAME_X3FL: return 39;
                default: return 0;
        }
}

void CPackages::createPluginManagerOpenText()
{
        int gameNumber = _gameTextNumber();

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

        CDirIO Dir(m_sCurrentDir);
        if ( !Dir.exists(L"t") )
                Dir.create(L"t");

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

        std::vector<Utils::WString> writeData;

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

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

        writeData.push_back(L"          <t id=\"99998\">[author]Plugin Manager[/author]It appears the plugin manager hasn't been closed properly.  Make sure its closed before running the game otherwise things may not work correctly</t>");
        writeData.push_back(L"          <t id=\"99999\">2</t>");        
        writeData.push_back(L"  </page>");

        writeData.push_back(L"</language>");
        textFile.writeFileUTF(&writeData);

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

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

void CPackages::CreatePluginManagerText()
{
        int gameNumber = _gameTextNumber();

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

        CDirIO Dir(m_sCurrentDir);
        if ( !Dir.exists(L"t") )
                Dir.create(L"t");

        m_iLastUpdated = (int)time(NULL);

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

        std::vector<Utils::WString> 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(L"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
        writeData.push_back(L"<language id=\"" + Utils::WString::Number(lang) + "\">");

        if ( !gameNumber )
                writeData.push_back(L"  <page id=\"" + Utils::WString::Number(PMTEXTFILE) + L"\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
        else
                writeData.push_back(L"  <page id=\"" + Utils::WString::Number(gameNumber) + Utils::WString::PadNumber(PMTEXTFILE, 4) + L"\" 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(L"          <t id=\"1\">" + Utils::WString::Number(m_iLastUpdated) + L"</t>");
        writeData.push_back(L"          <t id=\"2\">" + Utils::WString::Number(this->countPackages(TYPE_SPK, true)) + L"</t>");
        writeData.push_back(L"          <t id=\"3\">" + Utils::WString::Number(start) + L"</t>");
        writeData.push_back(L"          <t id=\"4\">" + Utils::WString::Number(lWares.size()) + L"</t>");
        writeData.push_back(L"          <t id=\"6\">" + Utils::WString::Number(lShips.size()) + L"</t>");

        // write some generic texts
        writeData.push_back(L"          <t id=\"110\">" + Utils::WString::Number((long)m_iLanguage) + L"</t>");
        writeData.push_back(L"          <t id=\"109\">Plugin Manager: \\033GPoll Gui Data\\033X</t>");
        writeData.push_back(L"          <t id=\"107\">Plugin Manager: \\033GExport Game Data\\033X </t>");
        writeData.push_back(L"          <t id=\"100\">\\n</t>");
        writeData.push_back(L"          <t id=\"101\">\\033B</t>");
        writeData.push_back(L"          <t id=\"102\">\\033G</t>");
        writeData.push_back(L"          <t id = \"103\">\\033B</t>");
        writeData.push_back(L"          <t id=\"104\">\\033X</t>");
        writeData.push_back(L"          <t id=\"105\">\\033Y</t>");
        writeData.push_back(L"          <t id=\"106\">\\033C</t>");
        writeData.push_back(L"          <t id=\"108\">\\033</t>");

        writeData.push_back(L"          <t id=\"99998\">[author]Plugin Manager[/author]It appears the plugin manager hasn't been closed properly.  Make sure its closed before running the game otherwise things may not work correctly</t>");
        writeData.push_back(L"          <t id=\"99999\">1</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
                Utils::WString textEntries;
                int textCount = 0;
                C_File *f = p->GetFirstFile(FILETYPE_TEXT);
                while ( f )
                {
                        Utils::WString sLang;
                        Utils::WString id;
                        if ( m_iGameFlags & EXEFLAG_TCTEXT )
                        {
                                id = f->baseName().token(L"-", 1);
                                sLang = f->baseName().token(L"-", 2);
                                if ( sLang.empty() )
                                        sLang = L"NULL";
                                else
                                        sLang = sLang.erase(0, 1);  // remove the "L"
                        }
                        else
                        {
                                sLang = f->baseName().left((int)f->baseName().length() - 4).padNumber(3);
                                id = f->baseName().mid(((int)f->baseName().length() - 4) + 1, 4);
                        }

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

                Utils::WString sTextCount((long)textCount);
                writeData.push_back(L"          <t id=\"" + Utils::WString::Number((long)start) + L"\">" + this->convertTextString(p->name()) + L"</t>");
                writeData.push_back(L"          <t id=\"" + Utils::WString::Number((long)(start + 1)) + L"\">" + this->convertTextString(p->author()) + L"</t>");
                writeData.push_back(L"          <t id=\"" + Utils::WString::Number((long)(start + 2)) + L"\">" + this->convertTextString(p->version()) + L"</t>");
                writeData.push_back(L"          <t id=\"" + Utils::WString::Number((long)(start + 3)) + L"\">" + this->convertTextString(p->name(lang)) + L"</t>");

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

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

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

                start += 10;
        }

        // write ware names
        if ( lWares.size() )
        {
                writeData.push_back(L"          <t id=\"5\">" + Utils::WString::Number(start) + L"</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(Utils::WString(L"           <t id=\"") + (long)start + L"\">" + this->convertTextString(w->sWareName) + L"</t>");
                        else
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)start + L"\">-1</t>");
                        writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(start + 1) + L"\">" + Utils::WString(w->cType) + L"</t>");
                        writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(start + 2) + L"\">" + (long)w->iPos + L"</t>");
                        start += 10;
                }
        }

        if ( lShips.size() )
        {
                writeData.push_back(L"          <t id=\"7\">" + Utils::WString::Number(start) + L"</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(Utils::WString(L"           <t id=\"") + (long)start + L"\">" + gs->sShipID + L"</t>");
                        else
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)start + L"\">-1</t>");
                        writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(start + 1) + L"\">" + (long)gs->iPos + L"</t>");
                        writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(start + 2) + L"\">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(Utils::WString(L"           <t id=\"") + (long)(doStart) + L"\">" + ((gs->pPackage->IsShipyard(i)) ? Utils::WString::Number(1) : Utils::WString::Number(0)) + L"</t>");
                                        ++doStart;
                                }
                        }

                        start += 20;
                }
        }

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

        // do emp
        if (m_iGame == GAME_X3TC || m_iGame == GAME_X3 || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL)
        {
                writeData.push_back(L"  <page id=\"17\" title=\"Plugin Manager Objects\">");
                writeData.push_back(GetEMPText());
                writeData.push_back(L"  </page>");
        }

        // wares
        if ( m_iGame == GAME_X3AP || m_iGame == GAME_X3TC || m_iGame == GAME_X3FL || m_iGame == GAME_X3 || lWares.size() || lShips.size() )
        {

                if ( !gameNumber )
                        writeData.push_back(L"  <page id=\"17\" title=\"Plugin Manager Objects\">");
                else
                        writeData.push_back(Utils::WString(L"  <page id=\"") + (long)gameNumber + L"0017\" title=\"Plugin Manager Objects\">");

                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(SHIPSTARTTEXT - 1) + L"\">ZZ_BLANKSHIP</t>");

                // 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
                        Utils::WString name = CSpkFile::GetWareText(w->pWare, m_iLanguage);
                        Utils::WString desc = CSpkFile::GetWareDesc(w->pWare, m_iLanguage);
                        if ( !name.empty() )
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(w->iText + 3) + L"\">" + this->convertTextString(name) + L"</t>");
                        if ( !desc.empty() )
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(w->iText + 4) + L"\">" + this->convertTextString(desc) + L"</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;

                        Utils::WString name = s->pPackage->textName(m_iLanguage);
                        Utils::WString desc = s->pPackage->textDescription(m_iLanguage);
                        if ( !name.empty() )
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)s->iText + L"\">" + this->convertTextString(name) + L"</t>");
                        if ( !desc.empty() )
                                writeData.push_back(Utils::WString(L"           <t id=\"") + (long)(s->iText + 1) + L"\">" + this->convertTextString(desc) + L"</t>");
                }
                writeData.push_back(L"  </page>");
        }
        writeData.push_back(L"</language>");
        textFile.writeFileUTF(&writeData);

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

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

bool CPackages::isCurrentDir(const Utils::WString &dir) const
{
        Utils::WString cur = m_sCurrentDir;

        if ( dir.Compare(cur) )
                return true;

        Utils::WString checkDir = cur;
        checkDir = checkDir.findReplace(L"/", L"\\");
        if ( checkDir.Compare(dir) )
                return true;

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

        return false;
}

void CPackages::backupSaves(bool vanilla)
{
        if (!_sSaveDir.empty())
        {
                // copy any saves into the vanilla directory
                Utils::WString dir = (vanilla) ? L"Vanilla" : L"Modified";

                // make sure the directory exists
                CDirIO saveDir(this->saveDirectory());
                CDirIO gameSaveDir(saveDir.dir(_sSaveDir));

                if (!gameSaveDir.exists())
                        gameSaveDir.create();
                if (!gameSaveDir.exists(dir))
                        gameSaveDir.create(dir);
                gameSaveDir.cd(dir);

                // backup the saves
                Utils::WStringList files;
                if(saveDir.dirList(files, Utils::WString::Null(), L"*.sav"))
                {
                        for(auto itr = files.begin(); itr != files.end(); ++itr)
                        {
                                CFileIO File(saveDir.file((*itr)->str));
                                if (!File.isFileExtension(L"sav"))
                                        continue;
                                // remove the file if already exists
                                if (gameSaveDir.exists((*itr)->str))
                                        CFileIO::Remove(gameSaveDir.file((*itr)->str));

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

void CPackages::restoreSaves(bool vanilla)
{
        // get dir to restore from
        if (!_sSaveDir.empty())
        {
                Utils::WString dir = (vanilla) ? L"Vanilla" : L"Modified";
                CDirIO toDir(this->saveDirectory());
                CDirIO restoreDir(toDir.dir(_sSaveDir));
                restoreDir.cd(dir);

                if (restoreDir.exists())
                {
                        //if we are in vanilla mode, we should remove the saves (so we only have vanilla saves
                        /*
                        if (vanilla) {
                                
                                Utils::WStringList *files = toDir.DirList();
                                if (files) {
                                        for (SStringList *node = files->Head(); node; node = node->next)
                                        {
                                                CFileIO saveFile(toDir.File(node->str));
                                                if (saveFile.extension().Compare("sav")) {
                                                        saveFile.remove();
                                                }
                                        }
                                        delete files;
                                }
                        }
                        */

                        // now we copy of the backed up save games
                        Utils::WStringList files;
                        if(restoreDir.dirList(files, Utils::WString::Null(), L"*.sav"))
                        {
                                for(auto itr = files.begin(); itr != files.end(); itr++)
                                {
                                        CFileIO File(restoreDir.file((*itr)->str));
                                        // remove the file if already exists
                                        if (toDir.exists((*itr)->str)) CFileIO::Remove(toDir.file((*itr)->str));

                                        // move file over
                                        File.copy(toDir.file((*itr)->str), true);
                                }
                        }
                }
        }
}

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

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

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

        this->Reset();
        m_bLoaded = false;

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

        // remove the plugin manager mod files
        if ( Dir.exists(L"mods/PluginManager.cat") )    CFileIO::Remove(Dir.file(L"mods/PluginManager.cat"));
        if ( Dir.exists(L"mods/PluginManager.dat") )    CFileIO::Remove(Dir.file(L"mods/PluginManager.dat"));

        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(L"types/Dummies.pck", m_sTempDir + L"/Dummies.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.open((e == -1) ? L"Dummies.txt" : m_sTempDir + L"/Dummies.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int insection = 0;
                                SDummyEntry *currentSection = NULL;
                                for ( int j = 0; j < (int)lines.size(); j++ )
                                {
                                        Utils::WString line(lines.at(j));
                                        line.removeChar(9);
                                        line.removeChar('\r');
                                        line.removeFirstSpace();
                                        line.removeEndSpace();
                                        if ( line.empty() )
                                                continue;
                                        if ( line[0] == '/' )
                                                continue;

                                        // read the section, first entry is section, second is size
                                        while ( !line.empty() )
                                        {
                                                if ( !insection )
                                                {
                                                        Utils::WString section = line.token(L";", 1);
                                                        insection = line.token(L";", 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.remTokens(L";", 1, 2);
                                                }
                                                else
                                                {
                                                        --insection;
                                                        // check the last entry for number of states
                                                        if ( currentSection->sSection.Compare(L"SDTYPE_GUN") )
                                                        {
                                                                int states = line.token(L";", 3).toInt();
                                                                int parts = line.token(L";", 4 + (states * 2)).toInt();
                                                                Utils::WString data = line.tokens(L";", 1, 4 + (states * 2) + (parts * 2)) + L";";
                                                                currentSection->lEntries.pushBack(data);

                                                                // remove done
                                                                line = line.remTokens(L";", 1, 4 + (states * 2) + (parts * 2));
                                                        }
                                                        else
                                                        {
                                                                int states = line.token(L";", 3).toInt();
                                                                Utils::WString data = line.tokens(L";", 1, 3 + (states * 2)) + L";";
                                                                currentSection->lEntries.pushBack(data);

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

                        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(auto itr = found->lEntries.begin(); itr != found->lEntries.end(); itr++)
                                        {
                                                if ((*itr)->str.token(L";", 1).Compare(dummy->sData.token(L";", 1)))
                                                {
                                                        f = true;
                                                        break;
                                                }
                                        }

                                        if ( f )
                                                continue;
                                }

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

                // finally, write the file
                std::vector<Utils::WString> lines;
                lines.push_back(L"// Dummies file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
                for ( SDummyEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
                {
                        lines.push_back(L"");
                        lines.push_back(L"// Section: " + dummy->sSection + L" Entries: " + Utils::WString::Number((long)dummy->lEntries.size()));
                        lines.push_back(dummy->sSection + L";" + Utils::WString::Number(dummy->lEntries.size()) + L";");
                        for(auto itr = dummy->lEntries.begin(); itr != dummy->lEntries.end(); itr++)
                        {
                                Utils::WString strLine = (*itr)->str;
                                strLine.removeChar(9);
                                strLine.removeChar('\r');
                                strLine.removeEndSpace();
                                strLine.removeFirstSpace();
                                strLine = strLine.findReplace(L"<::PiPe::>", L"|");
                                if ( strLine.right(1) != L";" )
                                        strLine += L";";
                                lines.push_back(strLine);
                        }
                }
                lines.push_back(L"");

                // write the file to disk
                CFileIO WriteFile(m_sTempDir + L"/dummies.txt");
                if ( WriteFile.writeFile(lines) )
                {
                        this->packFile(&WriteFile, L"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;

        std::vector<Utils::WString> cutList;
        int e = extractGameFile(L"types/CutData.pck", m_sTempDir + L"/CutData.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.open((e == -1) ? L"CutData.txt" : m_sTempDir + L"/CutData.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int entries = -1;
                                for ( int j = 0; j < (int)lines.size(); j++ )
                                {
                                        Utils::WString line(lines.at(j));
                                        line.removeChar(9);
                                        line.removeChar('\r');
                                        line.removeChar(' ');
                                        if ( line.empty() || line[0] == '/' )
                                                continue;
                                        if ( entries == -1 )
                                                entries = line.token(L";", 1).toInt();
                                        else
                                        {
                                                if ( line.right(1) != L";" )
                                                        line += L";";
                                                cutList.push_back(line);
                                                if ( static_cast<int>(cutList.size()) == entries)
                                                        break;
                                        }
                                }
                        }

                        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
                auto& list = s->pPackage->getCutData();
                for(auto itr = list.begin(); itr != list.end(); itr++)
                {
                        Utils::WString str = (*itr)->str;
                        str.removeChar(' ');
                        if ( str.right(1) != L";" )
                                str += L";";
                        if(std::find(cutList.begin(), cutList.end(), str) == cutList.end())
                                cutList.push_back(str);
                }
        }

        cutList.insert(cutList.begin(), Utils::WString::Number(cutList.size()) + ";");
        cutList.insert(cutList.begin(), L"/cut id;filename (leave blank to use id)");
        cutList.insert(cutList.begin(), L"// Cut Data file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + L"/CutData.txt");
        if ( WriteFile.writeFile(cutList) )
        {
                this->packFile(&WriteFile, L"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;

        Utils::WStringList aniList;
        int e = extractGameFile(L"types/Animations.pck", m_sTempDir + L"/Animations.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.open((e == -1) ? L"Animations.txt" : m_sTempDir + L"/Animations.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                for ( int j = 0; j < (int)lines.size(); j++ )
                                {
                                        Utils::WString line(lines.at(j));
                                        aniList.pushBack(line);
                                }
                        }

                        File.remove();
                }
        }

        Utils::WStringList 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(auto itr = s->pPackage->getAnimations().begin(); itr != s->pPackage->getAnimations().end(); itr++)
                        parsedAniList.pushBack((*itr)->str);
        }

        // format the list with added spaces
        Utils::WStringList formatedAniList;
        int lineCount = -1;
        for(auto itr = parsedAniList.begin(); itr != parsedAniList.end(); itr++)
        {
                // format the comment to match the line number
                lineCount++;
                Utils::WString oldComment = (*itr)->str.tokens(L"//", 2);
                Utils::WString comment = L"//" + Utils::WString::Number(lineCount);
                if (!oldComment.empty())
                {
                        comment += L" ";
                        oldComment.removeFirstSpace();
                        if ( oldComment.token(L" ", 1).isNumber() )
                                comment += oldComment.tokens(L" ", 2);
                        else
                                comment += oldComment;
                }
                Utils::WString line = (*itr)->str.token(L"//", 1);

                // split into seperate lines
                Utils::WString first = line.token(L";", 1);
                if ( first.Compare(L"TAT_TAGSINGLESTEP") )
                {
                        formatedAniList.pushBack(line.tokens(L";", 1, 5) + L";");
                        std::vector<Utils::WString> sLines;
                        if(line.tokens(L";", 6).tokenise(L";", sLines))
                        {
                                size_t max = sLines.size();
                                if ( sLines[max - 1].empty() )
                                        --max; // remove the last ";"

                                for (size_t i = 0; i < max; i++ )
                                {
                                        Utils::WString l = L"\t" + sLines[i] + L";";
                                        if ( i == (max - 1) )
                                                formatedAniList.pushBack(l + comment);
                                        else
                                                formatedAniList.pushBack(l);
                                }
                        }
                }
                else if ( (first.Compare(L"TAT_TAGONESHOT") || first.Compare(L"TAT_TAGLOOP")) && (line.contains(L"TATF_COORDS")) )
                {
                        formatedAniList.pushBack(line.tokens(L";", 1, 5) + L";");
                        std::vector<Utils::WString> sLines;
                        if (line.tokens(L";", 6).tokenise(L";", sLines))
                        {
                                size_t max = sLines.size();
                                if ( sLines[max - 1].empty() )
                                        --max; // remove the last ";"

                                Utils::WString prevLine;
                                for (size_t i = 0; i < max; i++ )
                                {
                                        Utils::WString l = sLines[i] + L";";
                                        if ( l.contains(L"TATF_COORDS") && !prevLine.empty() )
                                        {
                                                formatedAniList.pushBack(L"\t" + prevLine);
                                                prevLine = L"";
                                        }
                                        prevLine += l;
                                }

                                if ( !prevLine.empty() )
                                        formatedAniList.pushBack(L"\t" + prevLine + comment);

                        }
                }
                else
                        formatedAniList.pushBack(line + comment);
        }

        formatedAniList.pushFront(Utils::WString::Number(parsedAniList.size()) + L";");
        formatedAniList.pushFront(L"// Animations, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + L"/Animations.txt");
        if ( WriteFile.writeFile(&formatedAniList) )
        {
                this->packFile(&WriteFile, L"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(L"types/Bodies.pck", m_sTempDir + L"/Bodies.txt");
        if ( e )
        {
                CFileIO File;
                if ( File.open((e == -1) ? L"Bodies.txt" : m_sTempDir + L"/Bodies.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int entries = 0;
                                for (size_t j = 0; j < lines.size(); j++ )
                                {
                                        Utils::WString line(lines.at(j));
                                        line.removeChar(' ');
                                        line.removeChar(9);
                                        if ( line.empty() || line[0] == '/' )
                                                continue;
                                        if ( entries <= 0 )
                                        {
                                                entries = line.token(L";", 2).toInt();
                                                currentSection = new SBodies;
                                                currentSection->sSection = line.token(L";", 1);
                                                bodiesList.push_back(currentSection);
                                        }
                                        else if ( currentSection )
                                        {
                                                std::vector<Utils::WString> strs;
                                                if(line.tokenise(L";", strs))
                                                {
                                                        for (size_t i = 0; i < strs.size(); i++)
                                                        {
                                                                if ( strs[i].empty() )
                                                                        continue;
                                                                if(!currentSection->lEntries.contains(strs[i] + L";"))
                                                                        currentSection->lEntries.pushBack(strs[i] + L";");
                                                                --entries;
                                                        }
                                                }
                                        }
                                }
                        }
                        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(auto itr = s->pPackage->getBodies().begin(); itr != s->pPackage->getBodies().end(); itr++)
                {
                        Utils::WString section = (*itr)->str.token(L";", 1);
                        Utils::WString body = (*itr)->str.tokens(L";", 2).remove(' ');
                        if ( body.right(1) != L";" )
                                body += L";";

                        // 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);
                        }
                        if(!foundSection->lEntries.contains(body))
                                foundSection->lEntries.pushBack(body);
                }
        }

        // now write the file
        std::vector<Utils::WString> writeList;
        // the header first
        writeList.push_back(L"// Bodies file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
        writeList.push_back(L"//body type;num bodies;");
        writeList.push_back(L"//[body id/name]");

        // now our sections
        for ( SBodies *bSection = bodiesList.First(); bSection; bSection = bodiesList.Next() )
        {
                writeList.push_back(L"");
                writeList.push_back(L"// Section: " + bSection->sSection);
                writeList.push_back(bSection->sSection + L";" + Utils::WString::Number(bSection->lEntries.size()) + L";");
                for(auto itr = bSection->lEntries.begin(); itr != bSection->lEntries.end(); itr++)
                {
                        Utils::WString str = (*itr)->str;
                        str.removeChar(9);
                        str.removeChar(' ');
                        if ( str.right(1) != L";" )
                                str += L";";
                        writeList.push_back(str);
                }
        }

        // write the file to disk
        CFileIO WriteFile(m_sTempDir + L"/Bodies.txt");
        if ( WriteFile.writeFile(writeList) )
        {
                this->packFile(&WriteFile, L"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
                Utils::WString name = p->customStartName();
                if ( name.empty() )
                        continue;

                // find if maps file exists
                Utils::WStringList createFiles;
                createFiles.pushBack(name, L"maps/x3_universe");
                createFiles.pushBack(name, L"types/Jobs");
                createFiles.pushBack(name, L"types/JobWings");

                for(auto itr = createFiles.begin(); itr != createFiles.end(); itr++)
                {
                        Utils::WString dir = CFileIO((*itr)->data).dir();
                        FileType type = FileType::FILETYPE_EXTRA;
                        if ( dir.Compare(L"maps") )
                                type = FileType::FILETYPE_MAP;

                        if ( !p->findFile((*itr)->str + L".xml", type) && !p->findFile((*itr)->str + L".pck", type))
                        {
                                // create a maps files
                                int e = this->extractGameFile((*itr)->data + L".pck", m_sTempDir + L"/" + (*itr)->data + L".pck");
                                if ( e )
                                {
                                        CFileIO File((e == -1) ? ((*itr)->data + L".pck") : (m_sTempDir + L"/" + (*itr)->data + L".pck"));
                                        if ( File.exists() )
                                        {
                                                File.Rename(m_sCurrentDir + L"/" + dir + L"/" + (*itr)->str + L".pck");
                                                this->addCreatedFile(dir + L"/" + (*itr)->str + L".pck");
                                        }
                                }
                        }
                }
        }
}

void CPackages::addCreatedFile(const Utils::WString &sFile)
{
        Utils::WString file = sFile.findRemove(m_sCurrentDir);
        while ( file[0] == '/' )
                file.erase(0, 1);
        while ( file[0] == '\\' )
                file.erase(0, 1);

        if(!_lCreatedFiles.contains(file, true))
                _lCreatedFiles.pushBack(file);
}

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(L"types/Components.pck", m_sTempDir + L"/Components.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.open((e == -1) ? L"Components.txt" : m_sTempDir + L"/Components.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int insection = 0;
                                int insubsection = 0;
                                SComponantEntry *currentSection = NULL;
                                SComponantEntry2 *currentSubSection = NULL;
                                for ( int j = 0; j < (int)lines.size(); j++ )
                                {
                                        Utils::WString 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 )
                                                {
                                                        Utils::WString section = line.token(L";", 1);
                                                        insection = line.token(L";", 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.remTokens(L";", 1, 2);
                                                }
                                                else if ( !insubsection )
                                                {
                                                        --insection;
                                                        Utils::WString section = line.token(L";", 1);
                                                        insubsection = line.token(L";", 2).toInt();

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

                                                        line = line.remTokens(L";", 1, 2);
                                                }
                                                else
                                                {
                                                        --insubsection;
                                                        line = line.remove(' ');
                                                        if(!currentSubSection->lEntries.contains(line))
                                                                currentSubSection->lEntries.pushBack(line);
                                                        line = L"";
                                                }
                                        }
                                }
                        }

                        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(auto itr = found2->lEntries.begin(); itr != found2->lEntries.end(); itr++)
                                                {
                                                        if ( dummy->sData.remove(' ').Compare((*itr)->str) )
                                                        {
                                                                f = true;
                                                                break;
                                                        }
                                                }

                                                if ( f )
                                                        continue;
                                        }
                                }

                                found2->lEntries.pushBack(dummy->sData.remove(' '));
                        }
                }

                // finally, write the file
                std::vector<Utils::WString> lines;
                lines.push_back(L"// Components file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
                for ( SComponantEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
                {
                        lines.push_back(L"");
                        lines.push_back(L"// Section: " + dummy->sSection + L" Entries: " + Utils::WString::Number((long)dummy->lEntries.size()));
                        lines.push_back(dummy->sSection + L";" + Utils::WString::Number(dummy->lEntries.size()) + L";");
                        for ( CListNode<SComponantEntry2> *comp = dummy->lEntries.Front(); comp; comp = comp->next() )
                        {
                                lines.push_back(comp->Data()->sSection + L";" + Utils::WString::Number((long)comp->Data()->lEntries.size()) + L";");
                                for(auto itr = comp->Data()->lEntries.begin(); itr != comp->Data()->lEntries.end(); itr++)
                                {
                                        Utils::WString cStr = (*itr)->str;
                                        cStr.removeEndSpace();
                                        cStr.removeChar(9);
                                        cStr.removeChar('\r');
                                        if ( cStr.right(1) != L";" )
                                                cStr += L";";
                                        lines.push_back(cStr);
                                }
                        }
                }

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

bool CPackages::readWares(int iLang, CLinkList<SWareEntry> &list)
{
        if ( iLang == 0 ) iLang = m_iLanguage;

        Utils::WString empWares = this->empWaresForGame();

        for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
                if ( !node->Data()->IsEnabled() ) continue;
                node->Data()->readWares(iLang, list, empWares);
        }

        return true;
}

bool CPackages::readCommands(int iLang, CLinkList<SCommandSlot> &list)
{
        if ( iLang == 0 ) iLang = m_iLanguage;

        for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
                if ( !node->Data()->IsEnabled() ) continue;
                node->Data()->readCommands(iLang, list);
        }

        return true;
}

bool CPackages::readWingCommands(int iLang, CLinkList<SCommandSlot> &list)
{
        if ( iLang == 0 ) iLang = m_iLanguage;

        for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
                if ( !node->Data()->IsEnabled() ) continue;
                node->Data()->readWingCommands(iLang, list);
        }

        return true;
}

int CPackages::_warePriceOverride(enum WareTypes type, int pos, const Utils::WString &id)
{
        for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
                if ( node->Data()->type == type ) {
                        if ( node->Data()->type == Ware_Custom && id.Compare(node->Data()->id) ) return node->Data()->relval;
                        else if ( node->Data()->type != Ware_Custom && node->Data()->pos == pos ) return node->Data()->relval;
                }
        }
        return 0;
}

bool CPackages::_wareNotoOverride(enum WareTypes type, int pos, const Utils::WString &id, int *noto)
{
        for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
                if ( node->Data()->type == type && node->Data()->bNotority ) {
                        if ( node->Data()->type == Ware_Custom && !id.Compare(node->Data()->id) ) continue;
                        else if ( node->Data()->type != Ware_Custom && node->Data()->pos != pos ) continue;
                
                        (*noto) = node->Data()->notority;
                        return true;
                }
        }
        return false;
}

void CPackages::_removeWareOverride(enum WareTypes type, int pos, const Utils::WString &id)
{
        for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
                if ( node->Data()->type == type ) {
                        if ( node->Data()->type == Ware_Custom && !id.Compare(node->Data()->id) ) continue;
                        else if ( node->Data()->type != Ware_Custom && node->Data()->pos != pos ) continue;
                        m_lWarePrices.remove(node);
                        break;
                }
        }
}

int CPackages::empOveridePrice(int id)
{
        return _warePriceOverride(Ware_EMP, id, Utils::WString::Null()); 
}

bool CPackages::empOverideNoto(int id, int *noto)
{
        return _wareNotoOverride(Ware_EMP, id, Utils::WString::Null(), noto);
}

int CPackages::builtInWareOveridePrice(int id)
{
        return _warePriceOverride(Ware_BuiltIn, id, Utils::WString::Null()); 
}

bool CPackages::builtInWareOverideNoto(int id, int *noto)
{
        return _wareNotoOverride(Ware_BuiltIn, id, Utils::WString::Null(), noto);
}

int CPackages::customWareOveridePrice(const Utils::WString &id)
{
        return _warePriceOverride(Ware_Custom, 0, id); 
}

bool CPackages::customWareOverideNoto(const Utils::WString &id, int *noto)
{
        return _wareNotoOverride(Ware_Custom, 0, id, noto);
}

void CPackages::removeEmpOverride(int pos)
{
        _removeWareOverride(Ware_EMP, pos, Utils::WString::Null());
}

void CPackages::removeBuiltinWareOverride(int pos)
{
        _removeWareOverride(Ware_BuiltIn, pos, Utils::WString::Null());
}

void CPackages::removeCustomWareOverride(const Utils::WString &id)
{
        _removeWareOverride(Ware_Custom, 0, id);
}


bool CPackages::readGlobals(Utils::WStringList &globals) const
{
        int e = extractGameFile(L"types/Globals.pck", m_sTempDir);
        if ( e )
        {
                CFileIO File((e == -1) ? L"Globals.txt" : m_sTempDir + L"/Globals.txt");
                if ( File.exists() )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int entries = -1;
                                for(auto itr = lines.begin(); itr != lines.end(); itr++)
                                {
                                        Utils::WString str = itr->remove('\r').remove(9);
                                        str.removeFirstSpace();

                                        if ( str.empty() )
                                                continue;
                                        if ( str[0] == L'/' )
                                                continue;

                                        // remove comments
                                        if (str.contains(L"/") ) 
                                                str = str.token(L"/", 1);

                                        if ( entries == -1 )
                                                entries = str.token(L";", 1).toInt();
                                        else
                                                globals.pushBack(str.token(L";", 1), str.token(L";", 2));
                                }

                                return true;
                        }
                }
        }

        return false;
}

void CPackages::CreateGlobals()
{
        if ( _lGlobals.empty() )
                return; // no global settings

        Utils::WStringList globals;
        if (readGlobals(globals))
        {
                // apply out settings
                for(auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
                        globals.changeData((*itr)->str, (*itr)->data);

                // now write it
                Utils::WStringList writeList;
                for(auto itr = globals.begin(); itr != globals.end(); itr++)
                        writeList.pushBack((*itr)->str + L";" + (*itr)->data + L";");
                
                // finally, write the file
                writeList.pushFront(Utils::WString::Number(writeList.size()) + L"; /globals amount", L"");
                writeList.pushFront(L"// Globals file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2), L"");

                CFileIO WriteFile(m_sTempDir + L"/Globals.txt");
                if ( WriteFile.writeFile(&writeList) )
                {
                        this->packFile(&WriteFile, L"types/Globals.pck");
                        WriteFile.remove();
                }
        }
}

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

        // get the cockpit list to match with ships turrets
        Utils::WStringList Cockpits;
        Utils::WStringList cockpitList;
        if(_createCockpits(cockpitList))
        {
                for(auto itr = cockpitList.begin(); itr != cockpitList.end(); itr++)
                {
                        Utils::WString id = (*itr)->str.token(L";", 19);
                        Cockpits.pushBack(id);
                }
        }

        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(L"types/TShips.pck", m_sTempDir + L"/TShips.txt");
        if ( e )
        {
                int fileType = 51;
                std::vector<Utils::WString> tshipsList;

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

                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int count = -1;
                                for ( int j = 0; j < (int)lines.size(); j++ )
                                {
                                        Utils::WString 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.token(L";", 1).toInt();
                                                count = line.token(L";", 2).toInt();
                                        }
                                        else
                                        {
                                                if ( line.right(1) != L";" )
                                                        line += L";";

                                                // 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->shipID().Compare(shipData.sID))
                                                                                continue;
                                                                        s->iText = shiptext;
                                                                        if ( !s->pPackage->GetOriginalDescription() )
                                                                                shiptext += 2;
                                                                        s->iPos = tshipsList.size();
                                                                        added = true;
                                                                        tshipsList.push_back(s->pPackage->formatShipData(Cockpits, &s->iText, m_iGame));
                                                                        shipOverrides.remove(node);
                                                                        break;
                                                                }
                                                        }
                                                }

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

                        File.remove();

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

                        Utils::WString bufferStart;
                        if ( m_iGame == GAME_X3 )
                                bufferStart = L"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 = L"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.size(); i < m_iShipBuffer; i++ )
                                tshipsList.push_back(bufferStart + L"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.size();
                                if ( s->iType == WARETYPE_ADDED && s->pPackage )
                                {
                                        s->iText = shiptext;
                                        if ( !s->pPackage->GetOriginalDescription() )
                                                shiptext += 2;

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

                        // finally, write the file
                        tshipsList.insert(tshipsList.begin(), Utils::WString::Number(fileType) + L";" + Utils::WString::Number(tshipsList.size()) + L";");
                        tshipsList.insert(tshipsList.begin(), L"// TShips file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));

                        CFileIO WriteFile(m_sTempDir + L"/TShips.txt");
                        if ( WriteFile.writeFile(tshipsList) )
                        {
                                this->packFile(&WriteFile, L"types/TShips.pck");
                                WriteFile.remove();
                        }
                }
        }
}

bool CPackages::packFile(const Utils::WString &filename) const
{
        // 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 )
                {
                        Utils::WString ext = L"pck";
                        if ( File.isFileExtension(L"bob") )
                                ext = L"pbb";
                        else if ( File.isFileExtension(L"bod") )
                                ext = L"pbd";
                        CFileIO pckFile(File.changeFileExtension(ext));
                        if ( !CDirIO(pckFile.dir()).exists() )
                                CDirIO(pckFile.dir()).create();
                        pckFile.WriteData((char *)pckData, newFileSize);
                        return true;
                }
        }

        return false;
}

bool CPackages::unPackFile(const Utils::WString &filename, bool checkxml) const
{
        // 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, false);
                if ( !pckData )
                        pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, true);

                if ( pckData )
                {
                        Utils::WString ext = L"txt";
                        if ( File.isFileExtension(L"pbb") )
                                ext = L"bob";
                        else if ( File.isFileExtension(L"pbd") )
                                ext = L"bod";
                        CFileIO pckFile(File.changeFileExtension(ext));
                        if ( !CDirIO(pckFile.dir()).exists() )
                                CDirIO(pckFile.dir()).create();
                        pckFile.WriteData((char *)pckData, newFileSize);

                        // check for xml and rename
                        if ( checkxml )
                        {
                                int readmaxlines = 20;
                                bool isxml = false;
                                do {
                                        Utils::WString line = pckFile.readEndOfLine();
                                        if ( line.contains(L"<language id=") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        else if ( line.contains(L"<page id=") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        else if ( line.contains(L"<?xml") || line.contains(L"<script>") )
                                        {
                                                isxml = true;
                                                break;
                                        }
                                        --readmaxlines;
                                        if ( readmaxlines <= 0 )
                                                break;
                                } while (pckFile.isOpened());

                                if ( pckFile.isOpened() )
                                        pckFile.close();

                                if ( isxml )
                                        pckFile.Rename(pckFile.changeFileExtension(L"xml"));
                        }

                        return true;
                }
        }

        return false;
}

bool CPackages::packFile(CFileIO* File, const Utils::WString &sFilename) const
{
        Utils::WString filename = sFilename.findReplace(L"\\", L"/");
        if ( m_iGame == GAME_X3 )
        {
                CCatFile catFile;
                int error = catFile.open(m_sCurrentDir + L"/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->fullFilename(), filename, true, true) )
                                return false;
                        return true;
                }
        }
        else
        {
                // compress the file
                size_t fileSize;
                char *fileData = CFileIO(File->fullFilename()).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 + L"/" + filename);
                                if ( !CDirIO(pckFile.dir()).exists() )
                                        CDirIO(pckFile.dir()).create();
                                pckFile.WriteData((char *)pckData, newFileSize);
                                const_cast<CPackages *>(this)->addCreatedFile(pckFile.fullFilename());
                                return true;
                        }
                }
        }

        return false;
}

size_t CPackages::_createCockpits(Utils::WStringList &list)
{
        // first check we have any ships
        if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
                return NULL;

        // now extract the existing cockpits
        int fileType = 51;
        int e = extractGameFile(L"types/TCockpits.pck", m_sTempDir + L"/TCockpits.txt");
        if ( e )
        {
                // read the dummies
                CFileIO File;
                if ( File.open((e == -1) ? L"TCockpits.txt" : m_sTempDir + L"/TCockpits.txt") )
                {
                        std::vector<Utils::WString> lines;
                        if(File.readLines(lines))
                        {
                                int count = -1;
                                for(auto itr = lines.begin(); itr != lines.end(); itr++)
                                {
                                        Utils::WString line(*itr);
                                        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.token(L";", 1).toInt();
                                                count = line.token(L";", 2).toInt();
                                        }
                                        else
                                        {
                                                while ( !line.empty() )
                                                {
                                                        Utils::WString data = line.tokens(L";", 1, 19);
                                                        list.pushBack(data + L";");
                                                        line = line.remTokens(L";", 1, 19);

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

                        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;

                        for ( CListNode<SCockpit> *cn = s->pPackage->GetCockpits()->Front(); cn; cn = cn->next() )
                        {
                                bool foundEntry = false;
                                Utils::WString 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.replaceToken(L";", 9, Utils::WString::Number(wm->Data()->iMask));
                                                foundEntry = true;
                                                break;
                                        }
                                }

                                bool found = false;
                                for(auto itr = list.begin(); itr != list.end(); itr++)
                                {
                                        if ((*itr)->str.token(L";", 19).Compare(cn->Data()->sCockpit.token(L";", 19)))
                                        {
                                                // only replace existing entry if we have sepeperate weapon masks set
                                                if ( foundEntry )
                                                        (*itr)->str = cockpitStr;
                                                found = true;
                                                break;
                                        }
                                }

                                if ( !found )
                                        list.pushBack(cockpitStr);
                        }
                }

                // finally, write the file
                list.pushFront(Utils::WString::Number(fileType) + L";" + Utils::WString::Number(list.size()) + L";");
                list.pushFront(L"// TCockpits file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));

                CFileIO WriteFile(m_sTempDir + L"/TCockpits.txt");
                if ( WriteFile.writeFile(&list) )
                {
                        this->packFile(&WriteFile, L"types\\TCockpits.pck");
                        WriteFile.remove();
                }

                // remove those entrys
                list.popFront();
                list.popFront();
        }

        return list.size();
}

CBaseFile *CPackages::findScriptByAuthor(const Utils::WString &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->author().Compare(author) )
                        return p;
        }

        return NULL;
}

bool CPackages::extractAll(CBaseFile *baseFile, const Utils::WString &dir, int game, bool includedir, CProgressInfo *progress) const
{
        if (!baseFile)
                return false;

        Utils::WStringList gameAddons;
        for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
        {
                SGameExe *exe = m_gameExe.game(i);
                if (!exe->sAddon.empty())
                        gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
        }

        return baseFile->extractAll(dir, game, gameAddons, includedir, progress);
}

bool CPackages::generatePackagerScript(CBaseFile *baseFile, bool wildcard, Utils::WStringList *list, int game, bool datafile) const
{       
        if (!baseFile)
                return false;

        Utils::WStringList gameAddons;
        for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
        {
                SGameExe *exe = m_gameExe.game(i);
                if (!exe->sAddon.empty())
                        gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
        }

        return baseFile->GeneratePackagerScript(wildcard, list, game, gameAddons, datafile);
}

CBaseFile *CPackages::loadPackagerScript(const Utils::WString &filename, int compression, Utils::WString (*askFunc)(const Utils::WString &), Utils::WStringList *malformedLines, Utils::WStringList *unknownCommands, Utils::WStringList *variables, CProgressInfo *progress)
{
        // check the file exists
        if ( !CFileIO::Exists(filename) )
                return NULL;

        // read all the lines
        CFileIO File(filename);
        if ( !File.startRead() ) 
                return NULL;

        Utils::WStringList fileData;

        int iLine = 0;

        CBaseFile *package = NULL;

        while(!File.atEnd()) {
                // read the next line in the file
                Utils::WString line = File.readEndOfLine();

                // filter out any characters we dont really want
                line.removeChar(L"\t\r");
                line.removeFirstSpace();

                if ( line.empty() ) continue;

                // check for any comments (so we can ignore them)
                if ( line.left(2).Compare(L"//") || line[0] == L'#' ) continue;

                ++iLine;

                // all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
                if ( !line.contains(L':') )
                {
                        // there are some exeptions, and these are one word entrys only
                        line.removeEndSpace();
                        if ( line.contains(L" ") )
                        {
                                if ( malformedLines )
                                        malformedLines->pushBack(line, Utils::WString::Number(iLine));
                                continue;
                        }
                }

                // check for the type line
                if ( !package && line.token(L":", 1).Compare(L"FileType") )
                {
                        Utils::WString sFileType = line.tokens(L":", 2).removeFirstSpace();
                        if ( sFileType.Compare(L"Ship") )
                                package = new CXspFile();
                        else if ( sFileType.Compare(L"Script") )
                                package = new CSpkFile();
                        else if ( sFileType.Compare(L"Base") )
                                package = new CBaseFile();
                        continue;
                }

                fileData.pushBack(line, Utils::WString::Number(iLine));
        }

        // 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);

        Utils::WString ftpaddr;
        Utils::WString ftpuser;
        Utils::WString ftppass;
        Utils::WString ftpdir;
        Utils::WString sMainGame;
        Utils::WStringList otherGames;
        Utils::WStringList gameAddons;
        for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
        {
                SGameExe *exe = m_gameExe.game(i);
                if (!exe->sAddon.empty())
                        gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
        }

        // now lets read the rest of the day
        Utils::WStringList listVaribles;
        for (Utils::WStringNode *line = fileData.first(); line; line = fileData.next())
        {
                Utils::WString cmd = line->str.token(L":", 1);
                Utils::WString rest = line->str.tokens(L":", 2).removeFirstSpace();

                if (cmd.Compare(L"Varible") || cmd.Compare(L"Variable"))
                {
                        Utils::WString s1 = rest.token(L" ", 1);
                        Utils::WString s2 = rest.tokens(L" ", 2);
                        if(!listVaribles.changeData(s1, s2))
                                listVaribles.pushBack(s1, s2);
                }
                else
                {
                        // replace variables
                        if ( rest.contains(L"$") )
                        {
                                for (Utils::WStringNode *strVar = listVaribles.first(); strVar; strVar = listVaribles.next())
                                {
                                        if ( rest.contains(strVar->str) )
                                                rest = rest.findReplace(strVar->str, strVar->data);
                                }

                                if ( variables )
                                {
                                        for (Utils::WStringNode *strVar = variables->first(); strVar; strVar = variables->next())
                                        {
                                                if ( rest.contains(strVar->str) )
                                                        rest = rest.findReplace(strVar->str, strVar->data);
                                        }
                                }
                        }

                        //check for the built in varibles
                        if ( rest.contains(L"$ASK") )
                        {
                                Utils::WString replace = L"$ASK";
                                Utils::WString result;

                                if ( askFunc )
                                        result = askFunc(cmd);

                                if ( rest.contains(L"$ASK(") )
                                {
                                        replace = rest.tokens(L"$ASK(", 2).token(L")", 1);
                                        if ( result.empty() )
                                                result = replace;
                                        replace = L"$ASK(" + replace + L")";
                                }

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

                        // current path
                        if ( rest.contains(L"$PATH") )
                        {
                                Utils::WString currentDir = CFileIO(filename).dir();
                                if ( !currentDir.empty() )
                                        rest = rest.findReplace(L"$PATH", currentDir);
                        }

                        // now parse the rest of the values
                        if ( cmd.Compare(L"FtpUpload") )
                                ftpaddr = rest.token(L" ", 1) + L":" + rest.token(L" ", 2);
                        else if ( cmd.Compare(L"FtpUser") )
                                ftpuser = rest;
                        else if ( cmd.Compare(L"FtpPass") )
                                ftppass = rest;
                        else if ( cmd.Compare(L"FtpDir") )
                                ftpdir = rest;
                        else if ( cmd.Compare(L"MultiGames") ) {
                                sMainGame = rest.token(L" ", 1);
                                otherGames.tokenise(rest.tokens(L" ", 2), L" ");
                        }
                        else if ( !package->loadPackageData(cmd, rest, sMainGame, otherGames, gameAddons, progress))
                        {
                                if ( unknownCommands )
                                        unknownCommands->pushBack(cmd, rest);
                        }
                }
        }

        if ( package->filename().empty() )
                package->loadPackageData(L"AutoSave", L"$AUTOSAVE", sMainGame, otherGames, gameAddons, progress);

        if (package->autoExtraction())
        {
                for (auto itr = package->autoExtraction()->begin(); itr != package->autoExtraction()->end(); itr++)
                {
                        unsigned int game = itr->first;
                        for (auto node = package->fileList().Front(); node; node = node->next())
                        {
                                C_File *f = node->Data();
                                if (f->game() && f->game() != GAME_ALLNEW && !(f->game() & (1 << game)))
                                        continue;
                                package->extractFile(f, itr->second, game, gameAddons);
                        }
                }
        }

        if (package->autoExporter())
        {
                for (auto itr = package->autoExporter()->begin(); itr != package->autoExporter()->end(); itr++)
                        package->saveToArchive(itr->second, itr->first, &m_gameExe);
        }

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

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

                package->setFtpAddr(ftpaddr);
        }

        return package;
}

Utils::WString CPackages::getLanguageName() const
{
        return CPackages::ConvertLanguage(m_iLanguage);
}

size_t CPackages::updateFoundPackages(const Utils::WString& dir)
{
        m_lFoundPackages.MemoryClear();
        if (!m_sCurrentDir.empty())
                return findAllPackages(m_lFoundPackages, dir);

        return 0;
}

size_t CPackages::addFoundPackages(const Utils::WString& dir)
{
        return findPackageDirectories(m_lFoundPackages, dir);
}

int CPackages::findAllPackages(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
        int count = 0;
        if (!dir.empty())
        {
                count += findPackageDirectories(packages, dir + L"/Addons");
                count += findPackageDirectories(packages, dir + L"/Downloads");
        }

        count += findPackageDirectories(packages, L"./Addons");
        count += findPackageDirectories(packages, L"./Downloads");
        count += findPackageDirectories(packages, m_sMyDoc + L"/Egosoft/PluginManager/Addons");
        count += findPackageDirectories(packages, m_sMyDoc + L"/Egosoft/PluginManager/Downloads");

        if (_pCurrentDir)
        {
                count += findPackageDirectories(packages, _pCurrentDir->dir + L"/Addons");
                count += findPackageDirectories(packages, _pCurrentDir->dir + L"/Downloads");
                count += findPackageDirectories(packages, _pCurrentDir->dir + L"/ExtraContent");
        }

        return count;
}
int CPackages::findPackageDirectories(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
        CDirIO Dir(dir);
        int count = 0;
        Utils::WStringList files;
        if (Dir.dirList(files))
        {
                for (auto itr = files.begin(); itr != files.end(); itr++)
                {
                        Utils::WString d = Dir.file((*itr)->str);
                        if (CDirIO(d).isDir())
                                count += findPackageDirectories(packages, d);
                }
        }

        count += findPackageFiles(packages, dir);
        return count;
}
int CPackages::findPackageFiles(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
        CDirIO Dir(dir);
        int count = 0;
        for (int type = 0; type < 2; type++)
        {
                Utils::WStringList files;
                if (type == 0)
                        Dir.dirList(files, Utils::WString::Null(), L"*.spk");
                else if(type == 1)
                        Dir.dirList(files, Utils::WString::Null(), L"*.xsp");
                else
                        break;
                        
                for(auto itr = files.begin(); itr != files.end(); itr++)
                {
                        Utils::WString f = Dir.file((*itr)->str);
                        int error = 0;
                        CBaseFile *p = this->openPackage(f, &error, 0, SPKREAD_NODATA, READFLAG_NOUNCOMPRESS);
                        if (!p)
                                continue;
                        if (p->IsMod() || this->findSpkPackage(p->name(), p->author()))
                        {
                                delete p;
                                continue;
                        }

                        // check its for the correct game
                        if (!p->CheckGameCompatability(this->GetGame()))
                        {
                                delete p;
                                continue;
                        }

                        // check if its already on the list
                        bool found = false;
                        for (CBaseFile *checkp = packages.First(); checkp; checkp = packages.Next())
                        {
                                if (p->name().Compare(checkp->name()) && p->author().Compare(checkp->author()))
                                {
                                        found = true;
                                        break;
                                }
                        }

                        if (found)
                        {
                                delete p;
                                continue;
                        }
                        
                        if (p->icon())
                        {
                                bool addedIcon = false;
                                p->ReadIconFileToMemory();
                                p->icon()->setFilename(this->tempDirectory().findReplace(L"\\", L"/") + L"/" + p->author() + L"_" + p->name() + L"." + p->iconExt());
                                p->icon()->setFullDir(this->tempDirectory());
                                if (p->icon()->UncompressData())
                                {
                                        if (p->icon()->writeFilePointer())
                                                addedIcon = true;
                                }

                                if (!addedIcon)
                                        p->setIcon(NULL, L"");
                        }

                        // get an advert to display
                        if (p->GetFirstFile(FILETYPE_ADVERT))
                        {
                                bool done = false;
                                C_File *f = p->GetFirstFile(FILETYPE_ADVERT);
                                if (p->ReadFileToMemory(f))
                                {
                                        f->setFullDir(this->tempDirectory());
                                        if (f->UncompressData())
                                        {
                                                if (f->writeFilePointer())
                                                        done = true;
                                        }
                                }

                                if (!done)
                                        f->DeleteData();
                        }

                        packages.push_back(p);
                        ++count;
                }
        }

        return count;
}

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

        return Utils::WString::Number(lang);
}

bool CPackages::checkAccessRights(const Utils::WString &aDir) const
{
        Utils::WString dir = aDir;
        if (dir.empty())
                dir = m_sCurrentDir;

        // write a file, then read the contents
        CFileIO File(dir + L"/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
        std::vector<Utils::WString> lines;
        if (!File.readLines(lines))
                return false;

        // check that one of the lines is correct
        for(auto itr = lines.begin(); itr != lines.end(); itr++)
        {
                if ( *itr == L"testing access rights" )
                        return true;
        }

        return false;
}

size_t CPackages::loadShipData(const Utils::WString &file, Utils::WStringList &list) const
{
        CFileIO File;
        bool deleteFile = false;

        // load from cat file
        if (CFileIO(file).isFileExtension(L"cat"))
        {
                CCatFile cat;
                if (cat.open(file, this->getAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE)
                        return false;

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

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

                f.setFilename(m_sTempDir + L"/tships.txt");
                if (!f.writeFilePointer())
                        return false;

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

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

        bool ret = false;
        std::vector<Utils::WString> lines;
        if (File.readLines(lines))
        {
                bool readFirst = false;
                for (auto itr = lines.begin(); itr != lines.end(); itr++)
                {
                        if (itr->empty())
                                continue;
                        Utils::WString str = itr->remove('\r').remove(9);
                        str = str.removeFirstSpace();
                        if (str.empty())
                                continue;
                        if (str[0] == '/' || str[0] == '#')
                                continue;

                        if (!readFirst)
                                readFirst = true;
                        else
                        {
                                Utils::WString t = str.tokens(L";", -2);
                                while (t.right(1) == L";")
                                        t.truncate((int)t.length() - 1);
                                list.pushBack(t, str);
                        }
                }

                ret = true;
        }

        if (deleteFile)
                File.remove();

        return ret;
}

Utils::WString CPackages::readShipData(const Utils::WString &file, const Utils::WString &id) const
{
        Utils::WStringList list;
        if(!this->loadShipData(file, list))
                return Utils::WString::Null();

        CShipData data;
        for(auto itr = list.begin(); itr != list.end(); itr++)
        {
                if ((*itr)->str.Compare(id))
                        return (*itr)->data;
        }

        return Utils::WString::Null();
}

bool CPackages::readTextPage(const Utils::WString &file, Utils::WStringList &list, bool search, int page) const
{
        CFileIO File;
        bool deleteFile = false;

        // read all text files from mod
        if ( CFileIO(file).isFileExtension(L"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 (unsigned int i = 0; i < cat.GetNumFiles(); i++ )
                {
                        SInCatFile *f = cat.GetFile(i);
                        Utils::WString sF = f->sFile;
                        // is a text file
                        sF = sF.findReplace(L"\\", L"/");
                        if ( !sF.token(L"/", 1).Compare(L"t") )
                                continue;

                        Utils::WString baseFile = CFileIO(sF).baseName();
                        // check language
                        int lang = 0;
                        if ( baseFile.findPos(L"-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 + L"/" + CFileIO(f->sFile).baseName() + L".xml"))
                        {
                                if ( this->readTextPage(m_sTempDir + L"/" + CFileIO(f->sFile).baseName() + L".xml", list, search, page))
                                        done = true;
                        }
                }

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

                f.setFilename(m_sTempDir + L"/textfile.xml");
                if ( !f.writeFilePointer() )
                        return false;

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

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

        // open and read file
        std::vector<Utils::WString> lines;
        if(!File.readLines(lines))
                return false;

        bool inPage = false;
        for(auto itr = lines.begin(); itr != lines.end(); itr++)
        {
                // search for page
                if ( !inPage )
                {
                        if (itr->findPos(L"<page") > -1 )
                        {
                                // find the page id
                                int pos = itr->findPos(L"\"");
                                if ( pos > -1 )
                                {
                                        int endpos = itr->findPos(L"\"", pos + 1);
                                        if ( endpos > -1 )
                                        {
                                                Utils::WString p = itr->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 (itr->findPos(L"</page") > -1 )
                                break;

                        if (itr->findPos(L"<t id") > -1 )
                        {
                                int pos = itr->findPos(L"\"");
                                if ( pos > -1 )
                                {
                                        int endpos = itr->findPos(L"\"", pos + 1);
                                        if ( endpos > -1 )
                                        {
                                                int id = itr->mid(pos + 2, endpos - pos - 1).toInt();
                                                pos = itr->findPos(L">", endpos);
                                                if ( pos > -1 )
                                                {
                                                        ++pos;
                                                        endpos = itr->findPos(L"</", pos);
                                                        if ( endpos > -1 )
                                                        {
                                                                Utils::WString text = itr->mid(pos + 1, endpos - pos);
                                                                while ( text.findPos(L'(') != -1 && text.findPos(L')') != -1 )
                                                                {
                                                                        int s = text.findPos(L'(');
                                                                        text = text.erase(s, text.findPos(L')') - s + 1);
                                                                }
                                                                if(!search || !list.contains(Utils::WString::Number(id)))
                                                                        list.pushBack(Utils::WString::Number(id), text);
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }

        return true;
}


FileType CPackages::adjustFileType(const Utils::WString &file, FileType filetype) const
{
        CFileIO File(file);
        Utils::WString dir = File.GetDirIO().topDir();
        Utils::WString basename = File.baseName();

        Utils::WString ext = File.extension();

        // mod files
        if (ext.Compare(L"cat") || ext.Compare(L"dat"))
                return FILETYPE_MOD;
        // check for text files
        if ( File.filename().contains(L"-L") && File.filename().left(4).isNumber() )
                return FILETYPE_TEXT;
        if ( File.baseName().Compare(L"conversations") )
                return FILETYPE_TEXT;
        if ( basename.length() <= 4 && basename.isNumber() && (File.isFileExtension(L"xml") || File.isFileExtension(L"pck")) )
                return FILETYPE_TEXT;
        // X2/X3 text file
        if ( basename.length() >= 5 && basename.length() <= 8 && ((int)File.baseName()) )
                return FILETYPE_TEXT;
        if ( filetype == FILETYPE_TEXT ) // should no longer be anything text
                return FILETYPE_SCRIPT;
        if ( File.isFileExtension(L"wav") || File.isFileExtension(L"mp3") )
                return FILETYPE_SOUND;
        return filetype;
}

void CPackages::RemoveFailedFiles()
{
        Utils::WStringList removed;
        for (auto itr = _lNonRemovedFiles.begin(); itr != _lNonRemovedFiles.end(); itr++)
        {
                if (CFileIO::Remove((*itr)->str))
                        removed.pushBack((*itr)->str);
        }
        for (auto itr = removed.begin(); itr != removed.end(); itr++)
                _lNonRemovedFiles.remove((*itr)->str);
}

CXspFile *CPackages::extractShip(const Utils::WString &sCatFile, const Utils::WString &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;
}

void CPackages::getMergedFiles(Utils::WStringList &list, CCatFile *cat1, CCatFile *cat2) const
{
        // first add all files from the "primary" mod
        for (auto itr = cat1->GetFiles()->cbegin(); itr != cat1->GetFiles()->cend(); itr++)
                list.pushBack((*itr)->sFile.findReplace(L"\\", L"/"), L"1");

        // now add the ones from the secondary
        for (auto itr = cat2->GetFiles()->cbegin(); itr != cat2->GetFiles()->cend(); itr++)
        {
                Utils::WString sFile = (*itr)->sFile.findReplace(L"\\", L"/");
                // if its found on the 2nd list, dont add, just adjust the type
                if(!list.changeData(sFile, L"-1"))
                        list.pushBack(sFile, L"2");
        }
}

bool CPackages::canWeMerge(const Utils::WString &file) const
{
        return CModDiff::CanBeDiffed(file);
}

bool CPackages::needToMerge(const Utils::WString &file) const
{
        Utils::WString firstDir = file.token(L"/", 1);
        if ( firstDir.Compare(L"t") )
                return true;
        if ( firstDir.Compare(L"types") )
                return true;
        if ( firstDir.Compare(L"maps") )
                return true;

        return false;
}

bool CPackages::mergeMods(CCatFile *mod1, CCatFile *mod2, const Utils::WString &outFile, Utils::WStringList *cantMerge) const
{
        CCatFile newCat;
        if ( newCat.open(outFile, this->getAddonDir()) != CATERR_CREATED )
                return false;

        Utils::WStringList list;
        this->getMergedFiles(list, mod1, mod2);
        if (list.empty())
                return false;

        // add all the files to the new mod first
        Utils::WStringList conflicts;
        for(auto itr = list.begin(); itr != list.end(); itr++)
        {               
                int status = (*itr)->data.toInt();
                if ( status == 1 )
                {
                        if ( !newCat.writeFromCat(mod1, (*itr)->str) )
                        {
                                if ( cantMerge )
                                        cantMerge->pushBack((*itr)->str, L"1");
                        }
                }
                else if ( status == 2 )
                {
                        if ( !newCat.writeFromCat(mod2, (*itr)->str) )
                        {
                                if ( cantMerge )
                                        cantMerge->pushBack((*itr)->str, L"2");
                        }
                }
                else if ( status == -1 )
                {
                        if ( this->needToMerge((*itr)->str) )
                        {
                                if ( this->canWeMerge((*itr)->str) )
                                        conflicts.pushBack((*itr)->str);
                                else if ( cantMerge )
                                        cantMerge->pushBack((*itr)->str, L"-1");
                        }
                        else
                        {
                                if ( !newCat.writeFromCat(mod1, (*itr)->str) )
                                {
                                        if ( cantMerge )
                                                cantMerge->pushBack((*itr)->str, L"1");
                                }
                        }
                }
        }

        /* 
                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
//      Utils::WStringList *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, Utils::WStringList *list) const
{
        // not a valid file
        if ( !file ) return false;
        if ( file->GetFileType() != FILETYPE_MOD ) return false;
        if ( !file->fileExt().Compare(L"cat") ) return false;

        // we need to read the file list for the mod
        CCatFile cat;
        if ( cat.open(file->filePointer(), this->getAddonDir(), CATREAD_JUSTCONTENTS, false) == CATERR_NONE)
        {
                for (unsigned int i = 0; i < cat.GetNumFiles(); i++ )
                {
                        SInCatFile *f = cat.GetFile(i);
                        Utils::WString filename = f->sFile;
                        filename = filename.findReplace(L"\\", L"/");
                        bool found = false;
                        //TODO: rework this
                        if ( filename.left(2).Compare(L"t/") || filename.left(6).Compare(L"types/") )
                                found = true;
                        else if (filename.left(8).Compare(L"addon/t/") || filename.left(12).Compare(L"addon/types/"))
                                found = true;
                        else if (filename.left(9).Compare(L"addon2/t/") || filename.left(13).Compare(L"addon2/types/"))
                                found = true;

                        if ( found ) {
                                if ( list )
                                        list->pushBack(filename, Utils::WString::Number(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, Utils::WStringList *list) const
{
        // 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->fileExt().Compare(L"cat")) return false;
        if (!to->fileExt().Compare(L"cat")) return false;

        // get file lists from each file
        Utils::WStringList fromList;
        if (getModCompatabilityList(from, &fromList))
        {
                Utils::WStringList toList;
                if (getModCompatabilityList(to, &toList))
                {
                        // both have files we need to check, compare them
                        for(auto itr = fromList.begin(); itr != fromList.end(); itr++)
                        {
                                Utils::WString fromFile = (*itr)->str;
                                fromFile = fromFile.findReplace(L"\\", L"/");
                                fromFile = fromFile.findReplace(L"//", L"/");
                                for (auto toItr = toList.begin(); toItr != toList.end(); toItr++)
                                {
                                        Utils::WString toFile = (*toItr)->str;
                                        toFile = toFile.findReplace(L"\\", L"/");
                                        toFile = toFile.findReplace(L"//", L"/");
                                        if ( fromFile.Compare(toFile) )
                                        {
                                                if ( list )
                                                        list->pushBack(from->filename() + L"::" + fromFile, to->filename() + L"::" + toFile);
                                                else
                                                        return true;
                                        }
                                }
                        }
                }
        }

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

        return false;
}

bool CPackages::checkCompatabilityBetweenMods(CBaseFile *from, CBaseFile *to, Utils::WStringList *list) const
{
        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

        // check if one is a depencacy of the other
        if (from->isPackageNeeded(to->name(), to->author())) return false;
        if (to->isPackageNeeded(from->name(), from->author())) return false;
        // check if one is directly connected
        if (from->type() == BaseFileType::TYPE_SPK)
        {
                CSpkFile* fromSpk = dynamic_cast<CSpkFile*>(from);
                if (fromSpk->isAnotherMod())
                {
                        if (fromSpk->otherName().Compare(to->name()) && fromSpk->otherAuthor().Compare(to->author()))
                                return false;
                }
        }
        if (to->type() == BaseFileType::TYPE_SPK)
        {
                CSpkFile* toSpk = dynamic_cast<CSpkFile*>(to);
                if (toSpk->isAnotherMod())
                {
                        if (toSpk->otherName().Compare(from->name()) && toSpk->otherAuthor().Compare(from->author()))
                                return false;
                }
        }

        int count = 0;
        for ( C_File *f = from->GetFirstFile(FILETYPE_MOD); f; f = from->GetNextFile(f) )
        {
                if ( !f->IsFakePatch() ) continue;
                if ( f->fileExt().Compare(L"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->fileExt().Compare(L"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, Utils::WStringList *list, CLinkList<CBaseFile> *packages) const
{
        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(L"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::Remove(f->filePointer());
                f->setFullDir(L"");
        }

        return count;
}

bool CPackages::isSamePackage(CBaseFile *p1, CBaseFile *p2) const
{
        if ( !p1 || !p2 ) return false;
        if ( p1 == p2 ) return true;

        if ( p1->name().Compare(p2->name()) && p1->author().Compare(p2->author()) )
                return true;
        return false;
}

void CPackages::applyFakePatchOrder(const Utils::WStringList &list)
{
        _lFakePatchOrder.clear();
        for(auto itr = list.begin(); itr != list.end(); itr++)
                _lFakePatchOrder.pushBack((*itr)->str, (*itr)->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->gameChanging();
        p->iEase = package->easeOfUse();
        p->iPluginType = package->pluginType();
        p->iRec = package->recommended();
        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->author();
        p->sDesc = package->description().findReplace(L"\n", L"::newline::");
        p->sName = package->name();
        p->sUpdated = package->creationDate();
        p->sVersion = package->version();
        p->sFilename = CFileIO(package->filename()).filename();

        return p;
}

Utils::WString CPackages::FormatAvailablePackageData(CBaseFile *package)
{
        SAvailablePackage *p = CPackages::CreateAvailablePackageData(package);
        Utils::WString ret = CPackages::FormatAvailablePackageData(p);
        delete p;
        return ret;
}

Utils::WString CPackages::FormatAvailablePackageData(SAvailablePackage *package)
{
        Utils::WString ret = (long)package->iType;

        Utils::WString gameCompat;
        for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
                if ( !gameCompat.empty() )
                        gameCompat += L"!";
                gameCompat += Utils::WString::Number(node->Data()->iGame);
        }

        if ( gameCompat.empty() )
                gameCompat = L"0";

        ret = ret.addToken(L"::", gameCompat);
        ret = ret.addToken(L"::", package->sName);
        ret = ret.addToken(L"::", package->sAuthor);
        ret = ret.addToken(L"::", package->sVersion);
        ret = ret.addToken(L"::", package->sUpdated);
        ret = ret.addToken(L"::", package->sFilename);
        ret = ret.addToken(L"::", Utils::WString::Number(package->iEase));
        ret = ret.addToken(L"::", Utils::WString::Number(package->iChanging));
        ret = ret.addToken(L"::", Utils::WString::Number(package->iRec));
        ret = ret.addToken(L"::", Utils::WString::Number(package->iPluginType));
        ret = ret.addToken(L"::", Utils::WString::Number(package->iScriptType));
        ret = ret.addToken(L"::", (package->bSigned) ? L"1" : L"0");
        ret = ret.addToken(L"::", package->sDesc);

        return ret;
}

void CPackages::parseAvailablePackage(const Utils::WString &str, const Utils::WString &webaddress)
{
        // first check game
        std::vector<Utils::WString> tok;
        if (!str.tokenise(L"::", tok))
                return;
        // invalid number of entries?
        if (tok.size() < 7 ) return;

        SAvailablePackage *p = new SAvailablePackage;
        p->iType = tok[0].toLong();
        p->bSigned = false;

        // theres multiple games, so we need to split it
        if ( m_iGame ) {
                Utils::WString sGame = tok[1];
                if ( sGame.contains(L"!") ) {
                        for(int i = 1; i <= sGame.countToken(L"!"); i++) {
                                SGameCompat *gc = new SGameCompat;
                                gc->iVersion = 0;
                                gc->iGame = sGame.token(L"!", i).toLong();
                                p->lGames.push_back(gc);
                        }
                }
                else {
                        SGameCompat *gc = new SGameCompat;
                        gc->iVersion = 0;
                        gc->iGame = sGame.toLong();
                        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 + L"/" + p->sFilename;

        p->iChanging = p->iEase = p->iPluginType = p->iRec = p->iScriptType = -1;

        // check if we have the extra values
        if (tok.size() >= 12 )
        {
                p->iEase = tok[7].toLong();
                p->iChanging = tok[8].toLong();
                p->iRec = tok[9].toLong();
                p->iPluginType = tok[10].toLong();
                p->iScriptType = tok[11].toLong();
                if (tok.size()> 13 ) {
                        p->sDesc = tok[13];
                        p->bSigned = tok[12].toBool();
                }
                else
                        p->sDesc = tok[12];
        }
        else if (tok.size() > 7 )
                p->sDesc = tok[8];

        if ( !p->sDesc.empty() )
                p->sDesc = p->sDesc.findReplace(L"::newline::", L"\\n");

        addAvailablePackage(p);
}

const SAvailablePackage* CPackages::findAvailablePackage(const Utils::WString& filename) const
{
        for (CListNode<SAvailablePackage>* node = m_lAvailablePackages.Front(); node; node = node->next())
        {
                if (node->Data()->sFilename.Compare(filename))
                        return node->Data();
        }
        return NULL;
}
const SAvailablePackage* CPackages::findAvailablePackage(const Utils::WString& name, const Utils::WString& author) const
{
        for (CListNode<SAvailablePackage>* node = m_lAvailablePackages.Front(); node; node = node->next())
        {
                if (node->Data()->sName.Compare(name) && node->Data()->sAuthor.Compare(author))
                        return node->Data();
        }
        return NULL;
}

CBaseFile* CPackages::findFoundPackage(const Utils::WString& name, const Utils::WString& author) const
{
        for (CListNode<CBaseFile>* node = m_lFoundPackages.Front(); node; node = node->next())
        {
                if (node->Data()->name().Compare(name) && node->Data()->author().Compare(author))
                        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;
        }

        // check if a matching package is already found
        const CBaseFile* bf = findFoundPackage(package->sName, package->sAuthor);
        if (bf)
        {
                if (bf->version().compareVersion(package->sVersion) <= 0)
                        return true;            
        }

        const SAvailablePackage *p = findAvailablePackage(package->sFilename);
        if(p)
        {
                if (p->sVersion.compareVersion(package->sVersion) <= 0)
                        return true;
                m_lAvailablePackages.remove(p);
        }

        p = findAvailablePackage(package->sName, package->sAuthor);
        if (p)
        {
                if (p->sVersion.compareVersion(package->sVersion) <= 0)
                        return true;
                m_lAvailablePackages.remove(p);
        }

        m_lAvailablePackages.push_back(package); 
        saveAvailablePackages();
        return true;
}

void CPackages::saveAvailablePackages()
{
        std::vector<Utils::WString> lines;

        lines.push_back(L"Version;1;" + Utils::WString::Number(m_lAvailablePackages.size()));

        for (auto itr = m_lAvailablePackages.First(); itr; itr = m_lAvailablePackages.Next())
        {
                Utils::WString l = L"Package";
                l += L";" + itr->sName.findReplace(L";", L":&COL&:");
                l += L";" + itr->sAuthor.findReplace(L";", L":&COL&:");
                l += L";" + itr->sVersion.findReplace(L";", L":&COL&:");
                l += L";" + itr->sFilename;
                l += L";" + itr->sDesc.findReplace(L";", L":&COL&:");
                l += L";" + itr->sUpdated;

                l += L";" + Utils::WString::Number(itr->lGames.size());

                for (auto g = itr->lGames.First(); g; g = itr->lGames.Next())
                        l += L";" + Utils::WString::Number(g->iGame) + L";" + Utils::WString::Number(g->iVersion) + L";" + g->sVersion;

                l += L";" + Utils::WString::Number(itr->iType);
                l += L";" + Utils::WString::Number(itr->iPluginType);
                l += L";" + Utils::WString::Number(itr->iScriptType);
                l += L";" + Utils::WString::Number(itr->iChanging);
                l += L";" + Utils::WString::Number(itr->iEase);
                l += L";" + Utils::WString::Number(itr->iRec);
                l += itr->bSigned ? L";1" : L";0";

                lines.push_back(l);
        }

        // write out the file
        CDirIO dir(m_sCurrentDir + L"/PluginManager");
        if (!dir.exists())
                dir.create();
        CFileIO file(dir.file(L"packagecache.new"));
        if (file.writeFile(lines))
        {
                if (CFileIO::Exists(dir.file(L"packagecache.new")))
                {
                        if (CFileIO::Exists(dir.file(L"packagecache.dat")))
                                CFileIO::Remove(dir.file(L"packagecache.dat"));
                        file.Rename(dir.file(L"packagecache.dat"));
                }
        }
}

void CPackages::readAvailablePackages()
{
        m_lAvailablePackages.MemoryClear();

        CDirIO dir(m_sCurrentDir + L"/PluginManager");
        CFileIO file(dir.file(L"packagecache.dat"));

        if (file.exists())
        {
                size_t version = 0;
                size_t count = 0;
                std::vector<Utils::WString> lines;
                if (file.readLines(lines))
                {
                        for (auto itr = lines.begin(); itr != lines.end(); itr++)
                        {
                                Utils::WString cmd = itr->token(L";", 1);
                                if (cmd == L"Version")
                                {
                                        version = itr->token(L";", 2).toInt();
                                        count = itr->token(L";", 3).toInt();
                                }
                                else if (cmd == L"Package")
                                {
                                        int max = 0;
                                        std::vector<Utils::WString> str;
                                        itr->tokenise(L";", str);

                                        SAvailablePackage* package = new SAvailablePackage;
                                        int pos = 1;
                                        package->sName = str[pos++].findReplace(L":&COL&:", L";");
                                        package->sAuthor = str[pos++].findReplace(L":&COL&:", L";");
                                        package->sVersion = str[pos++].findReplace(L":&COL&:", L";");
                                        package->sFilename = str[pos++].findReplace(L":&COL&:", L";");
                                        package->sDesc = str[pos++].findReplace(L":&COL&:", L";");
                                        package->sUpdated = str[pos++].findReplace(L":&COL&:", L";");

                                        size_t games = str[pos++].toInt();
                                        for (size_t i = 0; i < games; i++)
                                        {
                                                SGameCompat* g = new SGameCompat;
                                                g->iGame = str[pos++].toInt();
                                                g->iVersion = str[pos++].toInt();
                                                g->sVersion = str[pos++];
                                                package->lGames.push_back(g);
                                        }

                                        package->iType = str[pos++].toInt();
                                        package->iPluginType = str[pos++].toInt();
                                        package->iScriptType = str[pos++].toInt();
                                        package->iChanging = str[pos++].toInt();
                                        package->iEase = str[pos++].toInt();
                                        package->iRec = str[pos++].toInt();
                                        package->bSigned = (str[pos] == L"1");

                                        addAvailablePackage(package);
                                }
                        }
                }
        }
}

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(Utils::WStringList *list) const
{
        for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
        {
                if (!node->Data()->webAddress().empty())
                {
                        if(!list->contains(node->Data()->webAddress()))
                                list->pushBack(node->Data()->webAddress());
                }
                if ( node->Data()->anyWebMirrors() )
                {
                        auto &l = node->Data()->webMirrors();
                        for (auto itr = l.begin(); itr != l.end(); itr++)
                        {
                                if(!list->contains((*itr)->str))
                                        list->pushBack((*itr)->str);
                        }
                }
        }

        CFileIO File(L"data\\web");

        if ( File.startRead() ) {
                while(!File.atEnd()) {
                        Utils::WString line = File.readEndOfLine();
                        line.removeChar(L"\n\r\t ");
                        if (!line.empty())
                        {
                                if(!list->contains(line))
                                        list->pushBack(line);
                        }
                }
                File.close();
        }

        if (!list->contains(L"http://xpluginmanager.co.uk/tcscripts"))
                list->pushBack(L"http://xpluginmanager.co.uk/tcscripts");
        if (!list->contains(L"http://xpluginmanager.co.uk/acscripts"))
                list->pushBack(L"http://xpluginmanager.co.uk/apscripts");
        if (!list->contains(L"http://xpluginmanager.co.uk/fcscripts"))
                list->pushBack(L"http://xpluginmanager.co.uk/flscripts");

        return list->size();
}

void CPackages::readArchiveData(const Utils::WString &filename, CBaseFile *archive) const
{
        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) const
{
        Utils::WStringList otherGames;
        Utils::WStringList gameAddons;
        for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
        {
                SGameExe *exe = m_gameExe.game(i);
                if (!exe->sAddon.empty())
                        gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
        }

        Utils::WString data(buf);
        std::vector<Utils::WString> str;
        if(data.tokenise(L"\n", str))
        {
                for(size_t i = 0; i < str.size(); i++ )
                {
                        Utils::WString line = str[i];
                        if ( line.empty() )
                                continue;

                        // filter out any spaces, tabs in front
                        line.removeChar('\t');
                        line.removeChar('\r');
                        Utils::WString linenospace = line;
                        linenospace.removeFirstSpace();
                        if ( linenospace.empty() )
                                continue;

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

                        // all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
                        if ( !line.contains(':'))
                                continue;

                        Utils::WString first = line.token(L":", 1);
                        Utils::WString rest = line.tokens(L":", 2).removeFirstSpace();

                        Utils::WString checkType = first;
                        bool shared = false;
                        if ( checkType.left(6).Compare(L"Shared") )
                        {
                                checkType = first.right(-6);
                                shared = true;
                        }
                        bool packed = false;
                        if (checkType.right(3).compare(L"PCK"))
                        {
                                checkType = checkType.left(-3);
                                packed = true;
                        }

                        // now check type name
                        int filetype = GetFileTypeFromString(checkType);
                        if (filetype == -1)
                        {
                                archive->loadPackageData(first, rest, Utils::WString::Null(), otherGames, gameAddons, NULL);
                        }
                }
        }
}

CBaseFile *CPackages::_archive_fromRar(const Utils::WString &filename, bool toInstall) const
{
        // make sure we can open the zip file
        CBaseFile *archive = NULL;
#ifdef _RAR
        HANDLE hArcData;
        int RHCode,PFCode;
        char CmtBuf[16384];
        struct RARHeaderDataEx HeaderData;
        struct RAROpenArchiveDataEx OpenArchiveData;

        // find the pluginmanager text to covnert to spkfile
        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;
                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 ( Utils::WString(HeaderData.FileName).Compare(L"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;
        Utils::WString extractedFile = CDirIO(m_sTempDir).file(L"extracted.tst").findReplace(L"/", L"\\").findReplace(L"\\\\", L"\\");
        while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
        {
                Utils::WString fileName = HeaderData.FileName;

                if ( HeaderData.FileAttr == 16 )
                        continue;
                PFCode=RARProcessFileW(hArcData, RAR_EXTRACT, NULL, NULL);
                if (PFCode!=0)
                {
                        error = true;
                        break;
                }

                CFileIO File(fileName);
                if ( File.exists() )
                {
                        if ( fileName.Compare(L"pluginmanager.txt") )
                                this->readArchiveData(File.fullFilename(), archive);
                        else
                        {
                                Utils::WString 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).filename(), L"", FILETYPE_SCRIPT);
                                        if ( f ) {
                                                f->readFromFile(File.fullFilename());
                                        }
                                        type = FILETYPE_UNINSTALL;
                                }

                                if ( type == -1 )
                                        f = archive->addFile(CFileIO(fileName).filename(), CFileIO(fileName).dir(), FILETYPE_EXTRA);
                                else
                                        f = archive->addFile(CFileIO(fileName).filename(), extradir, static_cast<FileType>(type));
                                f->readFromFile(File.fullFilename());
                        }

                        File.remove();
                }
        }

        RARCloseArchive(hArcData);

        if ( error )
        {
                delete archive;
                archive = NULL;
        }
#endif

        return archive;
}

CBaseFile *CPackages::_archive_fromZip(const Utils::WString &filename, bool toInstall) const
{
        CBaseFile *archive = NULL;

        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);

                Utils::WString Name(ze.name);

                // if its the data file, dont add it, but extract to get settings from
                if ( Name.Compare(L"pluginmanager.txt") )
                {
                        this->readArchiveData(iBuf, ze.unc_size, archive);
                        delete[] iBuf;
                }
                else
                {
                        Utils::WString extradir;
                        int type = SPK::GetAutomaticFiletype(Name, &extradir, true);

                        C_File *f = NULL;

                        Utils::WString filename = CFileIO(Name).filename();
                        Utils::WString dir = CFileIO(Name).dir();

                        // check for special file types
                        if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
                                f = archive->addFile(filename, dir, FILETYPE_SCRIPT);
                                if ( f ) {
                                        f->copyData((const unsigned char *)iBuf, ze.unc_size);
                                }
                                type = FILETYPE_UNINSTALL;
                        }

                        int game = 0;
                        // check for addons
                        if (dir.contains('/', true)) 
                        {
                                Utils::WString first = dir.token(L"/", 1);
                                int g = m_gameExe.findAddonType(first);
                                if (g != -1)
                                        game = g + 1;
                        }

                        if ( type == -1 )
                                f = archive->addFile(filename, dir, FILETYPE_EXTRA, game);
                        else
                                f = archive->addFile(filename, extradir, static_cast<FileType>(type), game);

                        if ( f )
                                f->SetData((const unsigned char *)iBuf, ze.unc_size);
                        else
                                delete[] iBuf;
                }
        }

        CloseZip(hz);

        if ( error )
        {
                delete archive;
                archive = NULL;
        }
        return archive;
}

CBaseFile *CPackages::createFromArchive(const Utils::WString &filename, bool toInstall) const
{
        // make sure we can open the zip file
        CBaseFile *archive = NULL;
        if ( CFileIO(filename).isFileExtension(L"rar") )
                archive = this->_archive_fromRar(filename, toInstall);
        else if ( CFileIO(filename).isFileExtension(L"zip") )
                archive = this->_archive_fromZip(filename, toInstall);

        if ( archive ) {
                archive->setFilename(CFileIO(filename).changeFileExtension(L"spk"));
                if ( toInstall )
                        archive->setName(CFileIO(filename).filename());
                else
                        archive->setName(CFileIO(filename).baseName());
        }

        return archive;
}

Utils::WString CPackages::CreateFromPackagerScript(CPackages *packages, const Utils::WString &filename)
{
        Utils::WString curDir = CFileIO(filename).dir();
        Utils::WStringList variables;
        variables.pushBack(L"$PATH", curDir);
        CBaseFile *package = packages->loadPackagerScript(filename, NULL, NULL, NULL, &variables);

        if ( !package )
                return Utils::WString::Null();

        Utils::WString saveto = package->filename();
        saveto = saveto.findReplace(L"$DEFAULTDIR", curDir + L"/");
        saveto = saveto.findReplace(L"$PATH", curDir);
        saveto = saveto.findReplace(L"\\", L"/");
        saveto = saveto.findReplace(L"//", L"/");
        if ( !saveto.right(4).Compare(L".spk") && package->GetType() != TYPE_XSP )
                saveto += L".spk";
        else if ( !saveto.right(4).Compare(L".xsp") && package->GetType() == TYPE_XSP )
                saveto += L".xsp";

        // write script
        if ( package->writeFile(saveto) )
        {
                if ( package->AutoGenerateUpdateFile() )
                        package->createUpdateFile(CFileIO(saveto).dir());
                return saveto;
        }

        return Utils::WString::Null();
}

int CPackages::GeneratePackageUpdateData(const Utils::WString &dir, bool includeSingle)
{
        Utils::WStringList filedata;

        CPackages packages;

        CDirIO Dir(dir);
        for ( int i = 0; i < 2; i++ )
        {
                Utils::WString pattern;
                if ( i == 0 ) pattern = L"*.spk";
                else if ( i == 1 ) pattern = L".xsp";
                else break;

                Utils::WStringList files;
                if(Dir.dirList(files, L"", pattern))
                {
                        for(auto itr = files.begin(); itr != files.end(); itr++)
                        {
                                int error = 0;
                                CBaseFile *p = packages.openPackage(Dir.file((*itr)->str), &error, 0, SPKREAD_NODATA);
                                if ( !p )
                                        continue;

                                if ( includeSingle )
                                        p->createUpdateFile(dir);
                                filedata.pushBack(CPackages::FormatAvailablePackageData(p));
                                delete p;
                        }
                }
        }

        if ( !filedata.empty() )
        {
                CFileIO File(dir + L"/xpackagedata.dat");
                if ( File.writeFile(&filedata) )
                        return filedata.size();
        }

        return 0;
}

size_t CPackages::verifyInstalledFiles(Utils::WStringList *missingFiles, bool getPackages) const
{
        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->filePointer().contains(L"::") ) {
                        Utils::WString modFile = f->filePointer().token(L"::", 1);
                        Utils::WString file = f->filePointer().token(L"::", 2);

                        if ( CFileIO::Exists(modFile)) {
                                CCatFile catFile;
                                if ( catFile.open(modFile, L"", CATREAD_CATDECRYPT, false) == CATERR_NONE ) {
                                        if ( catFile.findData(file) )
                                                exists = true;
                                }
                        }
                }
                else {
                        exists = CFileIO::Exists(f->filePointer());
                }

                if ( !exists )
                {
                        ++count;
                        if ( missingFiles )
                        {
                                Utils::WString 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 += L"\n";
                                                        packages += package->getFullPackageName(m_iLanguage);
                                                }
                                        }
                                }
                                Utils::WString filename = f->filePointer();
                                filename = filename.findRemove(m_sCurrentDir);
                                missingFiles->pushBack(filename, packages);
                        }
                }
        }
        return count;
}