Rev 281 | Rev 310 | 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>#endifenum {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 directoryCDirIO 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);elsem_pGameVFS.LoadFilesystem(m_sCurrentDir, 0);}elsem_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 existsCFileIO 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));}elsepackageFile->parseValueLine(line);}else if ( iStatus == READ_SCRIPTFILE ){if ( cmd == L"<script>" ){if ( packageFile->IsMod() && packageFile->IsEnabled() )m_pEnabledMod = packageFile;_processAddPackage(packageFile);packageFile = new CSpkFile();iStatus = READ_SCRIPT;}else if ( cmd == L"<ship>" ){_processAddPackage(packageFile);packageFile = new CXspFile();iStatus = READ_SCRIPT;}else if ( cmd == L"<base>" ){_processAddPackage(packageFile);packageFile = new CBaseFile();iStatus = READ_SCRIPT;}else if ( cmd == L"<archive>" ){_processAddPackage(packageFile);packageFile = new CArchiveFile();iStatus = READ_SCRIPT;}else if (cmd == L"</scripts>"){if ( packageFile->IsMod() && packageFile->IsEnabled() )m_pEnabledMod = packageFile;_processAddPackage(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;elsem_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 patchif ( !ReadyFakePatch() )return false;this->RemoveCreatedFiles();this->createPluginManagerOpenText();// match up waresfor ( 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 timethis->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 fileUtils::WString logDir = logDirectory();CFileIO LogFile(logDir + L"/log0" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L".txt");if ( LogFile.exists() ){// read the log file to memorystd::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 fileLogFile.remove();}}void CPackages::PurgeWares(){// mark all delete wares as availablefor ( 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 itif ( 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 themif ( 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 usedif ( m_iFakePatch < 0 ) return true;// now lets find the fake patchUtils::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 onewhile ( 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 itif ( useFile.empty() ) {int nextfree = this->findNextFakePatch();--nextfree; // gets the end fake patch// work backwards till we find a valid onewhile ( 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 firstCCatFile 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 themCFileIO 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 failedreturn false;}// no files found, but we have a fake patch ? restore from backupelse 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 filesfor ( 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 itCFileIO 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 versionsif ( 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 versionif ( 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 extensionif ( 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 folderif ( 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::_processAddPackage(CBaseFile* p){if (p){this->ConvertOldPackage(p);p->completeFile();m_lPackages.push_back(p);}}void CPackages::UpdatePackage(CBaseFile *p){if ( p->GetType() != TYPE_SPK )return;CSpkFile *package = (CSpkFile *)p;// update the signed statuspackage->updateSigned(true);// check for another modif ( !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 baseif ( doStatus == 0 || doStatus == -1 ){_pOriginalFiles->update(m_bRedo, &m_lFiles);}// update each package// parent/child, signed statusif ( doStatus == -1 || doStatus == 1 ){if ( individual ){// no package, most likly none installedif ( !m_pPackageNode )return false;this->UpdatePackage(m_pPackageNode->Data());// move to the next packagem_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 gamesif ( doStatus == -1 || doStatus == 3 ){}m_bRedo = false;return true;}int CPackages::checkOpenPackage(const Utils::WString &file, int *error){// first check if it existsif (!CFileIO::Exists(file)){*error = INSTALLERR_NOEXIST;return -1;}// open the spk filefloat fVersion = 0.0f;int check = CBaseFile::CheckFile(file, &fVersion);// wrong versionif ( 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 xspif ( CFileIO(file).isFileExtension(L"xsp") ){installFile = new CXspFile();if ( !((CXspFile *)installFile)->convertOld(file) ){delete installFile;return NULL;}break;}// try and open as archiveelse if(CFileIO(file).isFileExtension(L"spk")){if (_check_archive_fromZip(file)){installFile = createFromArchive(file, true);if (installFile)return installFile;}}*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 filesif ( !(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 filesfor ( 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 patchif ( 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 modif ( 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 signedif ( 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 depanciesif ( !disabled && !this->CheckEnabledDependacy(package) ) {disabled = true;CLog::log(CLog::Log_Install, 2, L"Dependacies missing for package, setting to disabled");}// search for an old versionif ( 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 onesfor ( 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 themif ( !disabled ){// find any uninstall files and remove themCLog::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 filesCLog::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);}elsethis->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 filesreturn false;}_pOriginalFiles->installed(package);// if we're installing an addon, lets use the fake patch method for object filesif ( m_iGameFlags & EXEFLAG_ADDON ) this->_addToFakePatch(package);bool shuffle = package->anyFakePatchOrder();// merge the update into the old packagebool dontAdd = false;if ( package->GetType() == TYPE_SPK ){if ( ((CSpkFile *)package)->IsPackageUpdate() ){// now copy any files from a modfor ( 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 fileif (!f->Data()->checkFileExt(L"cat"))continue;// if fake patch, find first fake patch in packageC_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 fileif ( !node->Data()->checkFileExt(L"cat") )continue;if ( !node->Data()->IsFakePatch() )continue;findMatching = node->Data();break;}}// otherwise, just add to the mod of the same nameelse{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 fileif (!node->Data()->checkFileExt(L"cat"))continue;if ( node->Data()->name().Compare(f->Data()->name()) ){findMatching = node->Data();break;}}}if ( findMatching ){// copy accross all modsCCatFile 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 filesC_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 backelse{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 itif (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");}elsepackage->setIcon(NULL, L"");}}if (package->icon())package->icon()->DeleteData();}}// remove all dataCLog::log(CLog::Log_Install, 3, L"Clearing all unneeded file data");package->ClearFileData();// add to listif ( !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 newfor(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 onem_lPackages.insert(oldPackage, package);m_lPackages.remove(oldPackage, false);}elsem_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 packageif ( 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 usedif ( !f->getUsed() ){// remove from file listm_lFiles.remove(f, false);// if its a readme file, then check if its in the new packagebool dontRemove = false;if ( f->GetFileType() == FILETYPE_README ) {if ( package->findFile(f->filename(), FILETYPE_README) )dontRemove = true;}// remove from hard driveif ( !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 shufleif ( 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 packagesif ( 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 modif ( package->IsMod() && !disabled ) {m_pEnabledMod = package;CLog::log(CLog::Log_Install, 1, L"Setting package as primary mod");}package->updateTextDB();package->completeFile();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 removeUpdateUsedFiles(&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 removedint 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 dependantsif ( 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 packageint fileCount = 0;for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() ){C_File *f = node->Data();// display progress if neededif ( progress ){progress->UpdateProgress(fileCount++, maxFiles);progress->UpdateFile(f);}// skip uninstall filesif ( f->GetFileType() == FILETYPE_UNINSTALL ){uninstallFiles.push_back(f);continue;}// only delete files that are not used// if its a shared file, we skip itif ( 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 patchsif ( f->IsFakePatch() )shuffle = true;else if ( f->isAutoTextFile() )shuffle = true;// remove the file from the main list as swellm_lFiles.remove(f, false);delete f;}// remove all the packages from memoryfor ( 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);elsedelete p;for (CListNode<SGameShip>* wNode = m_lGameShips.Front(); wNode; wNode = wNode->next()){if (wNode->Data()->pPackage == p){wNode->Data()->pPackage = NULL;break;}}}m_lInstallList.clear();// check unistall filesif ( !uninstallFiles.empty() ){removeDirs.pushBack(L"PluginManager/Uninstall");// make sure the scripts directory is created, even thou it should always be there anywaysCDirIO scriptDir(m_sCurrentDir);if ( !scriptDir.exists(L"scripts") ){if ( scriptDir.create(L"Scripts") )this->addLogEntry(SPKINSTALL_CREATEDIRECTORY, L"Scripts", errors);elsethis->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 itUtils::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 itelse{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 listif ( newFile ){// first check if theres a matching onebool 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 itif ( !found )m_lUninstallFiles.push_back(newFile);elsedelete newFile;}}}uninstallFiles.clear();// remove all directies that we're not usingif ( 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 gapsif ( 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 listif ( !m_lInstallList.FindData(package) )m_lInstallList.push_back(package);// add all childrenCLinkList<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 depanciesif ( 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 wellif ( m_bAutoEnableChild ){CLinkList<CBaseFile> childList;this->GetChildPackages(package, &childList, true);// add all disabled packages to listfor ( 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 listfor ( 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 dependaciesif ( 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 listif ( !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 listCSpkFile *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 parentfor ( 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 listfor ( 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;elsewNode->Data()->iType = WARETYPE_ADDED;found = true;wNode->Data()->pPackage = p;wNode->Data()->sShipClass = p->shipClass();break;}}if ( found || !p->IsEnabled() )continue;// first find any freeSGameShip *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 placeif ( wNode->Data()->sShipID.Compare(p->shipID()) ){gw = wNode->Data();break;}}// none found, create oneif ( !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 listfor ( 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 listbool 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;elsewNode->Data()->iType = WARETYPE_ADDED;wNode->Data()->pWare = w;found = true;break;}}if ( found || !p->IsEnabled() )continue;// first find any freeSGameWare *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 placeif ( wNode->Data()->sWareName == w->sID ){gw = wNode->Data();break;}}// none found, create oneif ( !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 Reunionif ( 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 itif ( 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 themif ( 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 inUtils::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(catFile.fullFilename(), this->getAddonDir(), CATREAD_DAT, true) == CATERR_NONE){fakePatch.appendFile(fakeFile.fullFilename(), L"PlugMan\\TFake.pck", true, false);fakePatch.WriteCatFile();}}// backup existing mod files incase something happens to themif ( !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 patchm_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: " + Utils::WString::Number(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 Filesif ( !_lCreatedFiles.empty() ){for(auto itr = _lCreatedFiles.begin(); itr != _lCreatedFiles.end(); itr++)lines.pushBack(L"CreatedFile: " + (*itr)->str.findRemove(m_sCurrentDir));}// write uninstall filesif ( !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 changesif ( !_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 orderingif ( !_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 listlines.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:";elseline += 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 listfor ( 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>");elsecontinue;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 filesif ( 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 skipif ( package->IsEnabled() )return true;// check if parent is enabledif ( package->GetParent() && !package->GetParent()->IsEnabled() ){m_iError = PKERR_NOPARENT;return false;}// check for modifiedif ( 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 childrenCLinkList<C_File> fileList;int maxFiles = this->GetAllPackageFiles(&m_lEnableList, &fileList, false);int fileCount = 0;// we use this list to match cat and dat filesUtils::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 disabledif ( !f->IsDisabled() )continue;// make the directory if it dont existCDirIO Dir(m_sCurrentDir);// fake patches are in the root, no need for directoryif ( !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 renamedif ( f->IsFakePatch() ){// first check if the matching file has been done alreadyif (fakePatches.contains(f->baseName()))newFilename = fakePatches.findString(f->baseName()) + L"." + f->fileExt();// we need to find the next available number insteadelse{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 pairfakePatches.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 itelse{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 filenamef->setFilename(m_sCurrentDir + L"/" + newFilename);// no longer disabled, we need to remove the flagf->SetDisabled(false);}// recursive, auto enable all childrenCBaseFile *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 modif ( oldMod && oldMod != m_pEnabledMod && !m_bForceModInstall )this->disablePackage(oldMod, errors, progress);// lets remove all the directories we might have left emptyUtils::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 reshufflebool original = false, shuffle = false;// holds our list of directories that we might need to remove, only empty ones from this list will actually be removedUtils::WStringList removeDirs;// get all files, including childrenCLinkList<C_File> fileList;int maxFiles = this->GetAllPackageFiles(&m_lDisableList, &fileList, true);// interate through all the files in the packageint 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 fileif ( 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 againif ( 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 removedif ( 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 directoryCDirIO Dir(m_sCurrentDir);Utils::WString newFilename = L"PluginManager/Disabled/";// fake patches have thier own special directoryif ( f->IsFakePatch() )newFilename += L"FakePatches";// otherwise we put them in thier usual directory structure inside the disabled direlsenewFilename += f->getDirectory(NULL);// make sure the directory exists so we can move the fileif ( !Dir.exists(newFilename) ){// we couldn't create the directory for some reason, this is not goodif ( !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 happeningif ( f->IsFakePatch() ){// find package the fake patch belongs toif ( 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 filenameelsenewFilename = 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 errorCFileIO 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 itelse if ( !currentFile.Rename(newFilename) ){this->addLogEntry(SPKINSTALL_DISABLEFILE_FAIL, f->getNameDirectory(checkPackage), errors);continue;}// must have been finethis->addLogEntry(SPKINSTALL_DISABLEFILE, f->getNameDirectory(checkPackage), errors);original = _pOriginalFiles->restoreFile(f, errors);// extra file thats not in the extras directoryif (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 filenamef->setFilename(newFilename);// finally mark the file as disabled so we know not to try to move it againf->SetDisabled(true);}// a fake patch has been disabled, we need to reshuffle the rest to fill in any gapsif ( 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 emptyif ( original )removeDirs.pushBack(L"PluginManager/Original");// remove any empty directories that we might have leftif ( !removeDirs.empty() )removeUnusedDirectories(removeDirs, errors);// finally mark the whole package as disabled// recursive, we need to disable all childrenfor ( 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 hopem_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 skipif ( !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 pointerif ( !package )return 0;// we are checking against a SPK package, so we match the name and authorif ( 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 obviouslyreturn 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 packagesfor ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() ){CBaseFile *file = node->Data();// only look for archive packagesif ( 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 foundreturn 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 packagesfor ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() ){CBaseFile *file = node->Data();// only look for spk packagesif ( 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 foundreturn 0;}CBaseFile* CPackages::findPackage(const Utils::WString &name, const Utils::WString &author) const{// interate through all packagesfor (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 foundreturn 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 packagesfor ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() ){CBaseFile *file = node->Data();// only look for spk packagesif ( 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 foundreturn 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 filesCListNode<C_File> *fnode = m_lFiles.Front();while ( fnode ){fnode->Data()->clearUsed();fnode = fnode->next();}// update for all packagesfor ( 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 filesCListNode<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 versionCBaseFile *oldPackage = findPackage(package);// check versions are newerif (oldPackage && (check & IC_OLDVERSION)){if ( oldPackage->version().compareVersion(package->version()) == COMPARE_OLDER )return INSTALLCHECK_OLDVERSION;}// now check for game versionif ((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 modifiedif (m_bVanilla && (check & IC_MODIFIED)){if ( !package->IsSigned() )return INSTALLCHECK_MODIFIED;}return INSTALLCHECK_OK;}bool CPackages::CheckOtherPackage(CBaseFile *package){// check the need for another modif ( package->GetType() == TYPE_SPK ){CSpkFile *spk = (CSpkFile *)package;if ( spk->IsAnotherMod() ){if ( !findSpkPackage(spk->otherName(), spk->otherAuthor()) ){// check the install listif ( 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 modif ( 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;}elsereturn false;}}// check any dependaciesif ( 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 versionif (version.compareVersion(p->version()) == COMPARE_OLDER)return false;if ( onlyEnabled && !p->IsEnabled() )return false;return true;}// now check the prepared listif ( 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;}}}elsefound = 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;}}}elsefound = 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;}}}elsefound = false;}if ( !found ){if ( list )list->pushBack(spk->otherName(), spk->otherAuthor());++count;}}}return count;}int CPackages::CheckPreparedInstallRequired(CLinkList<CBaseFile> *list){// loop through all packagesint count = 0;for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() ){CBaseFile *p = node->Data();// no requirements found, remove from listbool 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);}elsenode->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 filebool 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 itif ( !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 filesif ( !file->IsShared() )continue;// only do ones that are no longer neededif ( 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 filesif ( !file->IsShared() )continue;// only do ones that are no longer neededif ( 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 enabledif ( f->IsDisabled() )continue;if ( f->GetFileType() != FILETYPE_TEXT )continue;// check if the file is an auto text fileif ( !f->isAutoTextFile() )continue;// we need to rename itint 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);}elsethis->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 InstalledCLinkList<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 packagesstd::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 packageif (!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 firstfor (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 othersfor (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 firstfor (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 addedbool 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 restif (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 listfor (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 lowestwhile ( 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 laterif (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 placeif ( lowestFile->baseName().toInt() != lowest ){// if the file already exists, we need to move it elsewhereUtils::WString nextName = Utils::WString::PadNumber(lowest, 2);if ( CFileIO::Exists(m_sCurrentDir + L"/" + nextName + L".cat") ){// find the file in our internal file listC_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 numbershufflePatchTo(moveFile, findLastFakePatch(), errors);}// space should be free, now lets shuffle itshufflePatchTo(lowestFile, lowest, errors);}doneList.push_back(lowestFile); // we've done this file nowlowest++; // 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 gapsstd::vector<C_File*> fakepatches;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 enabledif (f->IsDisabled())continue;// check if the file is a fake patchif (!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 checkif (!f->checkFileExt(L"cat"))continue;bool added = false;int num = f->baseName().toInt();for (auto itr = fakepatches.begin(); itr != fakepatches.end(); itr++){int checkNum = (*itr)->baseName().toInt();if (num < checkNum){fakepatches.insert(itr, f);added = true;break;}}if (!added)fakepatches.push_back(f);}for(auto itr = fakepatches.begin(); itr != fakepatches.end(); itr++){// now lets check if its greater than our gapint check = findNextFakePatch();int patchNum = (*itr)->filename().token(L".", 1).toInt();if ( patchNum <= check )continue;shufflePatchTo(*itr, check, errors);}}void CPackages::shufflePatchTo(C_File *file, int to, Utils::WStringList *errors){// it is, we need to shift this to fill the gapUtils::WString newName = Utils::WString::PadNumber(to, 2) + L"." + file->fileExt();// now rename the fileCFileIO moveFile(file->filePointer());if ( moveFile.Rename(m_sCurrentDir + L"/" + newName) ){// display moveingthis->addLogEntry(SPKINSTALL_FAKEPATCH, file->name() + L"~" + newName, errors);// now find the matching pairing if it existsfor ( 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 fileif ( 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);}elsethis->addLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->name() + L"~" + newName2, errors);}// finally make sure the internal name matches the new onefile->setName(newName);}elsethis->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 patchesfor ( 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 existsif ( !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 existsif ( !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 vfsif ( 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 existsif ( !Dir.exists(newFilename + L".pck") ){// it doen't, check if theres an unpacked fileif ( !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 setif ( !force && m_iLanguage )return;CDirIO Dir(m_sCurrentDir);// check for lang.dat fileif ( 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 useif ( !m_iLanguage )return;// find all text filesUtils::WStringList ids;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();// only do text fielsif ( 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";elselang = 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 oneif (!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 languagefor(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 idint 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 availableelse if ( num == 44 && useId != -1)useId = 44;// if we have a german language, and its not yet found english or nullelse if ( num == 49 && useId != 44 && useId != -1 )useId = 49;// if we have not found a valid language yet, we will use this oneelse 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 existsUtils::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 scriptsif ( addFile ){addTextFileToScripts(addFile, textid);return true;}// first we need to find our text fileUtils::WString filename;if ( languageid != -1 )filename = SPK::FormatTextName(textid.toInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));elsefilename = 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 fileCFileIO readFile(textFile->filePointer());std::vector<Utils::WString> lines;readFile.readLines(lines);// find the language id in the linesstd::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 listm_lFiles.push_back(addFile);// now add it to the scriptsaddTextFileToScripts(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 listfor ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() ){CBaseFile *package = node->Data();// first make sure it doesn't already existif ( 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;}elsereturn false;}}return true;}int CPackages::removeAllPackages(Utils::WStringList *errors, CProgressInfo *progress){int files = 0;// remove all filesint max = m_lFiles.size() + _pOriginalFiles->count() + m_lUninstallFiles.size();for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){// update the progressif ( 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 progressfiles = _pOriginalFiles->restoreAll(progress, files, max);// remove any uninstall files that remainfor ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() ){// update the progressif ( progress )progress->UpdateProgress(files, max);++files;removeFile(uNode->Data());delete uNode->Data();}m_lUninstallFiles.clear();// delete all packagesfor ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() ){// clear file list as we have removed them alreadypNode->Data()->GetFileList()->clear();// delete the memory for the packagedelete 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 theseif (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 versionsm_gameExe.Reset();Utils::WString exeFile = this->findDataDir(dir, L"exe");// if file exists, read it, otherwise, just addif (!exeFile.empty() && CFileIO::Exists(exeFile))m_gameExe.readFile(exeFile);else{m_gameExe.parseExe(L"x2.exe|0:5:1: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:1: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:1: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: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:3:NO_XOR|TC_TEXT|SETTINGFILE|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;elsedir = this->getProperDir(dir);CDirIO Dir(dir);// check for lang.dat fileif ( 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 modUtils::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 numberint 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 filefor (; 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 fileif (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 wareint wareTextID = WARETEXTSTART;for ( int i = 0; i < WAREBUFFERS; i++ ){// its empty, no need to create ware filesif ( !m_lGameWares[i].size() )continue;// lets extract the ware filewchar_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 memoryUtils::WStringList wareLines;size_t oldSize = -1;int version = -1;// read first numberCFileIO 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 bufferif ( m_iWareBuffer[i] <= 0 )m_iWareBuffer[i] = wareLines.size() + 10;// last resort, readjust the bufferelse if ( wareLines.size() > static_cast<size_t>(m_iWareBuffer[i]) )m_iWareBuffer[i] = wareLines.size();// add the bufferswhile ( 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 linesbool 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, ¬ority) ) 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 waressize_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 memoryUtils::WStringList wareLines;size_t oldSize = -1;int version = -1;// read first numberCFileIO 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 waresif ( (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, ¬o) ) {line = line.replaceToken(L";", 14, Utils::WString::Number(noto));}}// check for any override values for EMPif ( 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, ¬o) ) {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 entrieswhile ( 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 overridessize_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, ¬o) ) {empStr[i] = empStr[i].replaceToken(L";", 14, Utils::WString::Number(noto));}}// remove any empty end entrieswhile ( empStr[empEntries - 1].empty() )--empEntries;Utils::WStringList addAfter;if ( wareLines.size() > maxsize ){// force emp, remove entries to allow them to be addedif ( m_bForceEMP ){// more after empif ( 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 allwhile (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 entryif ( 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 waressize_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 filewareLines.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;std::wstring::size_type pos = sStr.toStdWString().find_first_of(L"&", 0);while ( pos != std::wstring::npos ) {// find the next space and next ;. If ; comes first, assume its acommandstd::wstring::size_type spacePos = sStr.toStdWString().find_first_of(L" ", pos);std::wstring::size_type colonPos = sStr.toStdWString().find_first_of(L";", pos);if ( colonPos != std::wstring::npos && colonPos < spacePos ) {// replace with <::command::> so they the & doesn't get replacedUtils::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 commandpos = sStr.toStdWString().find_first_of(L"&", pos + 1);}// replace the & nownewStr = newStr.findReplace(L"&", L"&");// restore the commandsfor(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 casestext = text.findReplace(L"(", L"\\(");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"<");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\">");elsewriteData.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\">");elsewriteData.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 headingint 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 textswriteData.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 packageint 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;if (p->author().Compare("PluginManager", false))continue;CSpkFile *spk = (CSpkFile *)p;// count text filesUtils::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";elsesLang = 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>");}}elsewriteData.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 namesif ( 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>");elsewriteData.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>");elsewriteData.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 infoif ( gs->pPackage ){int doStart = start + 5;for (ShipyardRace i = ShipyardRace::Argon; i <= ShipyardRace::Max; i = static_cast<ShipyardRace>(static_cast<unsigned int>(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 empif (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>");}// waresif ( 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\">");elsewriteData.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 namesfor ( 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 languageUtils::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();// write out the settings fileif (m_iGameFlags & EXEFLAG_SETTINGFILE){CDirIO Dir(m_sCurrentDir);if (!Dir.exists(L"types"))Dir.create(L"types");Dir.cd(L"types");std::vector<Utils::WString> writeData;CFileIO dataFile(Dir.file(L"manager.txt"));if (dataFile.exists())dataFile.remove();writeData.push_back(L"Data;" + Utils::WString::Number(m_iLastUpdated) + L";" + Utils::WString::Number(this->countPackages(TYPE_SPK, true)) + L";" + Utils::WString::Number(lWares.size()) + L";" + Utils::WString::Number(lShips.size()) + L";");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;if (p->author().Compare("PluginManager", false))continue;CSpkFile* spk = (CSpkFile*)p;// count text filesUtils::WStringList textEntries;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";elsesLang = 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)textEntries.pushBack(id);}f = p->GetNextFile(f);}CLinkList<SSettingType>* settings = spk->settingsList();Utils::WString str = L"Script;" + spk->name() + L";" + spk->author() + L";" + spk->version() + L";" + spk->name(lang) + L";" + Utils::WString::Number(textEntries.size());for (auto itr = textEntries.begin(); itr != textEntries.end(); itr++)str += L";" + (*itr)->str;str += L";" + Utils::WString::Number(settings ? settings->size() : 0);if (settings){for (CListNode<SSettingType>* sNode = settings->Front(); sNode; sNode = sNode->next()){SSettingType* st = sNode->Data();str += L";" + st->sKey + L";" + spk->getSetting(st);}}writeData.push_back(str + L";");}for (CListNode<SGameWare>* node = lWares.Front(); node; node = node->next()){SGameWare* w = node->Data();Utils::WString str;if (w->pWare && w->iType == WARETYPE_ADDED)str = this->convertTextString(w->sWareName);elsestr = L"-1";str += L";" + Utils::WString(w->cType) + L";" + Utils::WString::Number(w->iPos);writeData.push_back(L"Ware;" + str + L";");}for (CListNode<SGameShip>* node = lShips.Front(); node; node = node->next()){SGameShip* gs = node->Data();if (gs->iType == WARETYPE_NONE)continue;Utils::WString str;if (gs->pPackage && gs->iType == WARETYPE_ADDED)str = gs->sShipID;elsestr = L"-1";str += L";" + Utils::WString::Number(gs->iPos);// write shipyard infoif (gs->pPackage && gs->iType == WARETYPE_ADDED){for (ShipyardRace i = ShipyardRace::Argon; i <= ShipyardRace::Max; i = static_cast<ShipyardRace>(static_cast<unsigned int>(i) * 2)){if (gs->pPackage->isShipyard(i))str += L";" + GetShipyardName(i);}}writeData.push_back(L"Ship;" + str + L";");}dataFile.writeFileUTF(&writeData);}}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 directoryUtils::WString dir = (vanilla) ? L"Vanilla" : L"Modified";// make sure the directory existsCDirIO 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 savesUtils::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 existsif (gameSaveDir.exists((*itr)->str))CFileIO::Remove(gameSaveDir.file((*itr)->str));// copy the file into the games save dir for backupFile.copy(gameSaveDir.file(File.filename()), true);}}}}void CPackages::restoreSaves(bool vanilla){// get dir to restore fromif (!_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 gamesUtils::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 existsif (toDir.exists((*itr)->str)) CFileIO::Remove(toDir.file((*itr)->str));// move file overFile.copy(toDir.file((*itr)->str), true);}}}}}bool CPackages::RemoveCurrentDirectory(){if ( !m_bLoaded )return false;// remove all package filesthis->removeAllPackages();// remove all plugin manager filesthis->RemoveCreatedFiles();this->Reset();m_bLoaded = false;// clear the plugin manager directoryCDirIO 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 filesif ( 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 shipsif ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )return;CLinkList<SDummyEntry> dummyList;// now extract the existing dummiesint e = extractGameFile(L"types/Dummies.pck", m_sTempDir + L"/Dummies.txt");if ( e ){// read the dummiesCFileIO 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 sizewhile ( !line.empty() ){if ( !insection ){Utils::WString section = line.token(L";", 1);insection = line.token(L";", 2).toInt();// search for the sectionscurrentSection = 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 statesif ( 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 doneline = 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 doneline = line.remTokens(L";", 1, 3 + (states * 2));}}}}}File.remove();}// add the new entries for the shipsfor ( 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 listfor ( 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 listelse{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 filestd::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 diskCFileIO 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 shipsif ( 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 listauto& 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 diskCFileIO 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 shipsif ( 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 listfor(auto itr = s->pPackage->getAnimations().begin(); itr != s->pPackage->getAnimations().end(); itr++)parsedAniList.pushBack((*itr)->str);}// format the list with added spacesUtils::WStringList formatedAniList;int lineCount = -1;for(auto itr = parsedAniList.begin(); itr != parsedAniList.end(); itr++){// format the comment to match the line numberlineCount++;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);elsecomment += oldComment;}Utils::WString line = (*itr)->str.token(L"//", 1);// split into seperate linesUtils::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);elseformatedAniList.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);}}elseformatedAniList.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 diskCFileIO 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 shipsif ( 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 fileCLinkList<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 entriesfor ( 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 listfor(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 intoSBodies *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 filestd::vector<Utils::WString> writeList;// the header firstwriteList.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 sectionsfor ( 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 diskCFileIO 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 startsif ( node->Data()->GetType() != TYPE_SPK )continue;CSpkFile *p = (CSpkFile *)node->Data();// only use custom startsif ( !p->IsCustomStart() )continue;// get the name of the start to useUtils::WString name = p->customStartName();if ( name.empty() )continue;// find if maps file existsUtils::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 filesint 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 shipsif ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )return;CLinkList<SComponantEntry> dummyList;// now extract the existing dummiesint e = extractGameFile(L"types/Components.pck", m_sTempDir + L"/Components.txt");if ( e ){// read the dummiesCFileIO 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 sizewhile ( !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 sectionscurrentSection = 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 shipsfor ( 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 listfor ( 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 sectionelse{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 filestd::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 diskCFileIO 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 commentsif (str.contains(L"/") )str = str.token(L"/", 1);if ( entries == -1 )entries = str.token(L";", 1).toInt();elseglobals.pushBack(str.token(L";", 1), str.token(L";", 2));}return true;}}}return false;}void CPackages::CreateGlobals(){if (_lGlobals.empty()){bool anyGlobals = false;for (CListNode<CBaseFile>* node = m_lPackages.Front(); node; node = node->next()){if (node->Data()->IsEnabled() && node->Data()->getGlobals().size()){anyGlobals = true;break;}}if(!anyGlobals)return; // no global settings}Utils::WStringList globals;if (readGlobals(globals)){// apply package settingsfor (CListNode<CBaseFile>* node = m_lPackages.Front(); node; node = node->next()){CBaseFile* p = node->Data();if (p->IsEnabled()){auto& list = p->getGlobals();for (auto itr = list.begin(); itr != list.end(); itr++){if (!globals.changeData((*itr)->str, (*itr)->data))globals.pushBack((*itr)->str, (*itr)->data);}}}// apply out settingsfor (auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++){if(!globals.changeData((*itr)->str, (*itr)->data))globals.pushBack((*itr)->str, (*itr)->data);}// now write itUtils::WStringList writeList;for(auto itr = globals.begin(); itr != globals.end(); itr++)writeList.pushBack((*itr)->str + L";" + (*itr)->data + L";");// finally, write the filewriteList.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 turretsUtils::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 fileint 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 oneCFileIO 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 overridesbool 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 bufferif ( !m_iShipBuffer )m_iShipBuffer = tshipsList.size() + 15;// there seems to be too many additional entries, we have no choise but to change the bufferelse 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;";elsebufferStart = L"0;0;0;0;0;SG_SH_M5;499999;0;0;102;0;11;373;0;112;247;ships\\argon\\argon_TL_scene;ships\\argon\\cockpits\\argon_tl_cp_scene;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;37;38;0;0;0;OBJ_SHIP_M5;1;1;-1;ships\\argon\\argon_TL;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";// add the buffers nowfor ( int i = tshipsList.size(); i < m_iShipBuffer; i++ )tshipsList.push_back(bufferStart + L"SHIP_BUFFER;");// now lets add our tships linefor ( 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;");elsetshipsList.push_back(bufferStart + L"SHIP_SPACER;");}// finally, write the filetshipsList.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 fileCFileIO 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 fileCFileIO 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 renameif ( 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 onesif ( !catFile.appendFile(File->fullFilename(), filename, true, true) )return false;return true;}}else{// compress the filesize_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 shipsif ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )return NULL;// now extract the existing cockpitsint fileType = 51;int e = extractGameFile(L"types/TCockpits.pck", m_sTempDir + L"/TCockpits.txt");if ( e ){// read the dummiesCFileIO 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 onesfor ( 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 entryfor ( 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 setif ( foundEntry )(*itr)->str = cockpitStr;found = true;break;}}if ( !found )list.pushBack(cockpitStr);}}// finally, write the filelist.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 entryslist.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 existsif ( !CFileIO::Exists(filename) )return NULL;// read all the linesCFileIO 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 fileUtils::WString line = File.readEndOfLine();// filter out any characters we dont really wantline.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 lineif ( !line.contains(L':') ){// there are some exeptions, and these are one word entrys onlyline.removeEndSpace();if ( line.contains(L" ") ){if ( malformedLines )malformedLines->pushBack(line, Utils::WString::Number(iLine));continue;}}// check for the type lineif ( !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 dayUtils::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 variablesif ( 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 variblesif ( 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 dateif ( 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);}// mydocumentsif ( rest.contains(L"$MYDOCUMENTS") ){if ( !m_sMyDoc.empty() )rest = rest.findReplace(L"$MYDOCUMENTS", m_sMyDoc);}// current pathif ( 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 valuesif ( 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;elseftpaddr = 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");elsebreak;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 gameif (!p->CheckGameCompatability(this->GetGame())){delete p;continue;}// check if its already on the listbool 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 displayif (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 contentsCFileIO File(dir + L"/accessrightscheck.dat");// check if file exists and remove itif ( File.exists() ){// if we cant remove it, we dont have enough rightsif ( !File.remove() )return false;// if its still there, we dont have enough rightsif ( File.exists() )return false;}// now create the fileif ( !File.writeString("testing access rights") )return false;// now check it existsif ( !File.exists() )return false;// now read the file for the correct contentsstd::vector<Utils::WString> lines;if (!File.readLines(lines))return false;// check that one of the lines is correctfor(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 fileif (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 fileelse 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;}elseFile.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 modif ( 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 timefor (unsigned int i = 0; i < cat.GetNumFiles(); i++ ){SInCatFile *f = cat.GetFile(i);Utils::WString sF = f->sFile;// is a text filesF = sF.findReplace(L"\\", L"/");if ( !sF.token(L"/", 1).Compare(L"t") )continue;Utils::WString baseFile = CFileIO(sF).baseName();// check languageint lang = 0;if ( baseFile.findPos(L"-L") != -1 ) // new language filelang = baseFile.right(3).toInt();else{baseFile.truncate((int)baseFile.length() - 4);lang = baseFile.toInt();}if ( lang != m_iLanguage )continue;// now extract and parseif ( 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 fileelse 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;}elseFile.open(file);if ( !File.exists() )return false;// open and read filestd::vector<Utils::WString> lines;if(!File.readLines(lines))return false;bool inPage = false;for(auto itr = lines.begin(); itr != lines.end(); itr++){// search for pageif ( !inPage ){if (itr->findPos(L"<page") > -1 ){// find the page idint 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 idelse{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 filesif (ext.Compare(L"cat") || ext.Compare(L"dat"))return FILETYPE_MOD;// check for text filesif ( 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 fileif ( basename.length() >= 5 && basename.length() <= 8 && ((int)File.baseName()) )return FILETYPE_TEXT;if ( filetype == FILETYPE_TEXT ) // should no longer be anything textreturn 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" modfor (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 secondaryfor (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 typeif(!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 firstUtils::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 doneif ( !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 fileif ( !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 modCCatFile 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 thisif ( 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));elsereturn 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 fileif ( !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 fileUtils::WStringList fromList;if (getModCompatabilityList(from, &fromList)){Utils::WStringList toList;if (getModCompatabilityList(to, &toList)){// both have files we need to check, compare themfor(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);elsereturn 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 otherif (from->isPackageNeeded(to->name(), to->author())) return false;if (to->isPackageNeeded(from->name(), from->author())) return false;// check if one is directly connectedif (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 againstif ( !compareFile->IsFakePatch() ) continue;if ( compareFile->fileExt().Compare(L"dat") ) continue;// now we have to files to compareif (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 filesfor ( 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 packagesint 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 selfif (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 gamestd::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 itif ( 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 valuesif (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();}elsep->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 foundconst 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 fileCDirIO 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 frontline.removeChar('\t');line.removeChar('\r');Utils::WString linenospace = line;linenospace.removeFirstSpace();if ( linenospace.empty() )continue;// check for any commentsif ( 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 lineif ( !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).toStdWString().compare(L"PCK")){checkType = checkType.left(-3);packed = true;}// now check type nameint 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 fileCBaseFile *archive = NULL;#ifdef _RARHANDLE hArcData;int RHCode,PFCode;wchar_t CmtBuf[16384];struct RARHeaderDataEx HeaderData;struct RAROpenArchiveDataEx OpenArchiveData;// find the pluginmanager text to covnert to spkfileif ( toInstall ) {memset(&OpenArchiveData,0,sizeof(OpenArchiveData));OpenArchiveData.ArcNameW=(wchar_t *)filename.c_str();OpenArchiveData.CmtBufW=CmtBuf;OpenArchiveData.CmtBufSize = sizeof(CmtBuf) / sizeof(CmtBuf[0]);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);}memset(&OpenArchiveData,0,sizeof(OpenArchiveData));OpenArchiveData.ArcNameW = (wchar_t*)filename.c_str();OpenArchiveData.CmtBufW = CmtBuf;OpenArchiveData.CmtBufSize = sizeof(CmtBuf) / sizeof(CmtBuf[0]);OpenArchiveData.OpenMode=RAR_OM_EXTRACT;OpenArchiveData.UserData=EXTRACT;hArcData=RAROpenArchiveEx(&OpenArchiveData);if (OpenArchiveData.OpenResult!=0) return NULL;if (toInstall)archive = new CArchiveFile(); // just installing an archive fileelsearchive = new CSpkFile(); // converting to a spk fileHeaderData.CmtBuf=NULL;memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));CDirIO outputDir(m_sTempDir + L"/Extract");if (!outputDir.exists())outputDir.create();bool error = false;while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0){Utils::WString fileName(HeaderData.FileNameW);if ( HeaderData.FileAttr == 16 )continue;PFCode = RARProcessFileW(hArcData, RAR_EXTRACT, outputDir.dir(), NULL);if (PFCode!=0){error = true;break;}CFileIO File(outputDir.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 typesC_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);elsef = archive->addFile(CFileIO(fileName).filename(), extradir, static_cast<FileType>(type));f->readFromFile(File.fullFilename());}File.remove();}}RARCloseArchive(hArcData);if (outputDir.exists())outputDir.removeDir(L".", true, true);if ( error ){delete archive;archive = NULL;}#endifreturn archive;}bool CPackages::_check_archive_fromZip(const Utils::WString& filename) const{HZIP hz = OpenZip(filename.c_str(), 0);if (!hz)return false;bool found = false;int index;// move the files from the zip to the packageZIPENTRY ze;if (FindZipItem(hz, L"pluginmanager.txt", true, &index, &ze) == Z_OK)found = true;CloseZip(hz);return found;}CBaseFile* CPackages::_archive_fromZip(const Utils::WString& filename, bool toInstall) const{CBaseFile *archive = NULL;HZIP hz = OpenZip(filename.c_str(), 0);if ( !hz )return NULL;int index;// move the files from the zip to the packageZIPENTRY ze;if ( FindZipItem(hz, L"pluginmanager.txt", true, &index, &ze) == Z_OK )toInstall = false;CloseZip(hz);hz = OpenZip(filename.c_str(), 0);if ( !hz )return NULL;// create the correct packageif ( toInstall )archive = new CArchiveFile(); // just installing an archive fileelsearchive = new CSpkFile(); // converting to a spk fileGetZipItem(hz, -1, &ze);int numitems = ze.index;std::set<unsigned int> games;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 directorieschar *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 fromif ( 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 typesif ( 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 addonsif (!dir.empty()){Utils::WString first = dir.token(L"/", 1);int g = m_gameExe.findAddonType(first);if (g != -1){game = g + 1;games.insert(game);}}// TODO: Check if an existing file exists with a different gameif ( type == -1 )f = archive->addFile(filename, dir, FILETYPE_EXTRA, game > 0 ? 1 << game : 0);elsef = archive->addFile(filename, extradir, static_cast<FileType>(type), game > 0 ? 1 << game : 0);if (f){if (!game && f->isFileInAddon())games.insert(1);f->SetData((const unsigned char*)iBuf, ze.unc_size);}elsedelete[] iBuf;}}CloseZip(hz);if ( error ){delete archive;archive = NULL;}// if there is only one game, then set all the files to "All Games"//if (games.size() <= 1){for (auto itr = archive->fileList().Front(); itr; itr = itr->next())itr->Data()->setGame(0);}return archive;}CBaseFile *CPackages::createFromArchive(const Utils::WString &filename, bool toInstall) const{// make sure we can open the zip fileCBaseFile *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);elsearchive = this->_archive_fromZip(filename, false);if ( archive ) {archive->setFilename(CFileIO(filename).changeFileExtension(L"spk"));if (archive->name().empty()){if (toInstall)archive->setName(CFileIO(filename).filename());elsearchive->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 scriptif ( 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;}