Rev 310 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "spk.h"
#include "emp.h"
#include <time.h>
#include <vector>
#include "OriginalFiles.h"
#include "spkdef.h"
#include <set>
#ifdef _RAR
#include <unrar.h>
#endif
enum {READ_START, READ_GLOBAL, READ_SCRIPT, READ_SCRIPTFILE, READ_WARES};
enum { EXTRACT, TEST, PRINT, LIST };
typedef struct SDummyEntry {
Utils::WString sSection;
Utils::WStringList lEntries;
} SDummyEntry;
typedef struct SComponantEntry2 {
Utils::WString sSection;
Utils::WStringList lEntries;
} SComponantEntry2;
typedef struct SComponantEntry {
Utils::WString sSection;
CLinkList<SComponantEntry2> lEntries;
} SComponantEntry;
Utils::WString CPackages::m_sTempDir;
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
CPackages::CPackages() : m_pCurrentGameExe(NULL),
_pOriginalFiles(NULL),
_pCurrentDir(NULL)
{
m_bRenameText = false;
m_iLanguage = 0;
m_iGame = -1;
m_iGameVersion = -1;
m_iGameFlags = 0;
m_bSurpressProtectedWarning = false;
m_iMaxPatch = 1;
m_iLastUpdated = 0;
m_iFakePatch = -1;
m_bVanilla = true;
m_pEnabledMod = NULL;
m_bForceModInstall = false;
m_bLoaded = false;
m_bRedo = false;
m_bOldPlugin = false;
m_bUsedWare = false;
m_pPackageNode = NULL;
m_bRemoveDir = false;
m_bDisableVanilla = false;
m_bLoadVFS = true;
m_iSaveGame = -1;
m_iSaveGameManager = -1;
m_bForceEMP = false;
for ( int i = 0; i < WAREBUFFERS; i++ )
m_iWareBuffer[i] = 0;
m_iShipBuffer = 0;
}
CPackages::~CPackages ()
{
m_lDisableList.clear();
m_lEnableList.clear();
this->Reset();
}
void CPackages::setCurrentDir(const Utils::WString &dir)
{
m_sCurrentDir = dir;
if (_pCurrentDir) delete _pCurrentDir;
_pCurrentDir = new GameDirectory();
_pCurrentDir->dir = dir;
_pCurrentDir->id = -1;
m_gameExe.getDirectoryData(_pCurrentDir);
_pCurrentDir->langid = this->getGameLanguage(_pCurrentDir->dir);
if (_pCurrentDir->langid > 0)
_pCurrentDir->langname = this->ConvertLanguage(_pCurrentDir->langid);
updateFoundPackages(L"");
}
void CPackages::SetVanilla(bool b)
{
if ( m_bVanilla != b && b ) {
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() ) {
pNode->Data()->SetModifiedEnabled(pNode->Data()->IsEnabled());
}
}
m_bVanilla = b;
}
void CPackages::setMod(const Utils::WString &mod)
{
if (mod != L"PluginManager")
m_sSetMod = mod;
}
void CPackages::setSaveGameManager(bool managed)
{
m_iSaveGameManager = managed ? 1 : 0;
if (managed)
{
// find new save directory
CDirIO dir(this->saveDirectory());
if (dir.exists())
{
int id = 1;
while (dir.exists(Utils::WString::PadNumber(id, 4)))
++id;
Utils::WString d = Utils::WString::PadNumber(id, 4);
dir.create(d);
_sSaveDir = d;
CDirIO destDir1(dir.dir(d));
destDir1.create(L"Vanilla");
destDir1.cd(L"Vanilla");
CDirIO destDir2(dir.dir(d));
destDir2.create(L"Modified");
destDir2.cd(L"Modified");
Utils::WStringList files;
if (dir.dirList(files, Utils::WString::Null(), L"*.sav"))
{
for (auto itr = files.begin(); itr != files.end(); ++itr)
{
Utils::WString f = dir.file((*itr)->str);
CFileIO(f).copy(destDir1.file((*itr)->str));
CFileIO(f).copy(destDir2.file((*itr)->str));
}
}
}
}
else
{
_sSaveDir.clear();
}
}
void CPackages::Reset()
{
m_lFiles.MemoryClear();
m_lUninstallFiles.MemoryClear();
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
pNode->Data()->GetFileList()->clear();
m_lPackages.MemoryClear();
SafeDelete(_pOriginalFiles);
m_iFakePatch = -1;
m_bVanilla = false;
m_pEnabledMod = NULL;
m_bLoaded = false;
m_iLanguage = 0;
m_bOldPlugin = false;
m_bRemoveDir = false;
m_bDisableVanilla = false;
m_bSurpressProtectedWarning = false;
m_iSaveGame = -1;
m_iSaveGameManager = -1;
m_sSetMod = L"";
for ( int i = 0; i < WAREBUFFERS; i++ )
{
m_iWareBuffer[i] = 0;
m_lGameWares[i].MemoryClear();
}
m_iShipBuffer = 0;
m_lGameShips.MemoryClear();
m_lAvailablePackages.MemoryClear();
m_lFoundPackages.MemoryClear();
m_lInstallList.MemoryClear();
m_lEnableList.MemoryClear();
m_lDisableList.MemoryClear();
_lCreatedFiles.clear();
_lNonRemovedFiles.clear();
_lGlobals.clear();
_lFakePatchOrder.clear();
if (_pCurrentDir)
delete _pCurrentDir;
_pCurrentDir = NULL;
}
void CPackages::LoadVirtualFileSystem()
{
if ( !m_bLoadVFS )
return;
m_pGameVFS.setAddon(this->getAddonDir());
if ( m_pEnabledMod )
{
C_File *f;
for ( f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); f; f = m_pEnabledMod->GetNextFile(f) )
{
if (f->IsFakePatch()) continue;
if (f->checkFileExt(L"cat")) break;
}
if ( f )
m_pGameVFS.LoadFilesystem(m_sCurrentDir, f->filePointer(), 0);
else
m_pGameVFS.LoadFilesystem(m_sCurrentDir, 0);
}
else
m_pGameVFS.LoadFilesystem(m_sCurrentDir, 0);
}
bool CPackages::isOldDir(const Utils::WString &dir)
{
bool oldPlugin = false;
CFileIO datFile(dir + L"/PluginManager/pluginmanager.dat");
if ( datFile.exists() )
{
std::vector<Utils::WString> readFile;
if(datFile.readLines(readFile))
{
for ( int i = 0; i < (int)readFile.size(); i++ )
{
Utils::WString line(readFile.at(i));
Utils::WString cmd = line.token(L":", 1);
if ( cmd.Compare(L"<script>") || cmd.Compare(L"</scripts>") )
break;
else if ( cmd.Compare(L"spkinstaller") || cmd.Compare(L"globalfiles") )
break;
else if ( cmd.Compare(L"pluginmanager") )
{
oldPlugin = true;
break;
}
}
}
}
return oldPlugin;
}
bool CPackages::read(const Utils::WString& dir, CProgressInfo* progress)
{
m_sCurrentDir = dir;
m_sCurrentDir = m_sCurrentDir.findReplace(L"\\", L"/");
this->setCurrentDir(dir);
m_bOldPlugin = false;
_lCreatedFiles.clear();
m_bRemoveDir = false;
m_bSurpressProtectedWarning = false;
m_iSaveGame = -1;
m_iSaveGameManager = -1;
_lNonRemovedFiles.clear();
_lGlobals.clear();
_lFakePatchOrder.clear();
if ( _pOriginalFiles ) delete _pOriginalFiles;
_pOriginalFiles = new COriginalFiles(m_sCurrentDir);
m_bVanilla = true;
// check the pluginmanager data file exists
CFileIO datFile(m_sCurrentDir + L"/PluginManager/pluginmanager.dat");
if ( datFile.exists() )
{
std::vector<Utils::WString> readFile;
if(datFile.readLines(readFile))
{
float fVersion = 0;
bool spkinstaller = false;
float fBeta = 0;
int iStatus = READ_START;
int iCount = 0;
int iWare = -1;
int iShip = -1;
CBaseFile *packageFile = 0;
for ( int i = 0; i < (int)readFile.size(); i++ )
{
Utils::WString line(readFile.at(i));
Utils::WString cmd = line.token(L":", 1).lower();
Utils::WString rest = line.tokens(L":", 2).removeFirstSpace();
if ( iStatus == READ_GLOBAL )
{
if ( cmd == L"<script>" )
{
packageFile = new CSpkFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == L"<ship>" )
{
packageFile = new CXspFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == L"<base>" )
{
packageFile = new CBaseFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == L"<archive>" )
{
packageFile = new CArchiveFile();
iStatus = READ_SCRIPT;
}
else if ( cmd != L"</scripts>" )
{
int game = 0;
bool disabled = false;
Utils::WString fileName = line.tokens(L":", 5);
Utils::WString origName;
if ( fileName.left(2) == L"D#" )
{
fileName.erase(0, 2);
disabled = true;
}
if ( fileName.left(2) == L"G#" ) {
fileName.erase(0, 2);
game = fileName.token(L"#", 1).toLong();
fileName = fileName.tokens(L"#", 2);
}
if ( fileName.isin(L"O#") )
{
origName = fileName.token(L"O#", 2);
fileName = fileName.token(L"O#", 1);
}
C_File *newFile = new C_File(m_sCurrentDir + fileName);
newFile->setGame(game);
newFile->SetOriginalName(origName);
newFile->SetDisabled(disabled);
newFile->setFileType(static_cast<FileType>(line.token(L":", 1).toLong()));
newFile->SetCreationTime(static_cast<time_t>(line.token(L":", 2).toLong()));
newFile->setDir(line.token(L":", 3));
if ( line.token(L":", 4).toLong() )
newFile->SetShared(true);
newFile->UpdateSigned();
m_lFiles.push_back(newFile);
}
}
else if ( iStatus == READ_SCRIPT )
{
if ( cmd == L"installspk" )
packageFile->setFilename(rest);
else if ( cmd == L"files" )
{
iStatus = READ_SCRIPTFILE;
if ( spkinstaller )
{
std::vector<Utils::WString> files;
rest.tokenise(L" ", files);
for (size_t i = 0; i < files.size(); i++ )
{
int fileNum = files[i].toLong();
if ( fileNum >= 0 && fileNum < m_lFiles.size() )
packageFile->AddFile(m_lFiles[fileNum]);
}
}
}
else if ( cmd == L"disabled" )
packageFile->SetEnabled(false);
else if ( cmd == L"modifieddisabled" )
packageFile->SetModifiedEnabled(false);
else if ( cmd == L"icon" )
{
C_File *icon = new C_File(rest.tokens(L" ", 2));
packageFile->setIcon(icon, rest.token(L" ", 1));
}
else
packageFile->parseValueLine(line);
}
else if ( iStatus == READ_SCRIPTFILE )
{
if ( cmd == L"<script>" )
{
if ( packageFile->IsMod() && packageFile->IsEnabled() )
m_pEnabledMod = packageFile;
_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;
else
m_iWareBuffer[i] = rest.token(L" ", i + 1).toLong();
}
}
else if ( cmd == L"createdfile" )
_lCreatedFiles.pushBack(rest);
else if ( cmd == L"wares" )
{
iWare = rest.token(L" ", 1).toLong();
iCount = rest.token(L" ", 2).toLong();
}
else if ( cmd == L"ships" )
{
iShip = 1;
iCount = rest.token(L" ", 1).toLong();
}
else if ( cmd == L"modified" )
m_bVanilla = false;
else if ( cmd == L"spkinstaller" )
{
fVersion = rest.toFloat();
spkinstaller = true;
}
else if ( cmd == L"betaversion" )
fBeta = rest.toFloat();
else if ( cmd == L"uninstall" )
{
C_File *uf = new C_File();
uf->setFileType(FILETYPE_SCRIPT);
uf->SetCreationTime(rest.token(L" ", 1).toLong());
uf->setFilename(m_sCurrentDir + L"/scripts/" + rest.tokens(L" ", 2));
m_lUninstallFiles.push_back(uf);
}
else if ( cmd == L"nonremovedfile" )
{
if ( !CFileIO::Remove(rest) )
_lNonRemovedFiles.pushBack(rest);
}
else if ( cmd == L"original" )
_pOriginalFiles->parse(rest);
else if ( cmd.Compare(L"GlobalSetting") )
_lGlobals.pushBack(rest.token(L":", 1), rest.tokens(L":", 2));
else if ( cmd.Compare(L"FakePatchOrder") )
_lFakePatchOrder.pushBack(rest.token(L":", 1), rest.tokens(L":", 2));
else if ( cmd.Compare(L"EMPPriceOverride") )
this->addEMPPriceOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
else if ( cmd.Compare(L"EMPNotoOverride") )
this->addEMPNotoOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
else if ( cmd.Compare(L"BuiltInWarePriceOverride") )
this->addBuiltInWarePriceOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
else if ( cmd.Compare(L"BuiltInWareNotoOverride") )
this->addBuiltInWareNotoOverride(rest.token(L" ", 1).toLong(), rest.token(L" ", 2).toLong());
else if ( cmd.Compare(L"CustomWarePriceOverride") )
this->addCustomWarePriceOverride(rest.token(L";", 1), rest.token(L";", 2).toLong());
else if ( cmd.Compare(L"CustomWareNotoOverride") )
this->addCustomWareNotoOverride(rest.token(L";", 1), rest.token(L";", 2).toLong());
else if ( cmd == L"globalfiles" )
iStatus = READ_GLOBAL;
if ( progress )
{
progress->UpdateProgress((long)i, static_cast<long>(readFile.size()));
}
}
readFile.clear();
if ( !spkinstaller )
m_bRedo = true;
}
}
if ( m_pEnabledMod && !m_pEnabledMod->IsEnabled() )
m_pEnabledMod = NULL;
m_iGame = m_gameExe.getGameType(m_sCurrentDir) + 1;
m_pCurrentGameExe = m_gameExe.game(m_gameExe.getGameType(m_sCurrentDir));
Utils::WString sGameVersion;
m_iGameVersion = m_gameExe.getGameVersion(m_sCurrentDir, &sGameVersion) + 1;
_sGameVersion = sGameVersion;
m_iGameFlags = m_gameExe.gameFlags(m_iGame - 1);
m_iMaxPatch = m_gameExe.maxPatch(m_iGame - 1);
// find the fake patch
if ( !ReadyFakePatch() )
return false;
this->RemoveCreatedFiles();
this->createPluginManagerOpenText();
// match up wares
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( node->Data()->GetType() == TYPE_SPK )
{
CSpkFile *p = (CSpkFile *)node->Data();
for ( CListNode<SWares> *wNode = p->GetWaresList()->Front(); wNode; wNode = wNode->next() )
{
SWares *w = wNode->Data();
for ( CListNode<SGameWare> *gNode = m_lGameWares[CPackages::ConvertWareType(w->cType)].Front(); gNode; gNode = gNode->next() )
{
if ( w->sID == gNode->Data()->sWareName )
{
gNode->Data()->pWare = w;
break;
}
}
}
}
else if ( node->Data()->GetType() == TYPE_XSP )
{
CXspFile *p = (CXspFile *)node->Data();
for ( CListNode<SGameShip> *gNode = m_lGameShips.Front(); gNode; gNode = gNode->next() )
{
if ( p->shipID().Compare(gNode->Data()->sShipID) )
{
gNode->Data()->pPackage = p;
break;
}
}
}
}
readAvailablePackages();
// check the purged time
this->PurgeGameObjects();
m_bLoaded = true;
return true;
}
void CPackages::RemoveCreatedFiles()
{
for(auto itr = _lCreatedFiles.begin(); itr != _lCreatedFiles.end(); itr++)
{
if ( CFileIO::Exists((*itr)->str) )
CFileIO::Remove((*itr)->str);
else if ( CFileIO::Exists(m_sCurrentDir + L"/" + (*itr)->str) )
CFileIO::Remove(m_sCurrentDir + L"/" + (*itr)->str);
}
_lCreatedFiles.clear();
}
void CPackages::PurgeGameObjects()
{
// check for the log file
Utils::WString logDir = logDirectory();
CFileIO LogFile(logDir + L"/log0" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L".txt");
if ( LogFile.exists() )
{
// read the log file to memory
std::vector<Utils::WString> lines;
if(LogFile.readLines(lines))
{
for ( int i = 0; i < (int)lines.size(); i++ )
{
Utils::WString line(lines.at(i));
Utils::WString start = line.token(L":", 1).toLower();
Utils::WString rest = line.token(L":", 2).removeFirstSpace();
if ( start.Compare(L"purged") )
{
long time = rest.toLong();
if ( time == m_iLastUpdated )
{
this->PurgeWares();
this->PurgeShips();
this->removeUninstallScripts();
}
}
}
}
// remove the log file
LogFile.remove();
}
}
void CPackages::PurgeWares()
{
// mark all delete wares as available
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( !w ) continue;
if ( w->iType == WARETYPE_DELETED )
w->iType = WARETYPE_NONE;
}
m_lGameWares[i].RemoveEmpty();
}
}
void CPackages::PurgeShips()
{
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->iType == WARETYPE_DELETED )
node->Data()->iType = WARETYPE_NONE;
}
m_lGameShips.RemoveEmpty();
}
Utils::WString CPackages::logDirectory()
{
Utils::WString logDir = m_sCurrentDir;
if ( m_iGameFlags & EXEFLAG_MYDOCLOG ) {
SGameExe *exe = m_gameExe.game(m_iGame - 1);
if ( exe ) {
if ( !exe->sMyDoc.empty() )
logDir = m_sMyDoc + L"/" + exe->sMyDoc;
}
}
return logDir;
}
Utils::WString CPackages::logDirectory(const Utils::WString &gameExe)
{
Utils::WString logDir = m_sCurrentDir;
if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
{
SGameExe *exe = m_gameExe.gameExe(CFileIO(gameExe).filename());
if ( exe )
{
if ( !exe->sMyDoc.empty() )
logDir = m_sMyDoc + L"/" + exe->sMyDoc;
}
}
return CFileIO(logDir).fullFilename();
}
Utils::WString CPackages::saveDirectory()
{
Utils::WString logDir = this->logDirectory();
if ( m_iGameFlags & EXEFLAG_NOSAVESUBDIR )
return logDir;
return logDir + L"/save";
}
bool CPackages::ReadyFakePatch()
{
// already exists, lets skip it
if ( CFileIO::Exists(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat") && CFileIO::Exists(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat") )
return true;
// if only one of them exists, lets remove them
if ( CFileIO::Exists(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat") )
CFileIO::Remove(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat");
if ( CFileIO::Exists(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat") )
CFileIO::Remove(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat");
// fake patch is not being used
if ( m_iFakePatch < 0 ) return true;
// now lets find the fake patch
Utils::WString useFile;
if ( m_iFakePatch > 0 )
{
Utils::WString file = Utils::WString::PadNumber(m_iFakePatch, 2);
if (checkValidPluginManagerFile(file))
useFile = file;
else if (checkIfPluginManagerFile(file))
useFile = file;
}
if ( useFile.empty() )
{
int nextfree = this->findNextFakePatch();
--nextfree; // gets the end fake patch
// work backwards till we find a valid one
while ( nextfree > m_iMaxPatch )
{
Utils::WString file = Utils::WString::PadNumber(nextfree, 2);
if (checkValidPluginManagerFile(file))
{
useFile = file;
break;
}
--nextfree;
}
}
Utils::WString addonDir = this->getAddonDir();
// couldn't find the correct file, lets search for it
if ( useFile.empty() ) {
int nextfree = this->findNextFakePatch();
--nextfree; // gets the end fake patch
// work backwards till we find a valid one
while ( nextfree > m_iMaxPatch )
{
Utils::WString file = Utils::WString::PadNumber(nextfree, 2);
if (checkIfPluginManagerFile(file))
{
useFile = file;
break;
}
--nextfree;
}
}
if ( !useFile.empty() )
{
// lets check whats in the file first
CCatFile openCat;
if ( openCat.open((m_sCurrentDir + L"/" + useFile + L".cat"), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
std::vector<SInCatFile *> *files = openCat.GetFiles();
bool found = false;
if ( files )
{
Utils::WString useAddonDir = addonDir;
if ( !useAddonDir.empty() ) useAddonDir += L"\\";
for (auto itr = files->cbegin(); itr != files->cend(); itr++)
{
if ((*itr)->sFile.Compare(L"PlugMan\\TFake.pck") )
continue;
if ((*itr)->sFile.Compare(useAddonDir + L"t\\44" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L".pck"))
continue;
if ((*itr)->sFile.Compare(useAddonDir + L"t\\" + Utils::WString::PadNumber(PMTEXTFILE, 4) + L"-L044.pck"))
continue;
found = true;
break;
}
}
// no files, jsut delete them
CFileIO catFile(m_sCurrentDir + L"/" + useFile + L".cat");
if ( !files || !found )
{
if ( catFile.remove() )
{
CFileIO datFile(m_sCurrentDir + L"/" + useFile + L".dat");
if ( datFile.remove() )
return true;
}
}
else
{
if ( catFile.Rename(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat") )
{
CFileIO datFile(m_sCurrentDir + L"/" + useFile + L".dat");
if ( datFile.Rename(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.dat") )
return true;
// TODO: it failed, restore cat file and do error
}
}
}
// if we're here, we tryed, and failed
return false;
}
// no files found, but we have a fake patch ? restore from backup
else if ( m_iFakePatch > 0 ) {
CLog::log(CLog::Log_Directory, 1, L"PlugMan FakePatch seems to be missing (" + Utils::WString::Number(m_iFakePatch) + L"), Restoring from backup");
CFileIO catFile(m_sCurrentDir + L"/PluginManager/Backup/PlugMan_Fake.cat");
if ( catFile.exists() ) {
catFile.copy(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat");
CFileIO datFile(m_sCurrentDir + L"/PluginManager/Backup/PlugMan_Fake.dat");
if ( datFile.exists() ) datFile.copy(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.dat");
}
}
// otherwise we didn't need to (hopefully)
return true;
}
bool CPackages::checkIfPluginManagerFile(const Utils::WString &filename) const
{
bool found = false;
CFileIO catFile(m_sCurrentDir + L"/" + filename + L".cat");
if ( catFile.exists() && CFileIO::Exists(m_sCurrentDir + L"/" + filename + L".dat"))
{
CCatFile openFile;
if ( openFile.open(catFile.fullFilename(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
// if ( openFile.internaldatFilename().Compare("PlugMan_Fake.dat") ) return true;
int count = 0;
int noncount = 0;
// check for some of the files
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
if ( node->Data()->fullDir().contains(L"::") ) {
if (CFileIO(node->Data()->fullDir().token(L"::", 1)).filename().Compare(L"PlugMan_Fake.cat") ) {
Utils::WString filename = node->Data()->filePointer().token(L"::", 2);
filename = filename.findReplace(L"/", L"\\");
if ( openFile.findData(filename) )
++count;
else
++noncount;
}
}
}
if ( (count && !noncount) || (count > noncount) )
found = true;
}
}
return found;
}
bool CPackages::checkValidPluginManagerFile(const Utils::WString &filename) const
{
// both the cat file and dat file exists, lets open it
CFileIO catFile(m_sCurrentDir + L"/" + filename + L".cat");
if ( catFile.exists() && CFileIO::Exists(m_sCurrentDir + L"/" + filename + L".dat"))
{
CCatFile openFile;
if ( openFile.open(catFile.fullFilename(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
if ( openFile.findData(L"PlugMan\\TFake.pck") )
return true;
if ( openFile.findData(L"pluginmanagerfake.pck") )
return true;
}
}
return false;
}
/**
* Converts a package in old format
*
* Some entries in older packages need to be changed for the new installer
* These Include:
* - Game Version, old format is an id of position in the Data/exe file, new is 2 values, one for game, one for the version
*/
void CPackages::ConvertOldPackage(CBaseFile *package) const
{
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( gNode->Data()->iGame == -1 ) {
// all versions
if ( gNode->Data()->iVersion == 0 )
gNode->Data()->iGame = 0;
else
{
int version = 0;
gNode->Data()->iGame = m_gameExe.convertGameType(gNode->Data()->iVersion, &version);
gNode->Data()->iVersion = version;
}
}
}
if ( package->forumLink().empty() && package->webSite().contains(L"forum.egosoft")) {
package->setForumLink(package->webSite());
package->setWebSite(L"");
}
// convert the version
if ( package->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)package;
if ( spk->GetScriptType() == CSpkFile::SCRIPTTYPE_CUSTOM )
{
Utils::WString type = spk->scriptTypeString(m_iLanguage);
if ( type.Compare(L"Ship Upgrade") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_SHIPUPGRADE);
else if ( type.Compare(L"Trade Script") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_TRADE);
else if ( type.Compare(L"Fleet Management") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_FLEET);
else if ( type.Compare(L"Navigation Script") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_NAVIGATION);
else if ( type.Compare(L"Piracy") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_PIRACY);
else if ( type.Compare(L"Other") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_OTHER);
else if ( type.Compare(L"Ship Command") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_SHIPCOMMAND);
else if ( type.Compare(L"Station Command") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_STATIONCOMMAND);
else if ( type.Compare(L"al plugin") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_ALPLUGIN);
else if ( type.Compare(L"combat script") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_COMBAT);
else if ( type.Compare(L"bbs and missions") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_MISSION);
else if ( type.Compare(L"extension mod") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_EXTENSION);
else if ( type.Compare(L"rebalance mod") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_REBALANCE);
else if ( type.Compare(L"general mod") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_GENERALMOD);
else if ( type.Compare(L"total conversion") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_TOTAL);
else if ( type.Compare(L"cheat script") )
spk->setScriptType(CSpkFile::SCRIPTTYPE_CHEAT);
else if ( type == L"Library Script" )
{
spk->setScriptType(L"");
spk->SetLibrary();
}
if ( spk->GetScriptType() != CSpkFile::SCRIPTTYPE_CUSTOM )
spk->setScriptType(L"");
}
}
else if ( package->GetType() == TYPE_XSP )
{
CXspFile *xsp = (CXspFile *)package;
Utils::WString data = xsp->shipData();
for ( int i = 17; i <= 18; i++ )
{
Utils::WString model = data.token(L";", i);
Utils::WString modelExt = model.right(4);
// check file extension
if ( modelExt.Compare(L".bod") || modelExt.Compare(L".pbd") )
data = data.replaceToken(L";", i, model.left(-4));
}
xsp->setShipData(data);
}
// any extra files that are in director folder
if ( package->AnyFileType(FILETYPE_EXTRA) )
{
for ( C_File *f = package->GetFirstFile(FILETYPE_EXTRA); f; f = package->GetNextFile(f) )
{
if ( !f->dir().Compare(L"director") )
continue;
if (f->checkFileExt(L"xml") || f->checkFileExt(L"pck"))
{
f->setDir(L"");
f->setFileType(FILETYPE_MISSION);
}
}
}
}
void CPackages::_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 status
package->updateSigned(true);
// check for another mod
if ( !package->IsAnotherMod() )
return;
package->SetParent((CSpkFile *)findSpkPackage(package->otherName(), package->otherAuthor()));
}
/**
* Updates the package list once data has been read
*
* Finds any original Files
* Updates Signed status of all packages
*/
bool CPackages::UpdatePackages(int doStatus, bool individual)
{
// if theres no original files, then set the current structure as the base
if ( doStatus == 0 || doStatus == -1 )
{
_pOriginalFiles->update(m_bRedo, &m_lFiles);
}
// update each package
// parent/child, signed status
if ( doStatus == -1 || doStatus == 1 )
{
if ( individual )
{
// no package, most likly none installed
if ( !m_pPackageNode )
return false;
this->UpdatePackage(m_pPackageNode->Data());
// move to the next package
m_pPackageNode = m_pPackageNode->next();
if ( !m_pPackageNode )
return false;
}
else
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
this->UpdatePackage(node->Data());
}
}
if ( doStatus == -1 || doStatus == 2 )
this->LoadVirtualFileSystem();
// adjust save games
if ( doStatus == -1 || doStatus == 3 )
{
}
m_bRedo = false;
return true;
}
int CPackages::checkOpenPackage(const Utils::WString &file, int *error) const
{
// first check if it exists
if (!CFileIO::Exists(file))
{
*error = INSTALLERR_NOEXIST;
return -1;
}
// open the spk file
float fVersion = 0.0f;
int check = CBaseFile::CheckFile(file, &fVersion);
// wrong version
if ( fVersion > (float)FILEVERSION )
{
*error = INSTALLERR_VERSION;
return -1;
}
return check;
}
CMultiSpkFile *CPackages::openMultiPackage(const Utils::WString &file, int *error, CProgressInfo *progress)
{
int check = checkOpenPackage(file, error);
if ( *error == -1 )
return false;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return false;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
bool ret = false;
if ( package->readFile(file, true))
{
if ( package->readAllPackages(SPKREAD_NODATA) )
{
for ( CListNode<SMultiSpkFile> *node = package->GetFileList()->Front(); node; node = node->next() )
this->ConvertOldPackage(node->Data()->pFile);
ret = true;
}
}
if ( ret )
return package;
delete package;
return NULL;
}
bool CPackages::openMultiPackage(const Utils::WString &file, CLinkList<CBaseFile> *packageList, int *error, CProgressInfo *progress )
{
int check = checkOpenPackage(file, error);
if ( *error == -1 )
return false;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return false;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
bool ret = false;
if ( package->readFile(file, true))
{
if ( package->readAllPackages(SPKREAD_ALL, packageList) )
{
for ( CListNode<CBaseFile> *node = packageList->Front(); node; node = node->next() )
this->ConvertOldPackage(node->Data());
ret = true;
}
}
delete package;
return ret;
}
int CPackages::prepareMultiPackage(const Utils::WString &file, CLinkList<CBaseFile> *errorPackageList, int *error, CProgressInfo *progress)
{
int check = checkOpenPackage(file, error);
if ( *error == -1 )
return 0;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return 0;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
int count = 0;
if ( package->readFile(file, true))
{
CLinkList<CBaseFile> packageList;
if ( package->readAllPackages(SPKREAD_ALL, &packageList) )
{
for ( CListNode<CBaseFile> *node = packageList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
this->ConvertOldPackage(p);
int error = this->PrepareInstallPackage(p);
if ( error )
{
node->Data()->SetLoadError(error);
errorPackageList->push_back(p);
}
else
++count;
}
}
packageList.clear();
}
for ( SMultiSpkFile *ms = package->GetFileList()->First(); ms; ms = package->GetFileList()->Next() )
ms->pFile = NULL;
delete package;
return count;
}
CBaseFile *CPackages::openPackage(const Utils::WString &file, int *error, CProgressInfo *progress, int readtype, int flags) const
{
int check = checkOpenPackage(file, error);
if (*error == -1)
return NULL;
CBaseFile *installFile = 0;
if ( progress )
progress->DoHalf();
switch (check)
{
case SPKFILE_OLD:
*error = INSTALLERR_OLD;
return NULL;
case SPKFILE_INVALID:
// convert xsp
if ( CFileIO(file).isFileExtension(L"xsp") )
{
installFile = new CXspFile();
if ( !((CXspFile *)installFile)->convertOld(file) )
{
delete installFile;
return NULL;
}
break;
}
// try and open as archive
else 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 files
if ( !(flags & READFLAG_NOUNCOMPRESS) )
installFile->UncompressAllFiles(progress);
this->ConvertOldPackage (installFile);
return installFile;
}
void CPackages::purgeUninstallScripts(CBaseFile *package, Utils::WStringList *errors)
{
for ( CListNode<C_File> *fNode = m_lUninstallFiles.Front(); fNode; fNode = fNode->next() )
{
C_File *uf = fNode->Data();
// check against any script files
for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
{
C_File *checkFile = checkNode->Data();
if ( !checkFile ) continue;
if ( checkFile->GetFileType() != FILETYPE_UNINSTALL && checkFile->GetFileType() != FILETYPE_SCRIPT )
continue;
if ( uf->filename().Compare(checkFile->filename()) )
{
if (removeUninstallFile(uf, errors) )
fNode->DeleteData();
break;
}
}
}
m_lUninstallFiles.RemoveEmpty();
this->WriteData();
}
int CPackages::installPreparedPackages(Utils::WStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *errored, CLinkList<CBaseFile> *installedList)
{
if ( m_lInstallList.empty() ) return false;
int installed = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( this->installPackage(p, errors, progress, !p->IsEnabled()) )
{
++installed;
if ( installedList )
installedList->push_back(p);
}
else if ( errored )
{
m_lPackages.remove(p);
errored->push_back(p);
}
}
m_lInstallList.clear();
this->WriteData();
return installed;
}
void CPackages::_addToFakePatch(CBaseFile *pPackage)
{
CCatFile cat;
if ( CCatFile::Opened(cat.open((m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat"), this->getAddonDir(), CATREAD_DAT))) {
for ( CListNode<C_File> *f = pPackage->GetFileList()->Front(); f; f = f->next() ) {
if ( f->Data()->GetFileType() != FILETYPE_SHIPSCENE && f->Data()->GetFileType() != FILETYPE_COCKPITSCENE && f->Data()->GetFileType() != FILETYPE_SHIPMODEL && f->Data()->GetFileType() != FILETYPE_SHIPOTHER ) {
continue;
}
if ( CCatFile::IsAddonDir(f->Data()->getNameDirectory(pPackage)) ) {
continue;
}
// check if its already in the fake patch
if ( f->Data()->fullDir().contains(L"::") ) {
continue;
}
Utils::WString toFile;
if ( cat.appendFile(f->Data()->filePointer(), f->Data()->getNameDirectory(pPackage), true, (m_iGameFlags & EXEFLAG_NOXOR) ? false : true, &toFile) ) {
CLog::logf(CLog::Log_Install, 2, L"Adding file: %s into the fake patch", f->Data()->getNameDirectory(pPackage).c_str());
CFileIO::Remove(f->Data()->filePointer());
f->Data()->setFilename(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat::" + toFile);
}
}
}
}
bool CPackages::_checkForDisable(CBaseFile *package, bool disabled, CBaseFile *oldPackage)
{
bool prevDisabled = disabled;
// first check if we are installed a mod
if ( package->IsMod() && m_pEnabledMod && !m_bForceModInstall ) {
disabled = true;
CLog::log(CLog::Log_Install, 2, L"Package is a mod and another mod is already enabled, setting to disabled");
}
// if vanilla nad package aint signed
if ( m_bVanilla && !package->IsSigned() ) {
disabled = true;
CLog::log(CLog::Log_Install, 2, L"Package is a not signed and are we in vanilla mode, setting to disabled");
}
// check any depancies
if ( !disabled && !this->CheckEnabledDependacy(package) ) {
disabled = true;
CLog::log(CLog::Log_Install, 2, L"Dependacies missing for package, setting to disabled");
}
// search for an old version
if ( oldPackage && oldPackage == m_pEnabledMod && disabled )
disabled = prevDisabled;
return disabled;
}
Utils::WString CPackages::getCurrentDirectory() const
{
return (_pCurrentDir) ? _pCurrentDir->dir : Utils::WString::Null();
}
bool CPackages::installPackage(CBaseFile *package, Utils::WStringList *errors, CProgressInfo *progress, bool disabled)
{
CLog::logf(CLog::Log_Install, 1, L"Starting to install new package, %s by %s (Version: %s)", package->name().c_str(), package->author().c_str(), package->version().c_str());
CBaseFile *oldPackage = findPackage(package);
disabled = _checkForDisable(package, disabled, oldPackage);
// update packages must have an old package installed already (should have been checked for already)
if ( package->GetType() == TYPE_SPK )
{
if ( ((CSpkFile *)package)->IsPackageUpdate() )
{
CLog::log(CLog::Log_Install, 3, L"Package is an Update, checking for existing package installed");
if ( !oldPackage ) {
CLog::log(CLog::Log_Install, 2, L"Package is an Update but no existing package found, cancelling install");
return false;
}
// change any mods to temp ones
for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
{
if ( f->Data()->GetFileType() != FILETYPE_MOD )
continue;
f->Data()->setDir(L"temp");
if ( f->Data()->IsFakePatch() ) {
CLog::logf(CLog::Log_Install, 2, L"Moving fake package to temporary location to preper for update, %s", f->Data()->filePointer().c_str());
f->Data()->setName(L"Fake_" + f->Data()->name());
}
}
}
}
// no need to backup if we're disabling them
if ( !disabled )
{
// find any uninstall files and remove them
CLog::log(CLog::Log_Install, 3, L"Purging uninstall scripts");
this->purgeUninstallScripts(package, errors);
// backup any original files before installing
_pOriginalFiles->backup(package, errors);
}
// install all the files
CLog::log(CLog::Log_Install, 3, L"Checking for any existing files");
if ( oldPackage )
{
CLog::logf(CLog::Log_Install, 3, L"Excluding existing package (%s) from file check list", oldPackage->version().c_str());
CLinkList<CBaseFile> excludeList;
excludeList.push_back(oldPackage);
this->UpdateUsedFiles(&excludeList, true);
}
else
this->UpdateUsedFiles(0, true);
CLog::log(CLog::Log_Install, 3, L"Reading all files into memory");
package->ReadAllFilesToMemory();
CLog::log(CLog::Log_Install, 3, L"Starting to install files");
if ( !package->installFiles (m_sCurrentDir, progress, &m_lFiles, errors, !disabled, this))
{
CLog::log(CLog::Log_Install, 2, L"There was an error installing files!!");
// TODO: clear up installed files
return false;
}
_pOriginalFiles->installed(package);
// if we're installing an addon, lets use the fake patch method for object files
if ( m_iGameFlags & EXEFLAG_ADDON ) this->_addToFakePatch(package);
bool shuffle = package->anyFakePatchOrder();
// merge the update into the old package
bool dontAdd = false;
if ( package->GetType() == TYPE_SPK )
{
if ( ((CSpkFile *)package)->IsPackageUpdate() )
{
// now copy any files from a mod
for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
{
if ( !f->Data() )
continue;
if ( f->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if (!f->Data()->checkFileExt(L"cat"))
continue;
// if fake patch, find first fake patch in package
C_File *findMatching = NULL;
if ( f->Data()->baseName().left(5).Compare(L"fake_") )
{
for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if ( !node->Data()->checkFileExt(L"cat") )
continue;
if ( !node->Data()->IsFakePatch() )
continue;
findMatching = node->Data();
break;
}
}
// otherwise, just add to the mod of the same name
else
{
for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if (!node->Data()->checkFileExt(L"cat"))
continue;
if ( node->Data()->name().Compare(f->Data()->name()) )
{
findMatching = node->Data();
break;
}
}
}
if ( findMatching )
{
// copy accross all mods
CCatFile catTo, catFrom;
if ( catFrom.open(f->Data()->filePointer(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
if ( catTo.open(findMatching->filePointer(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
for (unsigned int i = 0; i < catFrom.GetNumFiles(); i++ )
{
SInCatFile *c = catFrom.GetFile(i);
catTo.appendFile(f->Data()->filePointer() + L"::" + c->sFile, c->sFile);
}
}
}
// now remove the files
C_File *m = package->findMatchingMod(f->Data());
removeFile(f->Data(), errors);
m_lFiles.remove(f->Data());
f->ChangeData(NULL);
if ( m )
{
int pos = package->GetFileList()->FindPos(m);
removeFile(m, errors);
m_lFiles.remove(m);
if ( pos != -1 )
package->GetFileList()->GetNode(pos)->ChangeData(NULL);
}
}
// no matching file, then we shall just renaming back
else
{
if ( f->Data()->baseName().left(5).Compare(L"fake_") )
{
shuffle = true;
C_File *match = package->findMatchingMod(f->Data());
Utils::WString next = Utils::WString::PadNumber(this->findNextFakePatch(), 2);
Utils::WString oldFilePointer = f->Data()->filePointer();
f->Data()->setDir(L"");
f->Data()->changeBaseName(next);
if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package)))
f->Data()->setFilename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package));
if ( match )
{
Utils::WString oldFilePointer = match->filePointer();
match->setDir(L"");
match->changeBaseName(next);
if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + L"/" + match->getNameDirectory(package)))
match->setFilename(m_sCurrentDir + L"/" + match->getNameDirectory(package));
}
}
else
{
C_File *match = package->findMatchingMod(f->Data());
f->Data()->setDir(L"");
if ( CFileIO(f->Data()->filePointer()).Rename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package)))
f->Data()->setFilename(m_sCurrentDir + L"/" + f->Data()->getNameDirectory(package));
if ( match )
{
match->setDir(L"");
if ( CFileIO(match->filePointer()).Rename(m_sCurrentDir + L"/" + match->getNameDirectory(package)))
match->setFilename(m_sCurrentDir + L"/" + match->getNameDirectory(package));
}
}
}
}
package->GetFileList()->RemoveEmpty();
((CSpkFile *)oldPackage)->MergePackage(package);
delete package;
package = oldPackage;
oldPackage = false;
dontAdd = true;
CDirIO(m_sCurrentDir).removeDir(L"temp");
CDirIO(m_sCurrentDir).removeDir(L"Mods/temp");
}
}
// if theres an icon, write it
if (package->icon())
{
CLog::log(CLog::Log_Install, 3, L"Checking to install icon display file");
C_File *icon = package->icon();
if ( !icon->GetData() || !icon->GetDataSize() ) {
package->setIcon(NULL, L"");
CLog::log(CLog::Log_Install, 2, L"Unable to extract icon, clearing");
}
else
{
CDirIO Dir(m_sCurrentDir);
bool ready = true;
if ( !Dir.exists(L"PluginManager") )
{
if ( !Dir.create(L"PluginManager") )
ready = false;
}
if ( ready && !Dir.exists(L"PluginManager/Icons") )
{
if ( !Dir.create(L"PluginManager/Icons") )
ready = false;
}
if ( ready )
{
if ( !icon->UncompressData() )
package->setIcon(NULL, L"");
else
{
CFileIO iconFile(m_sCurrentDir + L"/PluginManager/Icons/" + package->author() + L"_" + package->name() + L"." + package->iconExt());
if ( iconFile.WriteData((const char *)icon->GetData(), icon->GetDataSize()) )
{
icon->setFilename(package->author() + L"_" + package->name() + L"." + package->iconExt());
icon->setFullDir(m_sCurrentDir + L"/PluginManager/Icons");
}
else
package->setIcon(NULL, L"");
}
}
if (package->icon())
package->icon()->DeleteData();
}
}
// remove all data
CLog::log(CLog::Log_Install, 3, L"Clearing all unneeded file data");
package->ClearFileData();
// add to list
if ( !dontAdd )
{
CLog::log(CLog::Log_Install, 1, L"Adding package into main list");
if ( oldPackage )
{
// find all other packages that has the old package as parent and switch to new
for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
if ( node->Data()->GetParent() == oldPackage ) {
node->Data()->SetParent(package);
}
}
// remove olld package from list, and replace it with the new one
m_lPackages.insert(oldPackage, package);
m_lPackages.remove(oldPackage, false);
}
else
m_lPackages.push_back (package);
}
CLog::log(CLog::Log_Install, 2, L"Updating file used count");
UpdateUsedFiles();
if ( disabled ) package->SetEnabled(false);
// remove any files no longer used by old package
if ( oldPackage )
{
CLog::log(CLog::Log_Install, 3, L"Removing any unused files from previous package");
CListNode<C_File> *fnode = oldPackage->GetFileList()->Front();
while ( fnode )
{
C_File *f = fnode->Data();
// no longer used
if ( !f->getUsed() )
{
// remove from file list
m_lFiles.remove(f, false);
// if its a readme file, then check if its in the new package
bool dontRemove = false;
if ( f->GetFileType() == FILETYPE_README ) {
if ( package->findFile(f->filename(), FILETYPE_README) )
dontRemove = true;
}
// remove from hard drive
if ( !dontRemove && removeFile(f, errors) )
{
CLog::logf(CLog::Log_Install, 1, L"Removed unused file: %s", f->filePointer().c_str());
// if a fake patch, we need to shufle
if ( f->IsFakePatch() )
shuffle = true;
else if ( f->isAutoTextFile() )
shuffle = true;
}
}
fnode = fnode->next();
}
}
if ( shuffle )
{
CLog::log(CLog::Log_Install, 2, L"Shuffling Fake patches");
shuffleFakePatches(errors);
CLog::log(CLog::Log_Install, 2, L"Shuffling Text Files");
shuffleTextFiles(errors);
}
// now we need to link any child/parent packages
if ( package->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)package;
if ( spk->IsAnotherMod() ) {
spk->SetParent((CSpkFile *)findSpkPackage(spk->otherName(), spk->otherAuthor()));
CLog::logf(CLog::Log_Install, 2, L"Linking to parent package: %s by %s (Version: %s)", spk->name().c_str(), spk->author().c_str(), spk->version().c_str());
}
}
// store enabled mod
if ( package->IsMod() && !disabled ) {
m_pEnabledMod = package;
CLog::log(CLog::Log_Install, 1, L"Setting package as primary mod");
}
package->updateTextDB();
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 remove
UpdateUsedFiles(&m_lInstallList);
Utils::WStringList removeDirs;
CLinkList<C_File> uninstallFiles;
CLinkList<C_File> fileList;
bool readme = false, original = false, shuffle = false;
// find all files that need to be removed
int maxFiles = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
maxFiles += this->GetAllPackageFiles(p, &fileList, true);
// disable any dependants
if ( p->GetType() == TYPE_SPK && ((CSpkFile *)p)->IsLibrary() )
{
CLinkList<CBaseFile> depList;
if ( this->GetDependacyList(p, &depList) )
{
for ( CBaseFile *depP = depList.First(); depP; depP = depList.Next() )
{
if ( depP->IsEnabled() )
this->PrepareDisablePackage(depP);
}
if ( m_lDisableList.size() )
this->disablePreparedPackages(errors, progress, disabledPackages);
}
}
}
// interate through all the files in the package
int fileCount = 0;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// display progress if needed
if ( progress )
{
progress->UpdateProgress(fileCount++, maxFiles);
progress->UpdateFile(f);
}
// skip uninstall files
if ( f->GetFileType() == FILETYPE_UNINSTALL )
{
uninstallFiles.push_back(f);
continue;
}
// only delete files that are not used
// if its a shared file, we skip it
if ( f->getUsed() || f->IsShared() )
continue;
if ( f->GetFileType() == FILETYPE_README )
readme = true;
else if ( f->GetFileType() == FILETYPE_UNINSTALL || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND || f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_SHIPSCENE || f->GetFileType() == FILETYPE_COCKPITSCENE || f->GetFileType() == FILETYPE_SHIPOTHER || f->GetFileType() == FILETYPE_SHIPMODEL || f->GetFileType() == FILETYPE_ADVERT )
{
Utils::WString dir = f->getDirectory(NULL);
if(!removeDirs.contains(dir))
removeDirs.pushBack(dir);
dir = dir.findReplace(L"\\", L"/");
if ( dir.contains(L"/") )
{
for ( int i = dir.countToken(L"/"); i; i-- )
{
Utils::WString remDir = dir.tokens(L"/", 1, i);
if(!removeDirs.contains(remDir))
removeDirs.pushBack(remDir);
}
}
}
if (f->GetFileType() == FILETYPE_EXTRA && f->dir().left(6).lower() == L"extras")
{
if (!removeDirs.contains(L"Extras"))
removeDirs.pushBack(L"Extras");
}
if (removeFile(f, errors))
original = _pOriginalFiles->restoreFile(f, errors);
else // problem removeing (try when the program closes)
_lNonRemovedFiles.pushBack(f->filePointer());
// check for fake patchs
if ( f->IsFakePatch() )
shuffle = true;
else if ( f->isAutoTextFile() )
shuffle = true;
// remove the file from the main list as swell
m_lFiles.remove(f, false);
delete f;
}
// remove all the packages from memory
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
p->GetFileList()->clear();
m_lPackages.remove(p);
if ( p == m_pEnabledMod )
m_pEnabledMod = NULL;
if ( uninstalledPackages )
uninstalledPackages->push_back(p);
else
delete p;
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 files
if ( !uninstallFiles.empty() )
{
removeDirs.pushBack(L"PluginManager/Uninstall");
// make sure the scripts directory is created, even thou it should always be there anyways
CDirIO scriptDir(m_sCurrentDir);
if ( !scriptDir.exists(L"scripts") )
{
if ( scriptDir.create(L"Scripts") )
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY, L"Scripts", errors);
else
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, L"Scripts", errors);
}
for ( C_File *uf = uninstallFiles.First(); uf; uf = uninstallFiles.Next() )
{
C_File *newFile = new C_File();
newFile->setFileType(FILETYPE_SCRIPT);
newFile->setFilename(uf->filename());
newFile->SetCreationTime(uf->GetCreationTime());
// other installed packages use this file as well, copy it
Utils::WString newFilename = m_sCurrentDir + L"/" + newFile->getNameDirectory(NULL);
CFileIO file(uf->filePointer());
if ( uf->getUsed() )
{
if ( file.copy(newFilename) )
this->addLogEntry(SPKINSTALL_UNINSTALL_COPY, newFile->getNameDirectory(NULL), errors);
else
{
this->addLogEntry(SPKINSTALL_UNINSTALL_COPY_FAIL, newFile->getNameDirectory(NULL), errors);
delete newFile;
newFile = NULL;
}
}
// otherwise just move it
else
{
if ( file.Rename(newFilename) )
this->addLogEntry(SPKINSTALL_UNINSTALL_MOVE, newFile->getNameDirectory(NULL), errors);
else
{
this->addLogEntry(SPKINSTALL_UNINSTALL_MOVE_FAIL, newFile->getNameDirectory(NULL), errors);
delete newFile;
newFile = NULL;
}
m_lFiles.remove(uf, false);
delete uf;
}
// add to the list
if ( newFile )
{
// first check if theres a matching one
bool found = false;
for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
{
C_File *checkFile = node->Data();
if ( checkFile->filename().Compare(newFile->filename()) )
{
found = true;
break;
}
}
// not found, so add it
if ( !found )
m_lUninstallFiles.push_back(newFile);
else
delete newFile;
}
}
}
uninstallFiles.clear();
// remove all directies that we're not using
if ( readme )
{
removeDirs.pushBack(L"PluginManager/Readme");
removeDirs.pushBack(L"Readme");
}
if ( original ) {
removeDirs.pushBack(L"PluginManager/Original/Replacements");
removeDirs.pushBack(L"PluginManager/Original");
}
removeDirs.pushBack(L"PluginManager/Disabled");
removeUnusedDirectories(removeDirs, errors);
// finally lets shuffle any fake patchs to fill in gaps
if ( shuffle )
{
shuffleFakePatches(errors);
shuffleTextFiles(errors);
}
this->WriteData();
return true;
}
/**
* Prepares a package to be uninstalled
*
* Adds the package, and all its children onto the uninstall list to be used by UninstallPreparePackages
*/
void CPackages::PrepareUninstallPackage(CBaseFile *package)
{
// add package to list
if ( !m_lInstallList.FindData(package) )
m_lInstallList.push_back(package);
// add all children
CLinkList<CBaseFile> children;
if ( this->GetChildPackages(package, &children, true) )
{
for ( CBaseFile *p = children.First(); p; p = children.Next() )
{
if ( !m_lInstallList.FindData(p) )
m_lInstallList.push_back(p);
}
}
}
bool CPackages::PrepareEnablePackage(CBaseFile *package)
{
ClearError();
if ( package->GetParent() && !package->GetParent()->IsEnabled() )
{
m_iError = PKERR_NOPARENT;
return false;
}
if ( m_bVanilla && !package->IsSigned() )
{
m_iError = PKERR_MODIFIED;
return false;
}
// check if it needs depancies
if ( package->AnyDependacies() )
{
if ( this->getMissingDependacies(package, NULL, true) )
{
m_iError = PKERR_MISSINGDEP;
return false;
}
}
if ( !m_lEnableList.FindData(package) )
{
if ( !package->IsEnabled() )
m_lEnableList.push_back(package);
// do all the children as well
if ( m_bAutoEnableChild )
{
CLinkList<CBaseFile> childList;
this->GetChildPackages(package, &childList, true);
// add all disabled packages to list
for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
{
if ( !p->IsEnabled() )
m_lEnableList.push_back(p);
}
}
}
m_bRemoveDir = true;
return true;
}
bool CPackages::PrepareDisableForVanilla()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsSigned() && p->IsEnabled() )
{
this->PrepareDisablePackage(p);
m_bDisableVanilla = true;
}
}
return true;
}
bool CPackages::PrepareEnableLibrarys()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->GetType() != TYPE_SPK )
continue;
if ( !p->IsEnabled() && ((CSpkFile *)p)->IsLibrary() )
this->PrepareEnablePackage(p);
}
return true;
}
bool CPackages::PrepareEnableFromVanilla()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() && p->IsModifiedEnabled() ) {
this->PrepareEnablePackage(p);
}
}
return true;
}
int CPackages::GetDependacyList(CBaseFile *package, CLinkList<CBaseFile> *list)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->isPackageNeeded(package->name(), package->author()))
list->push_back(p);
}
return list->size();
}
bool CPackages::PrepareDisablePackage(CBaseFile *package)
{
if ( !m_lDisableList.FindData(package) )
{
if ( package->IsEnabled() )
m_lDisableList.push_back(package);
CLinkList<CBaseFile> childList;
this->GetChildPackages(package, &childList, true);
// add all disabled packages to list
for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
{
if ( p->IsEnabled() && !m_lDisableList.FindData(p) )
m_lDisableList.push_back(p);
}
// if its a library, check for any dependacies
if ( package->GetType() == TYPE_SPK && ((CSpkFile *)package)->IsLibrary() )
{
CLinkList<CBaseFile> depList;
if ( this->GetDependacyList(package, &depList) )
{
for ( CBaseFile *p = depList.First(); p; p = depList.Next() )
{
if ( !m_lDisableList.FindData(p) )
this->PrepareDisablePackage(p);
}
}
}
}
return true;
}
/**
* Prepares a package to be installed
*/
int CPackages::PrepareInstallPackage(CBaseFile *package, bool disabled, bool force, int check)
{
// add package to list
if ( !m_lInstallList.FindData(package) )
{
int error = this->CheckInstallPackage(package, check);
if ( error == INSTALLCHECK_OK || force )
{
if ( disabled )
package->SetEnabled(false);
bool added = false;
if ( package->GetType() == TYPE_SPK )
{
// find other mods in the currently added list
CSpkFile *spk = (CSpkFile *)package;
if ( spk->IsAnotherMod() )
{
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( spk->otherName().Compare(p->name()) && spk->otherAuthor().Compare(p->author()) )
{
m_lInstallList.insert(m_lInstallList.FindPos(p) + 2, package);
added = true;
break;
}
}
}
}
if ( !added )
{
// check if we are a parent
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
if ( node->Data()->GetType() != TYPE_SPK )
continue;
CSpkFile *spk = (CSpkFile *)node->Data();
if ( !spk->isAnotherMod() )
continue;
if ( spk->otherName().Compare(package->name()) && spk->otherAuthor().Compare(package->author()) )
{
added = true;
m_lInstallList.insert(node->Data(), package);
break;
}
}
if ( !added )
m_lInstallList.push_back(package);
}
return INSTALLCHECK_OK;
}
package->SetLoadError(error);
return error;
}
return INSTALLCHECK_ALREADYQUEUED;
}
void CPackages::RemovePreparedInstall(CBaseFile *package)
{
if ( !package )
{
m_lInstallList.MemoryClear();
}
else
{
if ( m_lInstallList.FindData(package) )
{
m_lInstallList.remove(package, true);
}
}
}
wchar_t CPackages::ConvertWareTypeBack(int w)
{
switch ( w )
{
case WARES_BIO:
return L'b';
case WARES_ENERGY:
return L'e';
case WARES_FOOD:
return L'f';
case WARES_MINERAL:
return L'm';
case WARES_TECH:
return L't';
case WARES_NATURAL:
return L'n';
}
return L't';
}
int CPackages::ConvertWareType(wchar_t w)
{
switch (std::tolower(w))
{
case L'b':
return WARES_BIO;
case L'e':
return WARES_ENERGY;
case L'f':
return WARES_FOOD;
case L'm':
return WARES_MINERAL;
case L't':
return WARES_TECH;
case L'n':
return WARES_NATURAL;
}
return WARES_TECH;
}
void CPackages::SetupShips()
{
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->iType != WARETYPE_NONE )
wNode->Data()->iType = WARETYPE_DELETED;
}
// find any new ships to add to the list
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
if ( pNode->Data()->GetType() != TYPE_XSP )
continue;
CXspFile *p = (CXspFile *)pNode->Data();
bool found = false;
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->sShipID.Compare(p->shipID()) )
{
if ( !p->IsEnabled() )
wNode->Data()->iType = WARETYPE_DISABLED;
else
wNode->Data()->iType = WARETYPE_ADDED;
found = true;
wNode->Data()->pPackage = p;
wNode->Data()->sShipClass = p->shipClass();
break;
}
}
if ( found || !p->IsEnabled() )
continue;
// first find any free
SGameShip *gw = NULL;
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
gw = wNode->Data();
// find an old entry for the ware and add it to the same place
if ( wNode->Data()->sShipID.Compare(p->shipID()) )
{
gw = wNode->Data();
break;
}
}
// none found, create one
if ( !gw )
{
gw = new SGameShip;
gw->sShipID = p->shipID();
gw->sShipClass = p->shipClass();
gw->pPackage = p;
m_lGameShips.push_back(gw);
}
gw->iType = WARETYPE_ADDED;
}
}
void CPackages::SetupWares()
{
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *wNode = m_lGameWares[i].Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->iType != WARETYPE_NONE )
wNode->Data()->iType = WARETYPE_DELETED;
}
}
// find any new wares to add to the list
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
if ( pNode->Data()->GetType() != TYPE_SPK )
continue;
CSpkFile *p = (CSpkFile *)pNode->Data();
for ( CListNode<SWares> *node = p->GetWaresList()->Front(); node; node = node->next() )
{
SWares *w = node->Data();
int wareType = CPackages::ConvertWareType(w->cType);
// check if its on the list
bool found = false;
for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->sWareName == w->sID )
{
if ( !p->IsEnabled() )
wNode->Data()->iType = WARETYPE_DISABLED;
else
wNode->Data()->iType = WARETYPE_ADDED;
wNode->Data()->pWare = w;
found = true;
break;
}
}
if ( found || !p->IsEnabled() )
continue;
// first find any free
SGameWare *gw = NULL;
for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
{
if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
gw = wNode->Data();
// find an old entry for the ware and add it to the same place
if ( wNode->Data()->sWareName == w->sID )
{
gw = wNode->Data();
break;
}
}
// none found, create one
if ( !gw )
{
gw = new SGameWare;
gw->sWareName = w->sID;
gw->iPos = m_lGameWares[wareType].size();
gw->pWare = w;
gw->cType = w->cType;
m_lGameWares[wareType].push_back(gw);
}
gw->iType = WARETYPE_ADDED;
}
}
}
/**
* Closing the current directory
*
* When existing, program needs to close the directory
*/
bool CPackages::closeDir(Utils::WStringList *errors, CProgressInfo *progress, bool removedir)
{
if ( m_sCurrentDir.empty() )
return true;
if ( !m_bLoaded )
return true;
CLog::log(CLog::Log_Directory, 1, L"closing directory: " + m_sCurrentDir);
if ( m_bRenameText ) {
CLog::log(CLog::Log_Directory, 2, L"Creating other language files for game");
CreateLanguageTextFiles(errors);
}
CLog::log(CLog::Log_Directory, 2, L"Backing up save game files");
if ( CFileIO::Exists(m_sCurrentDir + L"/mods/PluginManager.dat") ) {
CLog::log(CLog::Log_IO, 3, L"Removing old PluginManager.dat file");
CFileIO::Remove(m_sCurrentDir + L"/mods/PluginManager.dat");
}
if ( CFileIO::Exists(m_sCurrentDir + L"/mods/PluginManager.cat") ) {
CLog::log(CLog::Log_IO, 3, L"Removing old PluginManager.cat file");
CFileIO::Remove(m_sCurrentDir + L"/mods/PluginManager.cat");
}
if ( !m_bVanilla )
{
// base mode for Reunion
if ( m_iGame == GAME_X3 && m_pEnabledMod )
{
C_File *fDat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
while (fDat && !fDat->IsFakePatch() && !fDat->checkFileExt(L"dat"))
fDat = m_pEnabledMod->GetNextFile(fDat);
if ( fDat )
{
C_File *fCat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
while ( fCat && !fCat->IsFakePatch() && !fCat->checkFileExt(L"cat") && !fCat->baseName().Compare(fDat->baseName()) )
fCat = m_pEnabledMod->GetNextFile(fCat);
if ( fCat )
{
CFileIO(fDat->filePointer()).copy(m_sCurrentDir + L"/mods/PluginManager.dat");
CFileIO(fCat->filePointer()).copy(m_sCurrentDir + L"/mods/PluginManager.cat");
}
}
}
else if ( m_iGame == GAME_X3 && !m_sSetMod.empty() && CFileIO::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat") && CFileIO::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".dat"))
{
CLog::log(CLog::Log_Directory, 2, L"Copying mod file: " + m_sSetMod + L", to PluginManager.cat");
CFileIO(m_sCurrentDir + L"/mods/" + m_sSetMod + L".dat").copy(m_sCurrentDir + L"/mods/PluginManager.dat");
CFileIO(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat").copy(m_sCurrentDir + L"/mods/PluginManager.cat");
}
if ( !CDirIO(m_sCurrentDir).exists(L"mods") )
CDirIO(m_sCurrentDir).create(L"mods");
SetupWares();
SetupShips();
createEMPFile();
CreateWareFiles();
CreateDummies();
CreateComponants();
CreateTShips();
CreateCutData();
CreateBodies();
CreateAnimations();
CreateCustomStarts();
CreateGlobals();
CreatePluginManagerText();
RestoreFakePatch();
}
RemoveFailedFiles();
WriteData();
if ( removedir && m_bRemoveDir )
{
m_bRemoveDir = false;
Utils::WStringList removeDirs;
removeDirs.pushBack(L".");
removeUnusedDirectories(removeDirs, errors);
}
this->setCurrentDir(L"");
m_bLoaded = false;
return true;
}
Utils::WString CPackages::getModKey() const
{
return m_gameExe.getModKey(m_iGame - 1);
}
Utils::WString CPackages::selectedModName() const
{
if ( !m_pEnabledMod )
{
if ( m_sSetMod.empty() && m_iGame == GAME_X3 )
return L"PluginManager";
return m_sSetMod;
}
if ( !m_pEnabledMod->IsEnabled() )
return m_sSetMod;
C_File *f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
if ( !f )
return m_sSetMod;
Utils::WString name = f->filename();
name = name.left(-4);
return name;
}
bool CPackages::RestoreFakePatch()
{
CLog::log(CLog::Log_Directory, 1, L"Restoring PluginManager fake patch into game");
m_iFakePatch = -1;
CFileIO catFile(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.cat");
CFileIO datFile(m_sCurrentDir + L"/PluginManager/PlugMan_Fake.dat");
// if only 1 exists, remove it
if ( catFile.exists() && !datFile.exists() ) {
CLog::log(CLog::Log_Directory, 1, L"WARNING: cat/dat file mismatch, dat file seems to be missing, removing cat file");
if ( !catFile.remove() ) return false;
}
else if ( !catFile.exists() && datFile.exists() ) {
CLog::log(CLog::Log_Directory, 1, L"WARNING: cat/dat file mismatch, cat file seems to be missing, removing dat file");
if ( !datFile.remove() ) return false;
}
// if both exists, lets rename them
if ( catFile.exists() && datFile.exists() )
{
CLog::log(CLog::Log_Directory, 3, L"Creating pluginmanagerfake.txt file to add to Fake Patch");
// we need to add the plugin manager file in
Utils::WString file = m_sTempDir;
if ( !file.empty() )
file += L"/";
file += L"pluginmanagerfake.txt";
file = file.findReplace(L"\\", L"/");
CFileIO fakeFile(file);
std::vector<Utils::WString> lines;
lines.push_back(L"//pluginmanager fake patch");
CLog::log(CLog::Log_Directory, 3, L"Writing pluginmanagerfake.txt file to add to Fake Patch");
if ( !fakeFile.writeFile(lines) ) {
CLog::log(CLog::Log_Directory, 3, L"Writing pluginmanagerfake.txt failed!!");
}
else {
CLog::log(CLog::Log_Directory, 2, "Adding TFake.pck file into FakePatch");
CCatFile fakePatch;
if (fakePatch.open(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 them
if ( !datFile.exists() || !catFile.exists() ) {
CLog::log(CLog::Log_Directory, 2, Utils::WString("ERROR: ") + (!catFile.exists() ? L"cat" : L"dat") + L" file appears to be missing");
}
else {
catFile.GetDirIO().create(L"Backup");
catFile.copy(catFile.dir() + L"/Backup/" + catFile.filename());
datFile.copy(datFile.dir() + L"/Backup/" + datFile.filename());
}
// find next available fake patch
m_iFakePatch = this->findNextFakePatch();
CLog::log(CLog::Log_Directory, 2, L"Finding next available fake patch number: " + (long)m_iFakePatch);
Utils::WString filename = Utils::WString::PadNumber(m_iFakePatch, 2);
CLog::log(CLog::Log_Directory, 2, L"Renaming cat file to: " + filename + L".cat");
if ( catFile.Rename(m_sCurrentDir + L"/" + filename + L".cat") )
{
CLog::log(CLog::Log_Directory, 2, L"Renaming dat file to: " + filename + L".dat");
if ( datFile.Rename(m_sCurrentDir + L"/" + filename + L".dat") ){
CLog::log(CLog::Log_Directory, 3, L"Deleting pluginmanagerfake.txt temporary file");
fakeFile.remove();
return true;
}
else {
CLog::log(CLog::Log_Directory, 2, L"ERROR: failed to rename dat file");
}
// TODO: restore cat file
}
else {
CLog::log(CLog::Log_Directory, 2, L"ERROR: failed to rename cat file");
}
CLog::log(CLog::Log_Directory, 3, L"Deleting pluginmanagerfake.txt temporary file");
fakeFile.remove();
return false;
}
return true;
}
/**
* Save package detail to date file
*
* Writes the current package list data into pluginmanager.dat file
*/
void CPackages::WriteData()
{
if ( m_sCurrentDir.empty() )
return;
CLog::log(CLog::Log_Directory, 1, L"Writing data file for current directory: " + m_sCurrentDir);
Utils::WStringList lines;
Utils::WString version = L"SpkInstaller: " + Utils::WString::FromFloat(GetLibraryVersion(), 2);
lines.pushBack(version);
lines.pushBack(Utils::WString(L"UpdateTime: ") + (long)m_iLastUpdated);
if ( m_iFakePatch != -1 )
lines.pushBack(Utils::WString(L"FakePatch: ") + (long)m_iFakePatch);
if ( m_iSaveGame != -1 )
lines.pushBack(Utils::WString(L"SaveGame: ") + (long)m_iSaveGame);
lines.pushBack(Utils::WString(L"SaveGameManager: ") + (long)m_iSaveGameManager);
if ( !m_bVanilla )
lines.pushBack(L"Modified");
if ( m_bUsedWare )
lines.pushBack(L"UsedWare");
if ( m_bSurpressProtectedWarning )
lines.pushBack(L"SurpressProtectedWarning");
if ( !m_sSetMod.empty() )
lines.pushBack(L"SetMod: " + m_sSetMod);
if (!_sSaveDir.empty())
lines.pushBack(L"SaveDir: " + _sSaveDir);
lines.pushBack(L"ShipBuffer: " + 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 Files
if ( !_lCreatedFiles.empty() )
{
for(auto itr = _lCreatedFiles.begin(); itr != _lCreatedFiles.end(); itr++)
lines.pushBack(L"CreatedFile: " + (*itr)->str.findRemove(m_sCurrentDir));
}
// write uninstall files
if ( !m_lUninstallFiles.empty() )
{
for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
{
C_File *uf = node->Data();
Utils::WString uString = L"Uninstall: ";
uString += Utils::WString::Number((long)uf->GetCreationTime()) + L" ";
uString += uf->filename();
lines.pushBack(uString);
}
}
// write the original file data
_pOriginalFiles->writeData(lines);
// write the global changes
if ( !_lGlobals.empty() )
{
for(auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
lines.pushBack(L"GlobalSetting: " + (*itr)->str + L":" + (*itr)->data.findRemove(L";"));
}
// write the fake patch ordering
if ( !_lFakePatchOrder.empty() )
{
for(auto itr = _lFakePatchOrder.begin(); itr != _lFakePatchOrder.end(); itr++)
lines.pushBack(L"FakePatchOrder: " + (*itr)->str + L":" + (*itr)->data);
}
for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
SWarePriceOverride *ware = node->Data();
switch(ware->type) {
case Ware_EMP:
lines.pushBack(Utils::WString(L"EMPPriceOverride:") + (long)ware->pos + L" " + (long)ware->relval);
if ( ware->bNotority )
lines.pushBack(Utils::WString(L"EMPNotoOverride:") + (long)ware->pos + L" " + (long)ware->notority);
break;
case Ware_Custom:
lines.pushBack(Utils::WString(L"CustomWarePriceOverride:") + ware->id + L";" + (long)ware->relval);
if ( ware->bNotority )
lines.pushBack(Utils::WString(L"CustomWareNotoOverride:") + ware->id + L";" + (long)ware->notority);
break;
case Ware_BuiltIn:
lines.pushBack(Utils::WString(L"BuiltInWarePriceOverride:") + (long)ware->pos + L" " + (long)ware->relval);
if ( ware->bNotority )
lines.pushBack(Utils::WString(L"BuiltInWareNotoOverride:") + (long)ware->pos + L" " + (long)ware->notority);
break;
}
}
// write the global file list
lines.pushBack(L"GlobalFiles:");
int num = 0;
for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
f->SetPos(num++);
Utils::WString line = Utils::WString::Number(f->GetFileType()) + L":" + Utils::WString::Number((long)f->GetCreationTime()) + L":" + f->dir() + L":";
if ( f->IsShared() && !f->IsFakePatch() )
line += L"1:";
else
line += L"0:";
Utils::WString filename = f->filePointer().findRemove(m_sCurrentDir);
if ( f->IsDisabled() )
line += L"D#";
line += L"G#";
line += (long)f->GetGame();
line += L"#";
line += filename;
if ( !f->originalName().empty() )
{
line += L"O#";
line += f->originalName();
}
lines.pushBack(line);
}
// write the package list
for ( CListNode<CBaseFile> *pn = m_lPackages.Front(); pn; pn = pn->next() )
{
CBaseFile *package = pn->Data();
if ( package->GetType() == TYPE_SPK )
lines.pushBack(L"<script>");
else if ( package->GetType() == TYPE_XSP )
lines.pushBack(L"<ship>");
else if ( package->GetType() == TYPE_ARCHIVE )
lines.pushBack(L"<archive>");
else if ( package->GetType() == TYPE_BASE )
lines.pushBack(L"<base>");
else
continue;
if ( !package->filename().empty() )
lines.pushBack(L"Installspk: " + package->filename());
Utils::WString valuesline = package->createValuesLine();
if ( valuesline.back() == '\n')
valuesline.truncate((int)valuesline.length() - 1);
lines.pushBack(valuesline);
if ( !package->IsEnabled() )
lines.pushBack(L"Disabled");
if ( !package->IsModifiedEnabled() )
lines.pushBack(L"ModifiedDisabled");
if (package->icon())
lines.pushBack(L"Icon: " + package->iconExt() + L" " + package->icon()->filePointer() );
Utils::WString fileline(L"Files:");
for ( CListNode<C_File> *fn = package->GetFileList()->Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
fileline += L" ";
fileline += Utils::WString::Number(f->GetPos());
}
lines.pushBack(fileline);
}
lines.pushBack(L"</scripts>");
CFileIO datFile(m_sCurrentDir + L"/PluginManager/PluginManager.new");
CDirIO Dir(m_sCurrentDir);
if ( !Dir.exists(L"PluginManager") ) {
CLog::log(CLog::Log_IO, 2, L"Creating PluginManager directory");
Dir.create(L"PluginManager");
}
CLog::log(CLog::Log_IO, 2, L"Writing data file: " + m_sCurrentDir + L"/PluginManager/PluginManager.new");
if ( !datFile.writeFileUTF(lines) )
CLog::log(CLog::Log_IO, 1, L"ERROR: Failed to write data file");
else {
CLog::log(CLog::Log_IO, 2, L"Removing old data file: " + m_sCurrentDir + L"/PluginManager/PluginManager.dat");
if ( !CFileIO::Exists(m_sCurrentDir + L"/PluginManager/PluginManager.dat") || CFileIO::Remove(m_sCurrentDir + L"/PluginManager/PluginManager.dat") ) {
CLog::log(CLog::Log_IO, 2, L"Renaming data file: PluginManager.new => PluginManager.dat");
datFile.Rename(m_sCurrentDir + L"/PluginManager/PluginManager.dat");
}
}
}
/**
* Get All Files
*
* Gets a list of all files, includes any child package files
*/
int CPackages::GetAllPackageFiles(CBaseFile *package, CLinkList<C_File> *fileList, bool includeChild)
{
for ( CListNode<C_File> *node = package->GetFileList()->Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( !fileList->FindData(f) )
fileList->push_back(f);
}
if ( includeChild )
{
CLinkList<CBaseFile> childList;
if ( this->GetChildPackages(package, &childList) )
{
for ( CBaseFile *child = childList.First(); child; child = childList.Next() )
this->GetAllPackageFiles(child, fileList, includeChild);
}
}
// disablign for vanilla, make sure we add all files
if ( m_bDisableVanilla )
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( !fileList->FindData(f) )
fileList->push_back(f);
}
}
return fileList->size();
}
int CPackages::GetAllPackageFiles(CLinkList<CBaseFile> *list, CLinkList<C_File> *fileList, bool includeChild)
{
for ( CListNode<CBaseFile> *node = list->Front(); node; node = node->next() )
this->GetAllPackageFiles(node->Data(), fileList, includeChild);
return fileList->size();
}
/**
* Add Log
*
* Adds a log entry to displayed at end
*/
void CPackages::addLogEntry(int type, const Utils::WString &args, Utils::WStringList *errors)
{
if (!errors)
return;
errors->pushBack(args, ERRORLOG(type));
}
/**
* Enable a package
*
* Enables all files in the package, unless they are already enabled, ie, used by other packages
* Backs up any original files before attempting to enable the file
*
* Fake patches are renamed to the next available slot
*
* Clears up the "PluginManager/Disabled" directory
*
* param: package - The package class to be removed
* param: errors - The string list for all the status for debugging, ie has an entry for whats happened to every file
* param: progress - The progress class, updates a progress screen of the derived class
*
* return: boolen - Returns true if the package enabling was successful
*/
bool CPackages::enablePackage(CBaseFile *package, Utils::WStringList *errors, CProgressInfo *progress )
{
ClearError();
// if already enabled, just skip
if ( package->IsEnabled() )
return true;
// check if parent is enabled
if ( package->GetParent() && !package->GetParent()->IsEnabled() )
{
m_iError = PKERR_NOPARENT;
return false;
}
// check for modified
if ( m_bVanilla && !package->IsSigned() )
{
m_iError = PKERR_MODIFIED;
return false;
}
if ( this->PrepareEnablePackage(package) )
return this->enablePreparedPackages(errors, progress);
return false;
}
bool CPackages::enablePreparedPackages(Utils::WStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *enabledPackages )
{
ClearError();
// get all files, including children
CLinkList<C_File> fileList;
int maxFiles = this->GetAllPackageFiles(&m_lEnableList, &fileList, false);
int fileCount = 0;
// we use this list to match cat and dat files
Utils::WStringList fakePatches;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if (!f->isForGame(m_iGame))
continue;
CBaseFile *package = NULL;
for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->IsFileAdded(f) )
{
package = p;
break;
}
}
if ( progress )
{
progress->UpdateProgress(fileCount++, maxFiles);
progress->UpdateFile(f);
}
// only move fiels that are disabled
if ( !f->IsDisabled() )
continue;
// make the directory if it dont exist
CDirIO Dir(m_sCurrentDir);
// fake patches are in the root, no need for directory
if ( !f->IsFakePatch() )
{
if ( !Dir.exists(f->getDirectory(package)) )
{
if ( !Dir.create(f->getDirectory(package)) )
{
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, f->getDirectory(package), errors);
continue;
}
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY, f->getDirectory(package), errors);
}
}
// check if theres an original file to backup
_pOriginalFiles->doBackup(f, errors);
Utils::WString newFilename = f->getNameDirectory(package);
// fake patches need to be renamed
if ( f->IsFakePatch() )
{
// first check if the matching file has been done already
if (fakePatches.contains(f->baseName()))
newFilename = fakePatches.findString(f->baseName()) + L"." + f->fileExt();
// we need to find the next available number instead
else
{
Utils::WString newPos = Utils::WString::PadNumber(findNextFakePatch(), 2);
newFilename = newPos + L"." + f->fileExt();
// and wee need to push this onto the string list so we can adjust the matchign pair
fakePatches.pushBack(f->baseName(), newPos);
}
}
// lets actually move the file back now
// !!error checking!!
CFileIO currentFile(f->filePointer());
CFileIO newFile(m_sCurrentDir + L"/" + newFilename);
if ( !currentFile.exists() )
{
// missing file ??
if ( !newFile.exists() )
{
this->addLogEntry(SPKINSTALL_MISSINGFILE, newFilename, errors);
continue;
}
}
// remove existing file
// file exists, so lets try to move it
else
{
if ( newFile.exists() )
newFile.remove();
if ( !currentFile.Rename(newFile.fullFilename()) )
{
this->addLogEntry(SPKINSTALL_ENABLEFILE_FAIL, newFilename, errors);
continue;
}
}
this->addLogEntry(SPKINSTALL_ENABLEFILE, newFilename, errors);
// adjust the internal name to match the new filename
f->setFilename(m_sCurrentDir + L"/" + newFilename);
// no longer disabled, we need to remove the flag
f->SetDisabled(false);
}
// recursive, auto enable all children
CBaseFile *oldMod = m_pEnabledMod;
CBaseFile *pMod = NULL;
for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
p->SetEnabled(true);
if ( p->IsMod() && !pMod )
pMod = p;
if ( enabledPackages )
enabledPackages->push_back(p);
_pOriginalFiles->installed(p);
}
if ( pMod )
m_pEnabledMod = pMod;
// disabled the mod
if ( oldMod && oldMod != m_pEnabledMod && !m_bForceModInstall )
this->disablePackage(oldMod, errors, progress);
// lets remove all the directories we might have left empty
Utils::WStringList removeDirs;
removeDirs.pushBack(L"PluginManager/Disabled");
removeUnusedDirectories(removeDirs, errors);
m_lEnableList.clear();
this->WriteData();
return true;
}
bool CPackages::disablePreparedPackages(Utils::WStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *disabledPackages )
{
if ( progress )
progress->UpdateStatus(PROGRESS_DISABLEFILE);
UpdateUsedFiles(&m_lDisableList, false);
// checks if there are any original files to restore and if any fake patches were disabled to reshuffle
bool original = false, shuffle = false;
// holds our list of directories that we might need to remove, only empty ones from this list will actually be removed
Utils::WStringList removeDirs;
// get all files, including children
CLinkList<C_File> fileList;
int maxFiles = this->GetAllPackageFiles(&m_lDisableList, &fileList, true);
// interate through all the files in the package
int fileCount = 0;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
CBaseFile *checkPackage = NULL;
for ( CListNode<CBaseFile> *node = m_lDisableList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->IsFileAdded(f) )
{
checkPackage = p;
break;
}
}
// update the progress count for the current file
if ( progress )
{
progress->UpdateProgress(++fileCount, maxFiles);
progress->UpdateFile(f);
}
// only delete files that are not used by any other enabled packages, counter from UpdateUsedFiles()
if ( f->getUsed() || (f->IsShared() && !m_bDisableVanilla) )
continue;
// file is already disabled, no need to disable again
if ( f->IsDisabled() )
continue;
// readmes, uninstall and extra files dont need to be disabled
// Extra files not in the "Extras" directory could be anywhere, so these should be disabled incase they are game changing files, ie in "types"
if ( f->GetFileType() == FILETYPE_README || f->GetFileType() == FILETYPE_UNINSTALL || (f->GetFileType() == FILETYPE_EXTRA && f->dir().left(5).lower() == L"Extra") )
continue;
// check if there is a matching uninstall file, ie there the script file is also an uninstall script file for a previously uninstalled package that has yet to be removed
if ( f->GetFileType() == FILETYPE_SCRIPT )
{
bool found = false;
for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
{
C_File *uFile = uNode->Data();
if ( uFile->filename().Compare(f->filename()) )
{
found = true;
break;
}
}
if ( found )
continue;
}
if ( f->GetFileType() == FILETYPE_MOD && !f->IsFakePatch() && f->checkFileExt(L"cat") )
{
if ( f->baseName().Compare(m_sSetMod) )
m_sSetMod = Utils::WString::Null();
}
// file is not being used by any enabled package
// set disabled and move to disabled directory
CDirIO Dir(m_sCurrentDir);
Utils::WString newFilename = L"PluginManager/Disabled/";
// fake patches have thier own special directory
if ( f->IsFakePatch() )
newFilename += L"FakePatches";
// otherwise we put them in thier usual directory structure inside the disabled dir
else
newFilename += f->getDirectory(NULL);
// make sure the directory exists so we can move the file
if ( !Dir.exists(newFilename) )
{
// we couldn't create the directory for some reason, this is not good
if ( !Dir.create(newFilename) )
{
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newFilename, errors);
continue;
}
this->addLogEntry(SPKINSTALL_CREATEDIRECTORY, newFilename, errors);
}
// fake patches need a special directory and filename so they dont overright each other as thier filenames are always changes
// if a package with a fake patch is installed while another one is disabled, it will fill the gap left by the disabled one
// they will then end up with the same filename, if it gets disabled they would overright each other, so we change the filename to prevent that from happening
if ( f->IsFakePatch() )
{
// find package the fake patch belongs to
if ( checkPackage )
{
newFilename = m_sCurrentDir + L"/PluginManager/Disabled/FakePatches/FakePatch_" + checkPackage->getNameValidFile() + L"_" + checkPackage->author() + L"_" + f->name();
shuffle = true;
}
}
else if ( f->isAutoTextFile() )
{
if ( checkPackage )
{
newFilename = m_sCurrentDir + L"/PluginManager/Disabled/TextFiles/Text_" + checkPackage->getNameValidFile() + L"_" + checkPackage->author() + L"_" + f->name();
shuffle = true;
}
}
// otherwise we can just use the standard filename
else
newFilename = m_sCurrentDir + L"/PluginManager/Disabled/" + f->getNameDirectory(checkPackage);
// now to move the file by renameing it to its new location
// !!error checking!!
// check the file, if it doesn't exist, and exists as disabled, we should just adjust the setting instead of an error
CFileIO currentFile(f->filePointer());
if ( !currentFile.exists() )
{
if ( !CFileIO(newFilename).exists() )
{
this->addLogEntry(SPKINSTALL_MISSINGFILE, f->getNameDirectory(checkPackage), errors);
continue;
}
}
// otherwise the file must exists, so lets move it
else if ( !currentFile.Rename(newFilename) )
{
this->addLogEntry(SPKINSTALL_DISABLEFILE_FAIL, f->getNameDirectory(checkPackage), errors);
continue;
}
// must have been fine
this->addLogEntry(SPKINSTALL_DISABLEFILE, f->getNameDirectory(checkPackage), errors);
original = _pOriginalFiles->restoreFile(f, errors);
// extra file thats not in the extras directory
if (f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND)
{
if(!removeDirs.contains(f->getDirectory(checkPackage)))
removeDirs.pushBack(f->getDirectory(checkPackage));
}
// change the filename
f->setFilename(newFilename);
// finally mark the file as disabled so we know not to try to move it again
f->SetDisabled(true);
}
// a fake patch has been disabled, we need to reshuffle the rest to fill in any gaps
if ( shuffle )
{
if ( progress )
progress->UpdateStatus(PROGRESS_SHUFFLEFAKE);
shuffleFakePatches(errors);
shuffleTextFiles(errors);
}
// original files were restored, check to remove the original file directory if its now empty
if ( original )
removeDirs.pushBack(L"PluginManager/Original");
// remove any empty directories that we might have left
if ( !removeDirs.empty() )
removeUnusedDirectories(removeDirs, errors);
// finally mark the whole package as disabled
// recursive, we need to disable all children
for ( CBaseFile *child = m_lDisableList.First(); child; child = m_lDisableList.Next() )
{
if ( m_pEnabledMod == child )
m_pEnabledMod = NULL;
child->SetEnabled(false);
if ( disabledPackages )
disabledPackages->push_back(child);
}
// disabling has completed successfully, we hope
m_bDisableVanilla = false;
m_lDisableList.clear();
this->WriteData();
return true;
}
/**
* Disables the selected package
*
* Disables all enabled files that are not being used by any other enabled package
* Any files that are being used by other enabled packages are skipped and left enabled
*
* Original Files are restored when file is disabled
*
* Fake patches are shuffled to fill in any gaps caused by disabling fake patches
*
* All files go into the Plugin/Disabled directory into thier respective directories.
*
* Any directories left empty when disabling files are then removed
*
* param: package - Package file to be disabled
* param: errors - A string list used to add the status as it progresses, used in debugging output
* param: progress - The progress class, updates the progress of the current disabling. Needs a divered class to report the progress somewhere
*
* return: boolean, true if there was no errors, otherwise false
*/
bool CPackages::disablePackage(CBaseFile *package, Utils::WStringList *errors, CProgressInfo *progress )
{
// if already disabled, just skip
if ( !package->IsEnabled() )
return true;
m_lDisableList.clear();
if ( this->PrepareDisablePackage(package) )
return this->disablePreparedPackages(errors, progress);
return false;
}
/**
* Find a Package
*
* Finds a matching package so we can find if one is already installed
*
* Uses seperate functions for each package type, ie for SPK and XSP packages
*/
CBaseFile* CPackages::findPackage(CBaseFile* package) const
{
// no point checking if we've been sent a null pointer
if ( !package )
return 0;
// we are checking against a SPK package, so we match the name and author
if ( package->GetType() == TYPE_SPK )
return findSpkPackage(package->name(), package->author());
else if ( package->GetType() == TYPE_XSP )
return findXspPackage(((CXspFile *)package)->shipID());
else if ( package->GetType() == TYPE_ARCHIVE )
return findArchivePackage(package->name());
// nothing found obviously
return 0;
}
CBaseFile *CPackages::findFirstPackageWithFile(C_File *f) const
{
return findNextPackageWithFile(NULL, f);
}
CBaseFile *CPackages::findNextPackageWithFile(CBaseFile *p, C_File *f) const
{
bool startCheck = (p) ? false : true;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( startCheck )
{
if ( node->Data()->IsFileAdded(f) )
return node->Data();
}
else if ( p == node->Data() )
startCheck = true;
}
return NULL;
}
/**
* Find a File
*
* Searches for a file matching the filetype and filename
* Optional dir is used for extras files
*/
C_File *CPackages::findFile(FileType filetype, const Utils::WString &filename, const Utils::WString &dir) const
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( f->GetFileType() != filetype )
continue;
if ( !f->filename().Compare(filename) )
continue;
if ( !dir.empty() && f->dir().Compare(dir) )
continue;
return f;
}
return NULL;
}
CArchiveFile *CPackages::findArchivePackage(const Utils::WString &name) const
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// only look for archive packages
if ( file->GetType() != TYPE_ARCHIVE )
continue;
// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
if ( file->name().Compare(name) )
return (CArchiveFile *)file;
}
// nothing found
return 0;
}
/**
* Find a SPK Package
*
* This searching all installed packages for a SPK Package matching the name and author
*/
CBaseFile *CPackages::findSpkPackage(const Utils::WString &name, const Utils::WString &author) const
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// only look for spk packages
if ( file->GetType() != TYPE_SPK )
continue;
// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
if ( file->name().Compare(name) && file->author().Compare(author) )
return file;
}
// nothing found
return 0;
}
CBaseFile* CPackages::findPackage(const Utils::WString &name, const Utils::WString &author) const
{
// interate through all packages
for (CListNode<CBaseFile>* node = m_lPackages.Front(); node; node = node->next())
{
CBaseFile* file = node->Data();
// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
if (file->name().Compare(name) && file->author().Compare(author))
return file;
}
// nothing found
return 0;
}
/**
* Find a XSP Package
*
* This searching all installed packages for a XSP Package matching the object id
*/
CBaseFile *CPackages::findXspPackage(const Utils::WString &id) const
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// only look for spk packages
if ( file->GetType() != TYPE_XSP )
continue;
// now compare the id, "Compare" is a non case senseative check, opposed to ==.
if ( ((CXspFile *)file)->shipID().Compare(id) )
return file;
}
// nothing found
return 0;
}
/**
* Update the used files count
*
* counts how many packages are currently using the file
*/
void CPackages::UpdateUsedFiles(CLinkList<CBaseFile> *ignoreList, bool includedisabled)
{
// clear amounts for all files
CListNode<C_File> *fnode = m_lFiles.Front();
while ( fnode )
{
fnode->Data()->clearUsed();
fnode = fnode->next();
}
// update for all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node= node->next() )
{
CBaseFile *file = node->Data();
if ( (ignoreList) && (ignoreList->FindData(file)) )
continue;
if ( !includedisabled && !file->IsEnabled() )
continue;
// now mark all files
CListNode<C_File> *fnode = file->GetFileList()->Front();
while ( fnode )
{
fnode->Data()->incUsed();
fnode = fnode->next();
}
}
}
/**
* Removes all empty directories that might have been created
*/
void CPackages::removeUnusedDirectories(const Utils::WStringList& dirs, Utils::WStringList* errors)
{
CDirIO Dir(m_sCurrentDir);
for(auto itr = dirs.begin(); itr != dirs.end(); itr++)
{
Utils::WString dir = (*itr)->str;
Utils::WStringList removedDir;
if (Dir.removeDir(dir, false, true, &removedDir) || !removedDir.empty())
{
for(auto rItr = removedDir.begin(); rItr != removedDir.end(); rItr++)
{
Utils::WString displayName = (*rItr)->str;
displayName = displayName.findRemove(m_sCurrentDir);
this->addLogEntry(SPKINSTALL_REMOVEDIR, displayName, errors);
}
}
}
}
/**
* Update signed status
*
* Updates the signed status of all packages
*/
void CPackages::UpdateSigned()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->GetType() != TYPE_SPK )
continue;
((CSpkFile *)p)->updateSigned(false);
}
}
/**
* Check validity of package file
*
* Checks if there is a newer version of the package installed
*/
int CPackages::CheckInstallPackage(CBaseFile *package, int check)
{
if ( package->GetType() == TYPE_XSP && m_iGame == GAME_X2 )
return INSTALLCHECK_NOSHIP;
// search for an old version
CBaseFile *oldPackage = findPackage(package);
// check versions are newer
if (oldPackage && (check & IC_OLDVERSION))
{
if ( oldPackage->version().compareVersion(package->version()) == COMPARE_OLDER )
return INSTALLCHECK_OLDVERSION;
}
// now check for game version
if ((check & IC_WRONGGAME) || (check & IC_WRONGVERSION))
{
if ( package->AnyGameCompatability() )
{
if ( (check & IC_WRONGGAME) && (!package->CheckGameCompatability(m_iGame)) )
return INSTALLCHECK_WRONGGAME;
else if ( (check & IC_WRONGVERSION) && (!package->checkGameVersionCompatability(m_iGame, _sGameVersion, m_iGameVersion)) )
return INSTALLCHECK_WRONGVERSION;
}
}
// check for modified
if (m_bVanilla && (check & IC_MODIFIED))
{
if ( !package->IsSigned() )
return INSTALLCHECK_MODIFIED;
}
return INSTALLCHECK_OK;
}
bool CPackages::CheckOtherPackage(CBaseFile *package)
{
// check the need for another mod
if ( package->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)package;
if ( spk->IsAnotherMod() )
{
if ( !findSpkPackage(spk->otherName(), spk->otherAuthor()) )
{
// check the install list
if ( m_lInstallList.empty() )
return false;
bool found = false;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
if (node->Data() && spk->otherName().Compare(node->Data()->name()) && spk->otherAuthor().Compare(node->Data()->author()) )
{
found = true;
break;
}
}
if ( !found )
return false;
}
}
}
return true;
}
bool CPackages::CheckEnabledDependacy(CBaseFile *p)
{
// check the for another mod
if ( p->GetType() == TYPE_SPK )
{
if ( ((CSpkFile *)p)->IsAnotherMod() )
{
CBaseFile *parent = this->findSpkPackage(((CSpkFile *)p)->otherName(), ((CSpkFile *)p)->otherAuthor());
if ( parent )
{
if ( !parent->IsEnabled() )
return false;
}
else
return false;
}
}
// check any dependacies
if ( p->AnyDependacies() )
{
for ( CListNode<SNeededLibrary> *dNode = p->GetNeededLibraries()->Front(); dNode; dNode = dNode->next() )
{
if ( dNode->Data()->sName.Compare(L"<package>") )
continue;
if ( !this->checkInstalledDependacy(dNode->Data()->sName, dNode->Data()->sAuthor, dNode->Data()->sMinVersion, true, true))
return false;
}
}
return true;
}
bool CPackages::checkInstalledDependacy(const Utils::WString &name, const Utils::WString &author, const Utils::WString &version, bool onlyEnabled, bool includePrepared) const
{
CBaseFile *p = this->findSpkPackage(name, author);
if ( p )
{
// now check version
if (version.compareVersion(p->version()) == COMPARE_OLDER)
return false;
if ( onlyEnabled && !p->IsEnabled() )
return false;
return true;
}
// now check the prepared list
if ( includePrepared )
{
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
p = node->Data();
if ( !p )
continue;
if ( p->name().Compare(name) && p->author().Compare(author) )
{
if (version.compareVersion(p->version()) == COMPARE_OLDER)
continue;
if ( onlyEnabled && !p->IsEnabled() )
continue;
return true;
}
}
}
return false;
}
bool CPackages::findAllNeededDependacies(CBaseFile *p, const CLinkList<CBaseFile> &packages, CLinkList<CBaseFile> *foundPackages, bool onlyEnabled, bool includePrepared) const
{
CLinkList<SNeededLibrary> *neededList = p->GetNeededLibraries();
if (neededList)
{
for (CListNode<SNeededLibrary> *node = neededList->Front(); node; node = node->next())
{
SNeededLibrary *nl = node->Data();
if (!checkInstalledDependacy((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared))
{
bool found = false;
for (auto itr = packages.Front(); itr; itr = itr->next())
{
if (itr->Data()->name().Compare(nl->sName) && itr->Data()->author().Compare(nl->sAuthor) && nl->sMinVersion.compareVersion(itr->Data()->version()) >= 0)
{
if (!findAllNeededDependacies(itr->Data(), packages, foundPackages, onlyEnabled, includePrepared))
return false;
if (foundPackages)
{
bool added = false;
for (auto itr2 = foundPackages->Front(); itr2; itr2 = itr2->next())
{
if (itr->Data() == itr2->Data() || (itr->Data()->name().Compare(itr2->Data()->name()) && itr->Data()->author().Compare(itr2->Data()->author())))
{
added = true;
break;
}
}
if(!added)
foundPackages->push_front(itr->Data());
}
found = true;
break;
}
}
if (!found)
return false;
}
}
}
if (p->GetType() == TYPE_SPK)
{
CSpkFile *spk = (CSpkFile *)p;
if (spk->isAnotherMod())
{
bool found = true;
if (!this->findSpkPackage(spk->otherName(), spk->otherAuthor()))
{
if (includePrepared)
{
found = false;
for (CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next())
{
CBaseFile *checkP = pNode->Data();
if (p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()))
{
found = true;
break;
}
}
}
else
found = false;
}
if (!found)
{
for (auto itr = packages.Front(); itr; itr = itr->next())
{
if (itr->Data()->name().Compare(spk->otherName()) && itr->Data()->author().Compare(spk->otherAuthor()))
{
if (!findAllNeededDependacies(itr->Data(), packages, foundPackages, onlyEnabled, includePrepared))
return false;
if(foundPackages)
{
bool added = false;
for (auto itr2 = foundPackages->Front(); itr2; itr2 = itr2->next())
{
if (itr->Data() == itr2->Data() || (itr->Data()->name().Compare(itr2->Data()->name()) && itr->Data()->author().Compare(itr2->Data()->author())))
{
added = true;
break;
}
}
if (!added)
foundPackages->push_front(itr->Data());
}
found = true;
break;
}
}
if(!found)
return false;
}
}
}
return true;
}
size_t CPackages::getDownloadableDependacies(CBaseFile* p, std::vector<const SAvailablePackage*>& list, bool onlyEnabled, bool includePrepared) const
{
size_t count = 0;
CLinkList<SNeededLibrary>* neededList = p->GetNeededLibraries();
if (neededList)
{
for (CListNode<SNeededLibrary>* node = neededList->Front(); node; node = node->next())
{
SNeededLibrary* nl = node->Data();
if (!checkInstalledDependacy((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared))
{
const SAvailablePackage *available = findAvailablePackage((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor);
if (available)
list.push_back(available);
++count;
}
}
}
if (p->GetType() == TYPE_SPK)
{
CSpkFile* spk = (CSpkFile*)p;
if (spk->IsAnotherMod())
{
bool found = true;
if (!this->findSpkPackage(spk->otherName(), spk->otherAuthor()))
{
if (includePrepared)
{
found = false;
for (CListNode<CBaseFile>* pNode = m_lInstallList.Front(); pNode; pNode = pNode->next())
{
CBaseFile* checkP = pNode->Data();
if (p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()))
{
found = true;
break;
}
}
}
else
found = false;
}
if (!found)
{
const SAvailablePackage* available = findAvailablePackage(spk->otherName(), spk->otherAuthor());
if (available)
list.push_back(available);
++count;
}
}
}
return count;
}
int CPackages::getMissingDependacies(CBaseFile *p, Utils::WStringList *list, bool onlyEnabled, bool includePrepared)
{
int count = 0;
CLinkList<SNeededLibrary> *neededList = p->GetNeededLibraries();
if ( neededList )
{
for ( CListNode<SNeededLibrary> *node = neededList->Front(); node; node = node->next() )
{
SNeededLibrary *nl = node->Data();
if ( !checkInstalledDependacy((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare(L"<package>")) ? false : onlyEnabled, (nl->sName.Compare(L"<package>")) ? false : includePrepared) )
{
if ( list )
list->pushBack(((nl->sName.Compare(L"<package>")) ? p->name() : nl->sName) + L"|" + nl->sMinVersion, (nl->sAuthor.Compare(L"<author>")) ? p->author() : nl->sAuthor);
++count;
}
}
}
if ( p->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)p;
if ( spk->IsAnotherMod() )
{
bool found = true;
if ( !this->findSpkPackage(spk->otherName(), spk->otherAuthor()) )
{
if ( includePrepared )
{
found = false;
for ( CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *checkP = pNode->Data();
if (checkP && p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()) )
{
found = true;
break;
}
}
}
else
found = false;
}
if ( !found )
{
if ( list )
list->pushBack(spk->otherName(), spk->otherAuthor());
++count;
}
}
}
return count;
}
int CPackages::CheckPreparedInstallRequired(CLinkList<CBaseFile> *list)
{
// loop through all packages
int count = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
// no requirements found, remove from list
bool missingDep = false;
if ( !this->CheckOtherPackage(p) )
missingDep = true;
else if ( this->getMissingDependacies(p, NULL, false, true) )
missingDep = true;
if ( missingDep )
{
if ( list )
{
node->ChangeData(NULL);
list->push_back(p);
}
else
node->DeleteData();
++count;
}
}
m_lInstallList.RemoveEmpty();
return count;
}
/**
* Remove uninstall file
*
* Removes a single uninstall file
*/
bool CPackages::removeUninstallFile(C_File *file, Utils::WStringList *errors)
{
CFileIO fio(file->filePointer());
if ( fio.exists() )
{
if ( fio.remove() ) {
this->addLogEntry(SPKINSTALL_UNINSTALL_REMOVE, file->getNameDirectory(NULL), errors);
return true;
}
else if ( errors )
this->addLogEntry(SPKINSTALL_UNINSTALL_REMOVE_FAIL, file->getNameDirectory(NULL), errors);
}
return false;
}
/**
* Remove uninstall scripts
*
* Removes any unused unisntall scripts
* Finds if any scripts are in the current package
*/
int CPackages::removeUninstallScripts(Utils::WStringList *errors, CProgressInfo *progress)
{
if ( m_lUninstallFiles.empty() )
return 0;
UpdateUsedFiles();
int files = 0;
for ( CListNode<C_File> *node = m_lUninstallFiles.Back(); node; node = node->prev() )
{
if ( progress )
progress->UpdateProgress(files, m_lUninstallFiles.size());
files++;
C_File *file = node->Data();
// first check if there is a matching file
bool found = false;
for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
{
C_File *checkFile = fNode->Data();
if ( checkFile->GetFileType() != FILETYPE_SCRIPT )
continue;
if ( checkFile->filename().Compare(file->filename()) )
{
found = true;
break;
}
}
// not found a matching file, we can safetly remove it
if ( !found )
{
if (removeUninstallFile(file))
node->DeleteData();
}
}
m_lUninstallFiles.RemoveEmpty();
return files;
}
/**
* Remove unused shared file
*
* Removes a single file
*/
bool CPackages::removeSharedFile(C_File *file, Utils::WStringList *errors)
{
CFileIO fio(file->filePointer());
if ( fio.exists() )
{
if ( fio.remove() ) {
this->addLogEntry(SPKINSTALL_SHARED, file->getNameDirectory(NULL), errors);
delete file;
return true;
}
else if ( errors )
this->addLogEntry(SPKINSTALL_SHARED_FAIL, file->getNameDirectory(NULL), errors);
}
return false;
}
/**
* Remove Unused Shared Files
*
* Files that have been marked as shared will not be removed or disabled when the last package is removed
* This function will remove any that are no longer connected with packages
*
* Marked shared fiels are mainly used for library scripts that arn't always added to packages
*/
int CPackages::removeUnusedSharedFiles(Utils::WStringList *errors, CProgressInfo *progress)
{
UpdateUsedFiles();
int files = 0;
int done = 0;
for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
{
if ( progress )
progress->UpdateProgress(files, m_lFiles.size());
files++;
C_File *file = node->Data();
// only do marked shared files
if ( !file->IsShared() )
continue;
// only do ones that are no longer needed
if ( file->getUsed() )
continue;
if (removeSharedFile(file, errors) )
++done;
node->ChangeData(NULL);
}
m_lFiles.RemoveEmpty();
return done;
}
/**
* Any Unused Shared
*
* Checks if theres any unused shared files available
*
* Any file thats marked as shared, and is no longer connected to any installed package
*/
bool CPackages::AnyUnusedShared()
{
UpdateUsedFiles();
for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
{
C_File *file = node->Data();
// only do marked shared files
if ( !file->IsShared() )
continue;
// only do ones that are no longer needed
if ( file->getUsed() )
continue;
return true;
}
return false;
}
void CPackages::shuffleTextFiles(Utils::WStringList *errors)
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// only do files that are enabled
if ( f->IsDisabled() )
continue;
if ( f->GetFileType() != FILETYPE_TEXT )
continue;
// check if the file is an auto text file
if ( !f->isAutoTextFile() )
continue;
// we need to rename it
int current = findNextTextFile();
if ( current < f->textFileID() )
{
CFileIO moveFile(f->filePointer());
Utils::WString newName = SPK::FormatTextName(current, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT)) + L"." + moveFile.extension();
if ( moveFile.Rename(m_sCurrentDir + L"/t/" + newName) )
{
this->addLogEntry(SPKINSTALL_AUTOTEXT, f->name() + L"~" + newName, errors);
f->setName(newName);
}
else
this->addLogEntry(SPKINSTALL_AUTOTEXT_FAIL, f->name() + L"~" + newName, errors);
}
}
}
/**
* Shuffle Fake Patches
*
* Rename the fake patches so they are always in sequence to be loaded into the game
*
* IE, when one is removed, the rest need to be shuffled down so theres no gaps
*/
void CPackages::shuffleFakePatches(Utils::WStringList *errors)
{
int check = findNextFakePatch();
// lets make sure our order is correct
// find Lowest Fake Patch Installed
CLinkList<C_File> doneList;
int lowest = FindLowestFakePatchInstalled();
if ( check < lowest ) lowest = check; // gap at the beginning, lets use that
// find all packages that need to go before or after other packages
std::set<CBaseFile*> packagesBefore;
std::map<CBaseFile*, std::set<CBaseFile*>> packagesAfter;
for (CListNode<CBaseFile>* pNode = m_lPackages.Front(); pNode; pNode = pNode->next())
{
CBaseFile* p = pNode->Data();
// search for any fake patches in package
if (!p->IsEnabled()) continue;
if (!p->AnyFileType(FILETYPE_MOD)) continue;
if (p->AnyDependacies())
{
for (SNeededLibrary* nl = p->GetNeededLibraries()->First(); nl; nl = p->GetNeededLibraries()->Next())
{
auto package = findPackage(nl->sName, nl->sAuthor);
if (package)
{
packagesBefore.insert(package);
packagesAfter[p].insert(package);
}
}
}
CSpkFile* spk = dynamic_cast<CSpkFile*>(p);
if (spk)
{
if (spk->IsAnotherMod())
{
auto package = findPackage(spk->otherName(), spk->otherAuthor());
if (package)
{
packagesBefore.insert(package);
packagesAfter[p].insert(package);
}
}
}
if (!p->getFakePatchBeforeOrder().empty())
{
packagesBefore.insert(p);
auto& list = p->getFakePatchBeforeOrder();
for (auto itr = list.begin(); itr != list.end(); itr++)
{
auto package = findPackage((*itr)->str, (*itr)->data);
if (package)
packagesAfter[package].insert(p);
}
}
if (!p->getFakePatchAfterOrder().empty())
{
auto& list = p->getFakePatchAfterOrder();
for (auto itr = list.begin(); itr != list.end(); itr++)
{
auto package = findPackage((*itr)->str, (*itr)->data);
if (package)
{
packagesBefore.insert(package);
packagesAfter[p].insert(package);
}
}
}
}
auto addOrderedList = [](const std::vector<CBaseFile*>& addList, const Utils::WStringList& orderList, std::vector<CBaseFile*>& addTo, const CPackages *packages)
{
// add all the items in the ordered list first
for (auto itr = orderList.begin(); itr != orderList.end(); itr++)
{
auto p = packages->findPackage((*itr)->str, (*itr)->data);
if (p)
{
auto findItr = std::find(addList.begin(), addList.end(), p);
if (findItr != addList.end())
{
auto checkItr = std::find(addTo.begin(), addTo.end(), p);
if(checkItr == addTo.end())
addTo.push_back(p);
}
}
}
// now add all the others
for (auto itr = addList.begin(); itr != addList.end(); itr++)
{
auto checkItr = std::find(addTo.begin(), addTo.end(), *itr);
if (checkItr == addTo.end())
addTo.push_back(*itr);
}
};
auto removeList = [](const std::vector<CBaseFile*>& removeList, std::set<CBaseFile*>& list)
{
for (auto itr = removeList.begin(); itr != removeList.end(); itr++)
{
auto findItr = std::find(list.begin(), list.end(), *itr);
if (findItr != list.end())
list.erase(findItr);
}
};
std::vector<CBaseFile*> order;
while (!packagesBefore.empty())
{
std::vector<CBaseFile*> packagesOrder;
// all packages that go before, but not after should go first
for (auto itr = packagesBefore.begin(); itr != packagesBefore.end(); itr++)
{
CBaseFile* p = *itr;
auto findItr = packagesAfter.find(p);
if (findItr == packagesAfter.end())
packagesOrder.push_back(p);
else
{
// check if any packages that we go after have been added
bool notAdded = false;
for (auto checkItr = findItr->second.begin(); checkItr != findItr->second.end(); checkItr++)
{
auto findItr2 = std::find(order.begin(), order.end(), *checkItr);
if (findItr2 == order.end())
{
notAdded = true;
break;
}
}
if (!notAdded)
packagesOrder.push_back(p);
}
}
// no valid packages left ? lets just add the rest
if (packagesOrder.empty())
{
for (auto itr = packagesBefore.begin(); itr != packagesBefore.end(); itr++)
packagesOrder.push_back(*itr);
}
addOrderedList(packagesOrder, _lFakePatchOrder, order, this);
removeList(packagesOrder, packagesBefore);
}
// now add the remaining list ordered list
for (auto itr = _lFakePatchOrder.begin(); itr != _lFakePatchOrder.end(); itr++)
{
auto p = findPackage((*itr)->str, (*itr)->data);
if (p)
{
auto findItr = std::find(order.begin(), order.end(), p);
if (findItr == order.end())
order.push_back(p);
}
}
for(auto itr = order.begin(); itr != order.end(); itr++)
{
CBaseFile *package = *itr;
if ( package )
{
// might have more than 1 fake patch for the file, so we'll need the lowest
while ( true )
{
C_File *lowestFile = NULL;
for ( C_File *file = package->GetFirstFile(FILETYPE_MOD); file; file = package->GetNextFile(file) )
{
if (!file->IsFakePatch()) continue;
if (!file->checkFileExt(L"cat")) continue; // only do the cat file, we can shuffle the dat file to match later
if (doneList.FindData(file)) continue; // already done?
if ( !lowestFile )
lowestFile = file;
else
{
if ( file->baseName().toInt() < lowestFile->baseName().toInt() )
lowestFile = file;
}
}
if ( !lowestFile ) // no more files ?
break;
// check its filename, it might already be in the correct place
if ( lowestFile->baseName().toInt() != lowest )
{
// if the file already exists, we need to move it elsewhere
Utils::WString nextName = Utils::WString::PadNumber(lowest, 2);
if ( CFileIO::Exists(m_sCurrentDir + L"/" + nextName + L".cat") )
{
// find the file in our internal file list
C_File *moveFile = findFile(FILETYPE_MOD, nextName + L".cat");
if ( !moveFile ) // must not have it in our list ? lets move to the next
{
lowest++;
continue;
}
// now we can move the the cat/dat file elsewhere, lets shuffle it to the highest free number
shufflePatchTo(moveFile, findLastFakePatch(), errors);
}
// space should be free, now lets shuffle it
shufflePatchTo(lowestFile, lowest, errors);
}
doneList.push_back(lowestFile); // we've done this file now
lowest++; // move up the lowest ready for the next patch
}
}
}
// now lets shuffle the rest
// now find any packages with greater fake patchs and fill the gaps
std::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 enabled
if (f->IsDisabled())
continue;
// check if the file is a fake patch
if (!f->IsFakePatch())
continue;
// we only want cat and dat files, all fake patchs should be, but incase theres an error in the package somewhere we can check
if (!f->checkFileExt(L"cat"))
continue;
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 gap
int 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 gap
Utils::WString newName = Utils::WString::PadNumber(to, 2) + L"." + file->fileExt();
// now rename the file
CFileIO moveFile(file->filePointer());
if ( moveFile.Rename(m_sCurrentDir + L"/" + newName) )
{
// display moveing
this->addLogEntry(SPKINSTALL_FAKEPATCH, file->name() + L"~" + newName, errors);
// now find the matching pairing if it exists
for ( CListNode<C_File> *node2 = m_lFiles.Front(); node2; node2 = node2->next() )
{
C_File *f2 = node2->Data();
if ( f2->IsDisabled() || !f2->IsFakePatch() || f2->checkFileExt(file->fileExt()))
continue;
// needs to be the same file
if ( f2->baseName() != file->baseName() )
continue;
Utils::WString newName2 = Utils::WString::PadNumber(to, 2) + L"." + f2->fileExt();
CFileIO moveFile(f2->filePointer());
if ( moveFile.Rename(m_sCurrentDir + L"/" + newName2) )
{
this->addLogEntry(SPKINSTALL_FAKEPATCH, f2->name() + L"~" + newName2, errors);
f2->setName(newName2);
}
else
this->addLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->name() + L"~" + newName2, errors);
}
// finally make sure the internal name matches the new one
file->setName(newName);
}
else
this->addLogEntry(SPKINSTALL_FAKEPATCH_FAIL, file->name() + L"~" + newName, errors);
}
int CPackages::FindLowestFakePatchInstalled()
{
int lowest = 99;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() ) continue;
if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
// now get all fake patches
for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
{
if (!file->IsFakePatch()) continue;
if (file->checkFileExt(L"dat")) continue;
int check = file->baseName().toInt();
if ( check < lowest )
lowest = check;
}
}
return lowest;
}
int CPackages::findLastFakePatch(int start, const Utils::WString &dir) const
{
CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);
int check = start;
while ( check > 0 )
{
Utils::WString checkStr = Utils::WString::PadNumber(check, 2);
// check if a cat file exists
if ( !Dir.exists(checkStr + L".cat") )
{
// it doen't, check if theres a dat file (incase of package error)
if ( !Dir.exists(checkStr + L".dat") )
break;
}
--check;
}
return check;
}
/**
* Find next fake patch
*
* Searching for the next gap in patches, starting with 01.cat to 99.cat
*/
int CPackages::findNextFakePatch(int start, const Utils::WString &dir) const
{
CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);
int check = start;
while ( check < 99 )
{
++check;
Utils::WString checkStr = Utils::WString::PadNumber(check, 2);
// check if a cat file exists
if ( !Dir.exists(checkStr + L".cat") )
{
// it doen't, check if theres a dat file (incase of package error)
if ( !Dir.exists(checkStr + L".dat") )
break;
}
}
return check;
}
/**
* Find next text file
*
* Searching for the next gap in automatic text files, start with 0004-LXXX or XX0004
*/
unsigned int CPackages::findNextTextFile(unsigned int start) const
{
return findNextTextFile(Utils::WString::Null(), start);
}
unsigned int CPackages::findNextTextFile(const Utils::WString &dir, unsigned int start) const
{
int check = start;
if ( check < 2 ) check = 2;
while ( check < 9999 )
{
++check;
Utils::WString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
// check the vfs
if ( m_pGameVFS.isFileAvailable(L"t/" + newFilename + L".pck") ) continue;
if ( m_pGameVFS.isFileAvailable(L"t/" + newFilename + L".xml") ) continue;
break;
}
return check;
}
int CPackages::findLastTextFile(int start, const Utils::WString& dir) const
{
CDirIO Dir((dir.empty()) ? m_sCurrentDir : dir);
Dir.cd(L"t");
int check = start;
while ( check < 9999 )
{
++check;
Utils::WString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
// check if a packed file exists
if ( !Dir.exists(newFilename + L".pck") )
{
// it doen't, check if theres an unpacked file
if ( !Dir.exists(newFilename + L".xml") )
break;
}
}
return check;
}
/**
* Read game language
*
* Reads the lang.dat file from the game directory for the language id
*/
void CPackages::ReadGameLanguage(bool force)
{
// check if lauguage is already set
if ( !force && m_iLanguage )
return;
CDirIO Dir(m_sCurrentDir);
// check for lang.dat file
if ( Dir.exists(L"lang.dat") )
{
CFileIO File(Dir.file(L"lang.dat"));
size_t size;
char *data = File.ReadToData(&size);
if ( data )
{
Utils::WString str(data);
m_iLanguage = str.token(L"\n", 1).token(L" ", 1).toInt();
}
}
}
/**
* Create Language Text File
*
* Creates text files for all packages into the correct language
*/
void CPackages::CreateLanguageTextFiles(Utils::WStringList *errors)
{
// no need to create them if theres no language to use
if ( !m_iLanguage )
return;
// find all text files
Utils::WStringList ids;
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// only do text fiels
if ( f->GetFileType() != FILETYPE_TEXT )
continue;
Utils::WString id, lang;
if ( m_iGameFlags & EXEFLAG_TCTEXT )
{
id = f->baseName().token(L"-", 1);
lang = f->baseName().token(L"-", 2);
if ( lang.empty() )
lang = L"NULL";
else
lang = lang.erase(0, 1); // remove the "L"
}
else
{
lang = Utils::WString::PadNumber(f->baseName().left((int)f->baseName().length() - 4), 3);
id = f->baseName().mid(((int)f->baseName().length() - 4) + 1, 4);
}
// not added, add a new one
if (!ids.contains(id))
ids.pushBack(id, lang);
else
{
Utils::WString data = ids.findString(id);
if (data.empty())
data = lang;
else
{
data += L":";
data += lang;
}
ids.changeData(id, data);
}
}
// we should now have a list of all text files, we need to remove those that have a matching language
for(auto itr = ids.begin(); itr != ids.end(); itr++)
{
int size = 0;
std::vector<Utils::WString> data;
if(!(*itr)->data.tokenise(L":", data))
continue; // huh ? we shouldn't have this
// lets search for a matching id
int useId = 0;
bool found = false;
for ( int i = 0; i < size; i++ )
{
if ( data[i] == L"NULL" )
useId = -1;
else
{
int num = data[i].toInt();
if ( num == m_iLanguage )
{
found = true;
useId = m_iLanguage;
break;
}
// prioities the text file to use, no language first, then 44, then 49, otherwise, we pick the first available
else if ( num == 44 && useId != -1)
useId = 44;
// if we have a german language, and its not yet found english or null
else if ( num == 49 && useId != 44 && useId != -1 )
useId = 49;
// if we have not found a valid language yet, we will use this one
else if ( !useId )
useId = num;
}
}
if (found)
continue;
if ( !useId )
useId = data[0].toInt();
if ( !useId )
useId = -1;
renameTextFile((*itr)->str, useId, errors);
}
}
/**
* Rename a text file
*
* Creates a new text file for the selected langage by copying an existing one
*/
bool CPackages::renameTextFile(const Utils::WString &textid, int languageid, Utils::WStringList *errors)
{
// lets check if the file already exists
Utils::WString newFilename = SPK::FormatTextName(textid.toInt(), m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
C_File *addFile = findFile(FILETYPE_TEXT, newFilename + L".xml");
if ( !addFile )
addFile = findFile(FILETYPE_TEXT, newFilename + L".pck");
// we have found the file, lets just add it to our scripts
if ( addFile )
{
addTextFileToScripts(addFile, textid);
return true;
}
// first we need to find our text file
Utils::WString filename;
if ( languageid != -1 )
filename = SPK::FormatTextName(textid.toInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));
else
filename = textid;
C_File *textFile = findFile(FILETYPE_TEXT, filename + L".xml");
if ( !textFile )
{
textFile = findFile(FILETYPE_TEXT, filename + L".pck");
if ( !textFile )
return false;
}
// now lets create out new text file
CFileIO readFile(textFile->filePointer());
std::vector<Utils::WString> lines;
readFile.readLines(lines);
// find the language id in the lines
std::vector<Utils::WString> frontLines;
for(std::vector<Utils::WString>::iterator it = lines.begin(); it != lines.end(); ++it)
{
Utils::WString line = *it;
int pos = line.findPos(L"<language id");
if ( pos != -1)
{
Utils::WString newLine = L"<language id=\"";
newLine += Utils::WString::Number(m_iLanguage);
newLine += L"\">";
newLine += line.tokens(L">", 2);
frontLines.insert(frontLines.begin(), newLine);
lines.erase(lines.begin(), ++it);
break;
}
frontLines.insert(frontLines.begin(), line);
}
for(std::vector<Utils::WString>::iterator it = frontLines.begin(); it != frontLines.end(); ++it)
lines.insert(lines.begin(), *it);
addFile = new C_File(newFilename + ".xml");
addFile->setFileType(FILETYPE_TEXT);
CFileIO writeFile(m_sCurrentDir + L"/" + addFile->getNameDirectory(NULL));
if ( writeFile.writeFile(lines) )
{
this->addLogEntry(SPKINSTALL_WRITEFILE, addFile->getNameDirectory(NULL), errors);
addFile->setFilename(m_sCurrentDir + L"/" + addFile->getNameDirectory(NULL));
// now we have the file wrriten, we need to add it to all scripts that need it
// first we add it to the global list
m_lFiles.push_back(addFile);
// now add it to the scripts
addTextFileToScripts(addFile, textid);
}
else
{
this->addLogEntry(SPKINSTALL_WRITEFILE_FAIL, addFile->getNameDirectory(NULL), errors);
return false;
}
return true;
}
/**
* Add file to scripts
*
* Adds a file to all scripts that need it
*/
void CPackages::addTextFileToScripts(C_File *file, const Utils::WString &textid)
{
// now we need to find all scripts that have a matching file and add this to the list
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *package = node->Data();
// first make sure it doesn't already exist
if ( package->GetFileList()->FindData(file) )
continue;
bool found = false;
for ( CListNode<C_File> *fNode = package->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *tFile = fNode->Data();
if ( tFile->GetFileType() != FILETYPE_TEXT )
continue;
Utils::WString id = tFile->baseName().token(L"-", 1);
if ( id == textid )
{
found = true;
break;
}
}
if ( found )
package->GetFileList()->push_back(file);
}
}
bool CPackages::removeFile(C_File *file, Utils::WStringList *errors)
{
if ( !file )
return true;
Utils::WString remFileStr = file->filePointer().findReplace(m_sCurrentDir, L"");
if ( file->filePointer().contains(L"::")) {
CFileIO CatFile(file->filePointer().token(L"::", 1));
if ( CatFile.exists() ) {
CCatFile cat;
if ( cat.open(CatFile.fullFilename(), this->getAddonDir(), CATREAD_DAT, false) == CATERR_NONE) {
Utils::WString fileName = file->filePointer().token(L"::", 2);
if ( cat.findData(fileName) ) {
if ( cat.removeFile(fileName) ) {
this->addLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
}
else {
this->addLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
return false;
}
}
}
}
}
else {
CFileIO f(file->filePointer());
if ( f.exists() )
{
if ( f.remove() ) this->addLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
else if ( errors )
{
this->addLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
return false;
}
else
return false;
}
}
return true;
}
int CPackages::removeAllPackages(Utils::WStringList *errors, CProgressInfo *progress)
{
int files = 0;
// remove all files
int max = m_lFiles.size() + _pOriginalFiles->count() + m_lUninstallFiles.size();
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
// update the progress
if ( progress )
progress->UpdateProgress(files, max);
++files;
removeFile(node->Data());
delete node->Data();
}
m_lFiles.clear();
// restore any original files that are backed up
//TODO: find a better way to do the progress
files = _pOriginalFiles->restoreAll(progress, files, max);
// remove any uninstall files that remain
for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
{
// update the progress
if ( progress )
progress->UpdateProgress(files, max);
++files;
removeFile(uNode->Data());
delete uNode->Data();
}
m_lUninstallFiles.clear();
// delete all packages
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
// clear file list as we have removed them already
pNode->Data()->GetFileList()->clear();
// delete the memory for the package
delete pNode->Data();
}
m_lPackages.clear();
return files;
}
/**
* Get Install Before Text
*
* Returns the install before text for the package in the correct language
*/
Utils::WString CPackages::getInstallBeforeText(CBaseFile *package) const
{
return package->installText(m_iLanguage, true);
}
Utils::WString CPackages::getInstallAfterText(CBaseFile *package) const
{
return package->installText(m_iLanguage, false);
}
Utils::WString CPackages::getUninstallBeforeText(CBaseFile *package) const
{
return package->uninstallText(m_iLanguage, true);
}
Utils::WString CPackages::getUninstallAfterText(CBaseFile *package) const
{
return package->uninstallText(m_iLanguage, false);
}
int CPackages::GetChildPackages(CBaseFile *package, CLinkList<CBaseFile> *children, bool recursive)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->GetParent() == package )
{
children->push_back(p);
if ( recursive )
this->GetChildPackages(p, children, recursive);
}
}
return children->size();
}
void CPackages::AssignPackageNumbers()
{
int num = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
node->Data()->SetNum(num++);
}
Utils::WString CPackages::findDataDir(const Utils::WString &dir, const Utils::WString &file)
{
CDirIO Dir(dir);
// data files could be in 4 places depending on what program is used, check for all of these
if (Dir.exists(file))
return Dir.file(file);
else if (Dir.exists(L"Data/" + file))
return Dir.file(L"Data/" + file);
else if (Dir.exists(L"../" + file))
return Dir.file(L"../" + file);
else if (Dir.exists(L"../Data/" + file))
return Dir.file(L"../Data/" + file);
return Utils::WString::Null();
}
void CPackages::startup(const Utils::WString &dir, const Utils::WString &tempDir, const Utils::WString &myDoc)
{
this->setTempDirectory(tempDir);
this->setMyDocuments(myDoc);
// need to read the game exe versions
m_gameExe.Reset();
Utils::WString exeFile = this->findDataDir(dir, L"exe");
// if file exists, read it, otherwise, just add
if (!exeFile.empty() && CFileIO::Exists(exeFile))
m_gameExe.readFile(exeFile);
else
{
m_gameExe.parseExe(L"x2.exe|0:5: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;
else
dir = this->getProperDir(dir);
CDirIO Dir(dir);
// check for lang.dat file
if ( Dir.exists(L"lang.dat") )
{
CFileIO File(Dir.file(L"lang.dat"));
size_t size;
char *data = File.ReadToData(&size);
if ( data )
{
Utils::WString str(data);
return str.token(L"\n", 1).token(L" ", 1).toLong();
}
}
return 0;
}
Utils::WString CPackages::getGameRunExe(const Utils::WString &dir) const
{
return m_gameExe.gameRunExe(dir);
}
Utils::WString CPackages::getGameRunExe() const
{
return m_gameExe.gameRunExe(m_sCurrentDir);
}
Utils::WString CPackages::getGameNameFromType(int game) const
{
return m_gameExe.gameNameFromType(game - 1);
}
Utils::WString CPackages::getGameName() const
{
return getGameName(m_sCurrentDir);
}
Utils::WString CPackages::getGameName(const Utils::WString &dir) const
{
return m_gameExe.gameName(dir.empty() ? m_sCurrentDir : dir);
}
Utils::WString CPackages::getProperDir() const
{
return getProperDir(m_sCurrentDir);
}
Utils::WString CPackages::getProperDir(const Utils::WString &dir) const
{
return m_gameExe.properDir((dir.empty()) ? m_sCurrentDir : dir);
}
Utils::WString CPackages::getAddonDir() const
{
return getAddonDir(m_sCurrentDir);
}
Utils::WString CPackages::getAddonDir(const Utils::WString &dir) const
{
return m_gameExe.addonDir((dir.empty()) ? m_sCurrentDir : dir);
}
int CPackages::getGameAddons(Utils::WStringList &exes, const Utils::WString &dir) const
{
return m_gameExe.getGameAddons((dir.empty()) ? m_sCurrentDir : dir, exes);
}
int CPackages::getGameAddons(Utils::WStringList &exes) const
{
return m_gameExe.getGameAddons(m_sCurrentDir, exes);
}
Utils::WString CPackages::getGameTypesString(CBaseFile *package, bool includeVersion) const
{
if ( !package->AnyGameCompatability() )
return L"";
Utils::WString sGames;
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( !sGames.empty() )
sGames += L", ";
sGames += m_gameExe.gameNameFromType(gNode->Data()->iGame - 1);
if ( includeVersion ) {
Utils::WString version = m_gameExe.gameVersionFromType(gNode->Data()->iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
if ( !version.empty() ) {
sGames += L" (";
sGames += version;
sGames += L")";
}
}
}
return sGames;
}
Utils::WString CPackages::getGameVersionString(CBaseFile *package) const
{
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( gNode->Data()->iGame == m_iGame ) {
return m_gameExe.gameVersionFromType(m_iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
}
}
return Utils::WString::Null();
}
Utils::WString CPackages::getGameVersionFromType(int game, int version, const Utils::WString &sVersion) const
{
return m_gameExe.gameVersionFromType(game - 1, version - 1, sVersion);
}
int CPackages::CountBuiltInPackages(bool onlyEnabled)
{
int count = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( !node->Data()->CheckGameCompatability(m_iGame) )
continue;
if ( onlyEnabled && !node->Data()->IsEnabled() )
continue;
if ( node->Data()->author().Compare(L"PluginManager") )
++count;
}
return count;
}
int CPackages::countPackages(int type, bool onlyEnabled) const
{
int count = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( (type != TYPE_BASE) && (p->GetType() != type) )
continue;
if ( (onlyEnabled) && (!p->IsEnabled()) )
continue;
++count;
}
return count;
}
int CPackages::extractGameFile(const Utils::WString &aFilename, const Utils::WString &aTo, const Utils::WString &aDir, const Utils::WString &addon) const
{
// first check the enabled mod
Utils::WString dir = aDir;
if (dir.empty())
dir = m_sCurrentDir;
Utils::WString addonDir = addon;
if (addonDir.empty()) addonDir = this->getAddonDir(dir);
if (m_pEnabledMod && m_pEnabledMod->AnyFileType(FILETYPE_MOD))
{
for (C_File* file = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); file; file = m_pEnabledMod->GetNextFile(file))
{
if (!file->checkFileExt(L"cat"))
continue;
CCatFile catFile;
if (catFile.open(file->filePointer(), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
if (catFile.extractFile(aFilename, aTo))
return 1;
if (catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR)
{
if (catFile.extractFile(aFilename))
return -1;
}
}
}
}
else if (!m_sSetMod.empty())
{
if (CFileIO::Exists(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat"))
{
CCatFile catFile;
if (catFile.open(m_sCurrentDir + L"/mods/" + m_sSetMod + L".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
if (catFile.extractFile(aFilename, aTo))
return 1;
if (catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR)
{
if (catFile.extractFile(aFilename))
return -1;
}
}
}
}
// find the highest cat number
int catNumber = 1;
while (CFileIO::Exists(dir + L"/" + Utils::WString::PadNumber(catNumber, 2) + L".cat") && catNumber < 99)
++catNumber;
// get the last one, not the next free one
--catNumber;
// work backwards until we find the file
for (; catNumber; catNumber--)
{
CCatFile catFile;
if (catFile.open((dir + L"/" + Utils::WString::PadNumber(catNumber, 2) + L".cat"), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
// check for the file
if (catFile.extractFile(aFilename, aTo))
return 1;
if (catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR)
{
if (catFile.extractFile(aFilename))
return -1;
}
}
}
return 0;
}
void CPackages::CreateWareFiles()
{
// do each ware
int wareTextID = WARETEXTSTART;
for ( int i = 0; i < WAREBUFFERS; i++ )
{
// its empty, no need to create ware files
if ( !m_lGameWares[i].size() )
continue;
// lets extract the ware file
wchar_t wareType = CPackages::ConvertWareTypeBack(i);
Utils::WString wareFile = L"TWare";
wareFile += (wchar_t)std::toupper(wareType);
Utils::WString openFile;
int e;
if ( i == WARES_TECH && CFileIO::Exists(m_sTempDir + L"/TWareT.txt") )
openFile = m_sTempDir + L"/TWareT.txt";
else {
e = extractGameFile(L"types/" + wareFile + L".pck", m_sTempDir + L"/" + wareFile + L".txt");
if ( e == 1 )
openFile = m_sTempDir + L"/" + wareFile + L".txt";
else if ( e == -2 )
openFile = wareFile + L".txt";
}
if ( !openFile.empty() )
{
// read the file into memory
Utils::WStringList wareLines;
size_t oldSize = -1;
int version = -1;
// read first number
CFileIO readFile(m_sTempDir + L"/" + wareFile + L".txt");
std::vector<Utils::WString> lines;
if(readFile.readLines(lines))
{
for(auto itr = lines.begin(); itr != lines.end(); itr++)
{
Utils::WString line = *itr;
line.removeFirstSpace();
line.removeChar(9);
line.removeChar('\r');
if ( line.empty() )
continue;
if ( line[0] == '/' )
continue;
if ( oldSize == -1 )
{
version = line.token(L";", 1).toInt();
oldSize = line.token(L";", 2).toInt();
}
else
{
line.removeEndSpace();
if ( line.back() != L';')
line += L";";
wareLines.pushBack(line);
if ( wareLines.size() >= oldSize )
break;
}
}
// apply the buffer
if ( m_iWareBuffer[i] <= 0 )
m_iWareBuffer[i] = wareLines.size() + 10;
// last resort, readjust the buffer
else if ( wareLines.size() > static_cast<size_t>(m_iWareBuffer[i]) )
m_iWareBuffer[i] = wareLines.size();
// add the buffers
while ( wareLines.size() < static_cast<size_t>(m_iWareBuffer[i]) )
wareLines.pushBack(L"27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
// add the ware lines
bool create = false;
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->iType == WARETYPE_NONE )
continue;
else if ( w->iType == WARETYPE_DISABLED )
{
create = true;
wareLines.pushBack(L"27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;" + w->sWareName + L"_DISABLED;");
}
else if ( w->iType == WARETYPE_DELETED || !w->pWare )
{
create = true;
wareLines.pushBack(L"27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_DELETED;");
}
else if ( w->iType == WARETYPE_ADDED )
{
create = true;
w->pWare->iDescID = wareTextID;
w->iText = wareTextID;
wareTextID += 10;
w->iPos = wareLines.size();
long price = this->customWareOveridePrice(w->pWare->sID);
int notority = w->pWare->iNotority;
if ( !this->customWareOverideNoto(w->pWare->sID, ¬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 wares
size_t maxsize = 0;
Utils::WString empWares = empWaresForGame(&maxsize);
if ( maxsize )
{
int e = extractGameFile(L"types/TWareT.pck", m_sTempDir + L"/TWareT.txt");
if (e)
{
// read the file into memory
Utils::WStringList wareLines;
size_t oldSize = -1;
int version = -1;
// read first number
CFileIO readFile((e == -1) ? L"TWareT.txt" : m_sTempDir + L"/TWareT.txt");
std::vector<Utils::WString> lines;
if(readFile.readLines(lines))
{
for ( int i = 0; i < (int)lines.size(); i++ )
{
Utils::WString line(lines.at(i));
line.removeFirstSpace();
line.removeChar('\r');
line.removeChar(9);
if ( line[0] == '/' )
continue;
if ( oldSize == -1 )
{
version = line.token(L";", 1).toLong();
oldSize = line.token(L";", 2).toLong();
}
else
{
line.removeEndSpace();
if ( line.right(1) != L";" )
line += L";";
// check for any override values for built in wares
if ( (i >= 62 && i <= 81) || (i >= 88 && i <= 93) ) {
int pos = i - ((i >= 88) ? 88 : 62);
int price = this->builtInWareOveridePrice(pos);
if ( price ) {
line = line.replaceToken(L";", 9, Utils::WString::Number(price));
line = line.replaceToken(L";", 13, Utils::WString::Number(price));
}
int noto = 0;
if ( this->builtInWareOverideNoto(pos, ¬o) ) {
line = line.replaceToken(L";", 14, Utils::WString::Number(noto));
}
}
// check for any override values for EMP
if ( i >= 116 ) {
int price = this->empOveridePrice(i - 116);
if ( price ) {
line = line.replaceToken(L";", 9, Utils::WString::Number(price));
line = line.replaceToken(L";", 13, Utils::WString::Number(price));
}
int noto = 0;
if ( this->empOverideNoto(i - 116, ¬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 entries
while ( wareLines.size() < maxsize )
wareLines.pushBack(L"27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
std::vector<Utils::WString> empStr;
if(empWares.tokenise(L"\n", empStr))
{
// apply any price overrides
size_t empEntries = empStr.size();
for(size_t i = 0; i < empEntries; i++) {
int price = this->empOveridePrice(i);
if ( price ) {
empStr[i] = empStr[i].replaceToken(L";", 9, Utils::WString::Number(price));
empStr[i] = empStr[i].replaceToken(L";", 13, Utils::WString::Number(price));
}
int noto = 0;
if ( this->empOverideNoto(i, ¬o) ) {
empStr[i] = empStr[i].replaceToken(L";", 14, Utils::WString::Number(noto));
}
}
// remove any empty end entries
while ( empStr[empEntries - 1].empty() )
--empEntries;
Utils::WStringList addAfter;
if ( wareLines.size() > maxsize )
{
// force emp, remove entries to allow them to be added
if ( m_bForceEMP )
{
// more after emp
if ( wareLines.size() > (maxsize + empEntries) )
{
for (size_t i = (maxsize + empEntries); i < wareLines.size(); i++)
{
auto node = wareLines.get(i);
addAfter.pushBack(node->str);
}
}
// no remove them all
while (wareLines.size() > maxsize)
wareLines.removeAt(wareLines.size() - 1);
}
else if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL) // check if old emp is included, and convert it
{
if ( wareLines.size() > 128 )
{
Utils::WString test = wareLines.get(128)->str;
if ( test.tokens(L";", -2).Compare(L"SS_WARE_SW_CUSTOM16_1;") )
{
// if theres any at the end, remove the last emp entry
if ( wareLines.size() > (maxsize + empEntries - 1) )
{
wareLines.removeAt(maxsize + empEntries - 2);
}
wareLines.insertAt(128, L"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;128;");
}
}
}
}
// too many entries, need to remove the first set of EMP wares
size_t i = 0;
if ( wareLines.size() > maxsize )
i = wareLines.size() - maxsize;
for ( ; i < empEntries; i++ )
{
Utils::WString str = empStr[i];
str.removeEndSpace();
str.removeChar(9);
str.removeChar('\r');
if ( str.empty() )
continue;
if ( str.right(1) != L";")
str += L";";
wareLines.pushBack(str);
}
for(auto afterItr = addAfter.begin(); afterItr != addAfter.end(); afterItr++)
wareLines.pushBack((*afterItr)->str);
// finally we write the whole file
wareLines.pushFront(Utils::WString::Number(version) + ";" + Utils::WString::Number(wareLines.size()) + L";", Utils::WString::Null());
wareLines.pushFront(L"// Created by SPKInstaller Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2), Utils::WString::Null());
if ( readFile.writeFile(&wareLines) )
this->packFile(&readFile, L"types\\TWareT.pck");
}
}
}
}
Utils::WString parseXmlText(const Utils::WString &str)
{
Utils::WString newStr(str);
Utils::WStringList changes;
// find all XML commands, &<command>;
Utils::WString sStr = str;
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 acommand
std::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 replaced
Utils::WString repStr = sStr.substr(pos, (colonPos + 1) - pos);
Utils::WString repWithStr = L"<::" + sStr.substr(pos + 1, colonPos - pos - 1) + L"::>";
newStr = newStr.findReplace(repStr, repWithStr);
changes.pushBack(repStr, repWithStr);
}
// find the next command
pos = sStr.toStdWString().find_first_of(L"&", pos + 1);
}
// replace the & now
newStr = newStr.findReplace(L"&", L"&");
// restore the commands
for(auto itr = changes.begin(); itr != changes.end(); itr++)
newStr = newStr.findReplace((*itr)->data, (*itr)->str);
return newStr;
}
Utils::WString CPackages::convertTextString(const Utils::WString &sText)
{
//process any &
Utils::WString text = parseXmlText(sText);
// change special cases
text = text.findReplace(L"(", L"\\(");
text = text.findReplace(L")", L"\\)");
text = text.findReplace(L"[", L"{");
text = text.findReplace(L"]", L"}");
text = text.findReplace(L">", L">");
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\">");
else
writeData.push_back(L" <page id=\"" + Utils::WString::Number(gameNumber) + Utils::WString::PadNumber(PMTEXTFILE, 4) + L"\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
writeData.push_back(L" <t id=\"99998\">[author]Plugin Manager[/author]It appears the plugin manager hasn't been closed properly. Make sure its closed before running the game otherwise things may not work correctly</t>");
writeData.push_back(L" <t id=\"99999\">2</t>");
writeData.push_back(L" </page>");
writeData.push_back(L"</language>");
textFile.writeFileUTF(&writeData);
size_t fileSize;
char *fileData = CFileIO(textFile.fullFilename()).ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
CFileIO pckFile(m_sCurrentDir + L"/t/" + filename + L".pck");
pckFile.WriteData((char *)pckData, newFileSize);
this->addCreatedFile(pckFile.fullFilename());
}
}
textFile.remove();
}
void CPackages::CreatePluginManagerText()
{
int gameNumber = _gameTextNumber();
int lang = m_iLanguage;
if ( !lang || lang < 0 )
lang = 44;
CDirIO Dir(m_sCurrentDir);
if ( !Dir.exists(L"t") )
Dir.create(L"t");
m_iLastUpdated = (int)time(NULL);
Utils::WString filename = SPK::FormatTextName(PMTEXTFILE, lang, (m_iGameFlags & EXEFLAG_TCTEXT));
CFileIO textFile(m_sCurrentDir + L"/t/" + filename + L".xml");
std::vector<Utils::WString> writeData;
CLinkList<SGameWare> lWares;
CLinkList<SGameShip> lShips;
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->iType == WARETYPE_NONE )
continue;
lWares.push_back(w);
}
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( node->Data()->iType == WARETYPE_NONE )
continue;
lShips.push_back(node->Data());
}
writeData.push_back(L"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
writeData.push_back(L"<language id=\"" + Utils::WString::Number(lang) + "\">");
if ( !gameNumber )
writeData.push_back(L" <page id=\"" + Utils::WString::Number(PMTEXTFILE) + L"\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
else
writeData.push_back(L" <page id=\"" + Utils::WString::Number(gameNumber) + Utils::WString::PadNumber(PMTEXTFILE, 4) + L"\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
// write the heading
int start = 10000;
writeData.push_back(L" <t id=\"1\">" + Utils::WString::Number(m_iLastUpdated) + L"</t>");
writeData.push_back(L" <t id=\"2\">" + Utils::WString::Number(this->countPackages(TYPE_SPK, true)) + L"</t>");
writeData.push_back(L" <t id=\"3\">" + Utils::WString::Number(start) + L"</t>");
writeData.push_back(L" <t id=\"4\">" + Utils::WString::Number(lWares.size()) + L"</t>");
writeData.push_back(L" <t id=\"6\">" + Utils::WString::Number(lShips.size()) + L"</t>");
// write some generic texts
writeData.push_back(L" <t id=\"110\">" + Utils::WString::Number((long)m_iLanguage) + L"</t>");
writeData.push_back(L" <t id=\"109\">Plugin Manager: \\033GPoll Gui Data\\033X</t>");
writeData.push_back(L" <t id=\"107\">Plugin Manager: \\033GExport Game Data\\033X </t>");
writeData.push_back(L" <t id=\"100\">\\n</t>");
writeData.push_back(L" <t id=\"101\">\\033B</t>");
writeData.push_back(L" <t id=\"102\">\\033G</t>");
writeData.push_back(L" <t id = \"103\">\\033B</t>");
writeData.push_back(L" <t id=\"104\">\\033X</t>");
writeData.push_back(L" <t id=\"105\">\\033Y</t>");
writeData.push_back(L" <t id=\"106\">\\033C</t>");
writeData.push_back(L" <t id=\"108\">\\033</t>");
writeData.push_back(L" <t id=\"99998\">[author]Plugin Manager[/author]It appears the plugin manager hasn't been closed properly. Make sure its closed before running the game otherwise things may not work correctly</t>");
writeData.push_back(L" <t id=\"99999\">1</t>");
// now write each package
int settingStart = 100000;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() )
continue;
if ( p->GetType() != TYPE_SPK )
continue;
if (p->author().Compare("PluginManager", false))
continue;
CSpkFile *spk = (CSpkFile *)p;
// count text files
Utils::WString textEntries;
int textCount = 0;
C_File *f = p->GetFirstFile(FILETYPE_TEXT);
while ( f )
{
Utils::WString sLang;
Utils::WString id;
if ( m_iGameFlags & EXEFLAG_TCTEXT )
{
id = f->baseName().token(L"-", 1);
sLang = f->baseName().token(L"-", 2);
if ( sLang.empty() )
sLang = L"NULL";
else
sLang = sLang.erase(0, 1); // remove the "L"
}
else
{
sLang = f->baseName().left((int)f->baseName().length() - 4).padNumber(3);
id = f->baseName().mid(((int)f->baseName().length() - 4) + 1, 4);
}
if ( sLang != L"NULL" )
{
if ( sLang.toInt() == lang )
{
++textCount;
if ( !textEntries.empty() )
textEntries += L" ";
textEntries += id;
}
}
f = p->GetNextFile(f);
}
Utils::WString sTextCount((long)textCount);
writeData.push_back(L" <t id=\"" + Utils::WString::Number((long)start) + L"\">" + this->convertTextString(p->name()) + L"</t>");
writeData.push_back(L" <t id=\"" + Utils::WString::Number((long)(start + 1)) + L"\">" + this->convertTextString(p->author()) + L"</t>");
writeData.push_back(L" <t id=\"" + Utils::WString::Number((long)(start + 2)) + L"\">" + this->convertTextString(p->version()) + L"</t>");
writeData.push_back(L" <t id=\"" + Utils::WString::Number((long)(start + 3)) + L"\">" + this->convertTextString(p->name(lang)) + L"</t>");
CLinkList<SSettingType> *settings = spk->settingsList();
if ( settings && settings->size() )
{
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 4) + L"\">" + (long)settings->size() + L"</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 5) + L"\">" + (long)settingStart + L"</t>");
for ( CListNode<SSettingType> *sNode = settings->Front(); sNode; sNode = sNode->next() )
{
SSettingType *st = sNode->Data();
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(settingStart++) + L"\">" + st->sKey + L"</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(settingStart++) + L"\">" + spk->getSetting(st) + L"</t>");
}
}
else
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 4) + L"\">0</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 6) + L"\">" + sTextCount + L"</t>");
if ( textCount )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 7) + L"\">" + textEntries + L"</t>");
start += 10;
}
// write ware names
if ( lWares.size() )
{
writeData.push_back(L" <t id=\"5\">" + Utils::WString::Number(start) + L"</t>");
for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->pWare && w->iType == WARETYPE_ADDED )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)start + L"\">" + this->convertTextString(w->sWareName) + L"</t>");
else
writeData.push_back(Utils::WString(L" <t id=\"") + (long)start + L"\">-1</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 1) + L"\">" + Utils::WString(w->cType) + L"</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 2) + L"\">" + (long)w->iPos + L"</t>");
start += 10;
}
}
if ( lShips.size() )
{
writeData.push_back(L" <t id=\"7\">" + Utils::WString::Number(start) + L"</t>");
for ( CListNode<SGameShip> *node = lShips.Front(); node; node = node->next() )
{
SGameShip *gs = node->Data();
if ( gs->iType == WARETYPE_NONE )
continue;
if ( gs->pPackage && gs->iType == WARETYPE_ADDED )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)start + L"\">" + gs->sShipID + L"</t>");
else
writeData.push_back(Utils::WString(L" <t id=\"") + (long)start + L"\">-1</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 1) + L"\">" + (long)gs->iPos + L"</t>");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(start + 2) + L"\">Ship</t>");
// write shipyard info
if ( gs->pPackage )
{
int doStart = start + 5;
for (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 emp
if (m_iGame == GAME_X3TC || m_iGame == GAME_X3 || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL)
{
writeData.push_back(L" <page id=\"17\" title=\"Plugin Manager Objects\">");
writeData.push_back(GetEMPText());
writeData.push_back(L" </page>");
}
// wares
if ( m_iGame == GAME_X3AP || m_iGame == GAME_X3TC || m_iGame == GAME_X3FL || m_iGame == GAME_X3 || lWares.size() || lShips.size() )
{
if ( !gameNumber )
writeData.push_back(L" <page id=\"17\" title=\"Plugin Manager Objects\">");
else
writeData.push_back(Utils::WString(L" <page id=\"") + (long)gameNumber + L"0017\" title=\"Plugin Manager Objects\">");
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(SHIPSTARTTEXT - 1) + L"\">ZZ_BLANKSHIP</t>");
// object names
for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( !w->pWare || w->iType != WARETYPE_ADDED )
continue;
// find the correct text for the language
Utils::WString name = CSpkFile::GetWareText(w->pWare, m_iLanguage);
Utils::WString desc = CSpkFile::GetWareDesc(w->pWare, m_iLanguage);
if ( !name.empty() )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(w->iText + 3) + L"\">" + this->convertTextString(name) + L"</t>");
if ( !desc.empty() )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(w->iText + 4) + L"\">" + this->convertTextString(desc) + L"</t>");
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( !s->pPackage || s->iType != WARETYPE_ADDED )
continue;
if ( s->pPackage->GetOriginalDescription() )
continue;
Utils::WString name = s->pPackage->textName(m_iLanguage);
Utils::WString desc = s->pPackage->textDescription(m_iLanguage);
if ( !name.empty() )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)s->iText + L"\">" + this->convertTextString(name) + L"</t>");
if ( !desc.empty() )
writeData.push_back(Utils::WString(L" <t id=\"") + (long)(s->iText + 1) + L"\">" + this->convertTextString(desc) + L"</t>");
}
writeData.push_back(L" </page>");
}
writeData.push_back(L"</language>");
textFile.writeFileUTF(&writeData);
size_t fileSize;
char *fileData = CFileIO(textFile.fullFilename()).ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
CFileIO pckFile(m_sCurrentDir + L"/t/" + filename + L".pck");
pckFile.WriteData((char *)pckData, newFileSize);
this->addCreatedFile(pckFile.fullFilename());
}
}
textFile.remove();
// write out the settings file
if (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 files
Utils::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";
else
sLang = sLang.erase(0, 1); // remove the "L"
}
else
{
sLang = f->baseName().left((int)f->baseName().length() - 4).padNumber(3);
id = f->baseName().mid(((int)f->baseName().length() - 4) + 1, 4);
}
if (sLang != L"NULL")
{
if (sLang.toInt() == lang)
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);
else
str = 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;
else
str = L"-1";
str += L";" + Utils::WString::Number(gs->iPos);
// write shipyard info
if (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 directory
Utils::WString dir = (vanilla) ? L"Vanilla" : L"Modified";
// make sure the directory exists
CDirIO saveDir(this->saveDirectory());
CDirIO gameSaveDir(saveDir.dir(_sSaveDir));
if (!gameSaveDir.exists())
gameSaveDir.create();
if (!gameSaveDir.exists(dir))
gameSaveDir.create(dir);
gameSaveDir.cd(dir);
// backup the saves
Utils::WStringList files;
if(saveDir.dirList(files, Utils::WString::Null(), L"*.sav"))
{
for(auto itr = files.begin(); itr != files.end(); ++itr)
{
CFileIO File(saveDir.file((*itr)->str));
if (!File.isFileExtension(L"sav"))
continue;
// remove the file if already exists
if (gameSaveDir.exists((*itr)->str))
CFileIO::Remove(gameSaveDir.file((*itr)->str));
// copy the file into the games save dir for backup
File.copy(gameSaveDir.file(File.filename()), true);
}
}
}
}
void CPackages::restoreSaves(bool vanilla)
{
// get dir to restore from
if (!_sSaveDir.empty())
{
Utils::WString dir = (vanilla) ? L"Vanilla" : L"Modified";
CDirIO toDir(this->saveDirectory());
CDirIO restoreDir(toDir.dir(_sSaveDir));
restoreDir.cd(dir);
if (restoreDir.exists())
{
//if we are in vanilla mode, we should remove the saves (so we only have vanilla saves
/*
if (vanilla) {
Utils::WStringList *files = toDir.DirList();
if (files) {
for (SStringList *node = files->Head(); node; node = node->next)
{
CFileIO saveFile(toDir.File(node->str));
if (saveFile.extension().Compare("sav")) {
saveFile.remove();
}
}
delete files;
}
}
*/
// now we copy of the backed up save games
Utils::WStringList files;
if(restoreDir.dirList(files, Utils::WString::Null(), L"*.sav"))
{
for(auto itr = files.begin(); itr != files.end(); itr++)
{
CFileIO File(restoreDir.file((*itr)->str));
// remove the file if already exists
if (toDir.exists((*itr)->str)) CFileIO::Remove(toDir.file((*itr)->str));
// move file over
File.copy(toDir.file((*itr)->str), true);
}
}
}
}
}
bool CPackages::RemoveCurrentDirectory()
{
if ( !m_bLoaded )
return false;
// remove all package files
this->removeAllPackages();
// remove all plugin manager files
this->RemoveCreatedFiles();
this->Reset();
m_bLoaded = false;
// clear the plugin manager directory
CDirIO Dir(m_sCurrentDir);
Dir.removeDir(L"PluginManager", true, true, 0);
Dir.removeDir(L"dds", false, true, 0);
Dir.removeDir(L"objects", false, true, 0);
Dir.removeDir(L"types", false, true, 0);
Dir.removeDir(L"textures", false, true, 0);
// remove the plugin manager mod files
if ( Dir.exists(L"mods/PluginManager.cat") ) CFileIO::Remove(Dir.file(L"mods/PluginManager.cat"));
if ( Dir.exists(L"mods/PluginManager.dat") ) CFileIO::Remove(Dir.file(L"mods/PluginManager.dat"));
return true;
}
void CPackages::CreateDummies()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return;
CLinkList<SDummyEntry> dummyList;
// now extract the existing dummies
int e = extractGameFile(L"types/Dummies.pck", m_sTempDir + L"/Dummies.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? L"Dummies.txt" : m_sTempDir + L"/Dummies.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int insection = 0;
SDummyEntry *currentSection = NULL;
for ( int j = 0; j < (int)lines.size(); j++ )
{
Utils::WString line(lines.at(j));
line.removeChar(9);
line.removeChar('\r');
line.removeFirstSpace();
line.removeEndSpace();
if ( line.empty() )
continue;
if ( line[0] == '/' )
continue;
// read the section, first entry is section, second is size
while ( !line.empty() )
{
if ( !insection )
{
Utils::WString section = line.token(L";", 1);
insection = line.token(L";", 2).toInt();
// search for the sections
currentSection = NULL;
for ( CListNode<SDummyEntry> *node = dummyList.Front(); node; node = node->next() )
{
SDummyEntry *d = node->Data();
if ( d->sSection.Compare(section) )
{
currentSection = node->Data();
break;
}
}
if ( !currentSection )
{
currentSection = new struct SDummyEntry;
currentSection->sSection = section;
dummyList.push_back(currentSection);
}
// we have some more ?
line = line.remTokens(L";", 1, 2);
}
else
{
--insection;
// check the last entry for number of states
if ( currentSection->sSection.Compare(L"SDTYPE_GUN") )
{
int states = line.token(L";", 3).toInt();
int parts = line.token(L";", 4 + (states * 2)).toInt();
Utils::WString data = line.tokens(L";", 1, 4 + (states * 2) + (parts * 2)) + L";";
currentSection->lEntries.pushBack(data);
// remove done
line = line.remTokens(L";", 1, 4 + (states * 2) + (parts * 2));
}
else
{
int states = line.token(L";", 3).toInt();
Utils::WString data = line.tokens(L";", 1, 3 + (states * 2)) + L";";
currentSection->lEntries.pushBack(data);
// remove done
line = line.remTokens(L";", 1, 3 + (states * 2));
}
}
}
}
}
File.remove();
}
// add the new entries for the ships
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyDummies() )
continue;
// add each dummy to list
for ( CListNode<SDummy> *dNode = s->pPackage->GetDummies()->Front(); dNode; dNode = dNode->next() )
{
SDummy *dummy = dNode->Data();
SDummyEntry *found = NULL;
for ( CListNode<SDummyEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
{
if ( eNode->Data()->sSection.Compare(dummy->sSection) )
{
found = eNode->Data();
break;
}
}
if ( !found )
{
found = new SDummyEntry;
found->sSection = dummy->sSection;
dummyList.push_back(found);
}
// check if its already on the list
else
{
bool f = false;
for(auto itr = found->lEntries.begin(); itr != found->lEntries.end(); itr++)
{
if ((*itr)->str.token(L";", 1).Compare(dummy->sData.token(L";", 1)))
{
f = true;
break;
}
}
if ( f )
continue;
}
found->lEntries.pushBack(dummy->sData);
}
}
// finally, write the file
std::vector<Utils::WString> lines;
lines.push_back(L"// Dummies file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
for ( SDummyEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
{
lines.push_back(L"");
lines.push_back(L"// Section: " + dummy->sSection + L" Entries: " + Utils::WString::Number((long)dummy->lEntries.size()));
lines.push_back(dummy->sSection + L";" + Utils::WString::Number(dummy->lEntries.size()) + L";");
for(auto itr = dummy->lEntries.begin(); itr != dummy->lEntries.end(); itr++)
{
Utils::WString strLine = (*itr)->str;
strLine.removeChar(9);
strLine.removeChar('\r');
strLine.removeEndSpace();
strLine.removeFirstSpace();
strLine = strLine.findReplace(L"<::PiPe::>", L"|");
if ( strLine.right(1) != L";" )
strLine += L";";
lines.push_back(strLine);
}
}
lines.push_back(L"");
// write the file to disk
CFileIO WriteFile(m_sTempDir + L"/dummies.txt");
if ( WriteFile.writeFile(lines) )
{
this->packFile(&WriteFile, L"types\\dummies.pck");
WriteFile.remove();
}
}
}
void CPackages::CreateCutData()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyCutData() )
continue;
found = true;
break;
}
if ( !found )
return;
std::vector<Utils::WString> cutList;
int e = extractGameFile(L"types/CutData.pck", m_sTempDir + L"/CutData.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? L"CutData.txt" : m_sTempDir + L"/CutData.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int entries = -1;
for ( int j = 0; j < (int)lines.size(); j++ )
{
Utils::WString line(lines.at(j));
line.removeChar(9);
line.removeChar('\r');
line.removeChar(' ');
if ( line.empty() || line[0] == '/' )
continue;
if ( entries == -1 )
entries = line.token(L";", 1).toInt();
else
{
if ( line.right(1) != L";" )
line += L";";
cutList.push_back(line);
if ( static_cast<int>(cutList.size()) == entries)
break;
}
}
}
File.remove();
}
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyCutData() )
continue;
// add each dummy to list
auto& list = s->pPackage->getCutData();
for(auto itr = list.begin(); itr != list.end(); itr++)
{
Utils::WString str = (*itr)->str;
str.removeChar(' ');
if ( str.right(1) != L";" )
str += L";";
if(std::find(cutList.begin(), cutList.end(), str) == cutList.end())
cutList.push_back(str);
}
}
cutList.insert(cutList.begin(), Utils::WString::Number(cutList.size()) + ";");
cutList.insert(cutList.begin(), L"/cut id;filename (leave blank to use id)");
cutList.insert(cutList.begin(), L"// Cut Data file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
// write the file to disk
CFileIO WriteFile(m_sTempDir + L"/CutData.txt");
if ( WriteFile.writeFile(cutList) )
{
this->packFile(&WriteFile, L"types\\CutData.pck");
WriteFile.remove();
}
}
void CPackages::CreateAnimations()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyAnimations() )
continue;
found = true;
break;
}
if ( !found )
return;
Utils::WStringList aniList;
int e = extractGameFile(L"types/Animations.pck", m_sTempDir + L"/Animations.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? L"Animations.txt" : m_sTempDir + L"/Animations.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
for ( int j = 0; j < (int)lines.size(); j++ )
{
Utils::WString line(lines.at(j));
aniList.pushBack(line);
}
}
File.remove();
}
}
Utils::WStringList parsedAniList;
CXspFile::ReadAnimations(aniList, parsedAniList, 0);
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyAnimations() )
continue;
// add each dummy to list
for(auto itr = s->pPackage->getAnimations().begin(); itr != s->pPackage->getAnimations().end(); itr++)
parsedAniList.pushBack((*itr)->str);
}
// format the list with added spaces
Utils::WStringList formatedAniList;
int lineCount = -1;
for(auto itr = parsedAniList.begin(); itr != parsedAniList.end(); itr++)
{
// format the comment to match the line number
lineCount++;
Utils::WString oldComment = (*itr)->str.tokens(L"//", 2);
Utils::WString comment = L"//" + Utils::WString::Number(lineCount);
if (!oldComment.empty())
{
comment += L" ";
oldComment.removeFirstSpace();
if ( oldComment.token(L" ", 1).isNumber() )
comment += oldComment.tokens(L" ", 2);
else
comment += oldComment;
}
Utils::WString line = (*itr)->str.token(L"//", 1);
// split into seperate lines
Utils::WString first = line.token(L";", 1);
if ( first.Compare(L"TAT_TAGSINGLESTEP") )
{
formatedAniList.pushBack(line.tokens(L";", 1, 5) + L";");
std::vector<Utils::WString> sLines;
if(line.tokens(L";", 6).tokenise(L";", sLines))
{
size_t max = sLines.size();
if ( sLines[max - 1].empty() )
--max; // remove the last ";"
for (size_t i = 0; i < max; i++ )
{
Utils::WString l = L"\t" + sLines[i] + L";";
if ( i == (max - 1) )
formatedAniList.pushBack(l + comment);
else
formatedAniList.pushBack(l);
}
}
}
else if ( (first.Compare(L"TAT_TAGONESHOT") || first.Compare(L"TAT_TAGLOOP")) && (line.contains(L"TATF_COORDS")) )
{
formatedAniList.pushBack(line.tokens(L";", 1, 5) + L";");
std::vector<Utils::WString> sLines;
if (line.tokens(L";", 6).tokenise(L";", sLines))
{
size_t max = sLines.size();
if ( sLines[max - 1].empty() )
--max; // remove the last ";"
Utils::WString prevLine;
for (size_t i = 0; i < max; i++ )
{
Utils::WString l = sLines[i] + L";";
if ( l.contains(L"TATF_COORDS") && !prevLine.empty() )
{
formatedAniList.pushBack(L"\t" + prevLine);
prevLine = L"";
}
prevLine += l;
}
if ( !prevLine.empty() )
formatedAniList.pushBack(L"\t" + prevLine + comment);
}
}
else
formatedAniList.pushBack(line + comment);
}
formatedAniList.pushFront(Utils::WString::Number(parsedAniList.size()) + L";");
formatedAniList.pushFront(L"// Animations, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
// write the file to disk
CFileIO WriteFile(m_sTempDir + L"/Animations.txt");
if ( WriteFile.writeFile(&formatedAniList) )
{
this->packFile(&WriteFile, L"types\\Animations.pck");
WriteFile.remove();
}
}
void CPackages::CreateBodies()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyBodies() )
continue;
found = true;
break;
}
if ( !found )
return;
// lets read our current bodies file
CLinkList<SBodies> bodiesList;
SBodies *currentSection = NULL;
int e = extractGameFile(L"types/Bodies.pck", m_sTempDir + L"/Bodies.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? L"Bodies.txt" : m_sTempDir + L"/Bodies.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int entries = 0;
for (size_t j = 0; j < lines.size(); j++ )
{
Utils::WString line(lines.at(j));
line.removeChar(' ');
line.removeChar(9);
if ( line.empty() || line[0] == '/' )
continue;
if ( entries <= 0 )
{
entries = line.token(L";", 2).toInt();
currentSection = new SBodies;
currentSection->sSection = line.token(L";", 1);
bodiesList.push_back(currentSection);
}
else if ( currentSection )
{
std::vector<Utils::WString> strs;
if(line.tokenise(L";", strs))
{
for (size_t i = 0; i < strs.size(); i++)
{
if ( strs[i].empty() )
continue;
if(!currentSection->lEntries.contains(strs[i] + L";"))
currentSection->lEntries.pushBack(strs[i] + L";");
--entries;
}
}
}
}
}
File.remove();
}
}
// lets now add any new entries
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->anyBodies() )
continue;
// add each dummy to list
for(auto itr = s->pPackage->getBodies().begin(); itr != s->pPackage->getBodies().end(); itr++)
{
Utils::WString section = (*itr)->str.token(L";", 1);
Utils::WString body = (*itr)->str.tokens(L";", 2).remove(' ');
if ( body.right(1) != L";" )
body += L";";
// find the section to add into
SBodies *foundSection = NULL;
for ( CListNode<SBodies> *checkBody = bodiesList.Front(); checkBody; checkBody = checkBody->next() )
{
if ( checkBody->Data()->sSection.Compare(section))
{
foundSection = checkBody->Data();
break;
}
}
if ( !foundSection )
{
foundSection = new SBodies;
foundSection->sSection = section;
bodiesList.push_back(foundSection);
}
if(!foundSection->lEntries.contains(body))
foundSection->lEntries.pushBack(body);
}
}
// now write the file
std::vector<Utils::WString> writeList;
// the header first
writeList.push_back(L"// Bodies file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
writeList.push_back(L"//body type;num bodies;");
writeList.push_back(L"//[body id/name]");
// now our sections
for ( SBodies *bSection = bodiesList.First(); bSection; bSection = bodiesList.Next() )
{
writeList.push_back(L"");
writeList.push_back(L"// Section: " + bSection->sSection);
writeList.push_back(bSection->sSection + L";" + Utils::WString::Number(bSection->lEntries.size()) + L";");
for(auto itr = bSection->lEntries.begin(); itr != bSection->lEntries.end(); itr++)
{
Utils::WString str = (*itr)->str;
str.removeChar(9);
str.removeChar(' ');
if ( str.right(1) != L";" )
str += L";";
writeList.push_back(str);
}
}
// write the file to disk
CFileIO WriteFile(m_sTempDir + L"/Bodies.txt");
if ( WriteFile.writeFile(writeList) )
{
this->packFile(&WriteFile, L"types\\Bodies.pck");
WriteFile.remove();
}
}
void CPackages::CreateCustomStarts()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
// find all spk files (only ones that can be custom starts
if ( node->Data()->GetType() != TYPE_SPK )
continue;
CSpkFile *p = (CSpkFile *)node->Data();
// only use custom starts
if ( !p->IsCustomStart() )
continue;
// get the name of the start to use
Utils::WString name = p->customStartName();
if ( name.empty() )
continue;
// find if maps file exists
Utils::WStringList createFiles;
createFiles.pushBack(name, L"maps/x3_universe");
createFiles.pushBack(name, L"types/Jobs");
createFiles.pushBack(name, L"types/JobWings");
for(auto itr = createFiles.begin(); itr != createFiles.end(); itr++)
{
Utils::WString dir = CFileIO((*itr)->data).dir();
FileType type = FileType::FILETYPE_EXTRA;
if ( dir.Compare(L"maps") )
type = FileType::FILETYPE_MAP;
if ( !p->findFile((*itr)->str + L".xml", type) && !p->findFile((*itr)->str + L".pck", type))
{
// create a maps files
int e = this->extractGameFile((*itr)->data + L".pck", m_sTempDir + L"/" + (*itr)->data + L".pck");
if ( e )
{
CFileIO File((e == -1) ? ((*itr)->data + L".pck") : (m_sTempDir + L"/" + (*itr)->data + L".pck"));
if ( File.exists() )
{
File.Rename(m_sCurrentDir + L"/" + dir + L"/" + (*itr)->str + L".pck");
this->addCreatedFile(dir + L"/" + (*itr)->str + L".pck");
}
}
}
}
}
}
void CPackages::addCreatedFile(const Utils::WString &sFile)
{
Utils::WString file = sFile.findRemove(m_sCurrentDir);
while ( file[0] == '/' )
file.erase(0, 1);
while ( file[0] == '\\' )
file.erase(0, 1);
if(!_lCreatedFiles.contains(file, true))
_lCreatedFiles.pushBack(file);
}
void CPackages::CreateComponants()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return;
CLinkList<SComponantEntry> dummyList;
// now extract the existing dummies
int e = extractGameFile(L"types/Components.pck", m_sTempDir + L"/Components.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? L"Components.txt" : m_sTempDir + L"/Components.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int insection = 0;
int insubsection = 0;
SComponantEntry *currentSection = NULL;
SComponantEntry2 *currentSubSection = NULL;
for ( int j = 0; j < (int)lines.size(); j++ )
{
Utils::WString line(lines.at(j));
if ( line[0] == '/' )
continue;
line.removeChar('\r');
line = line.removeFirstSpace();
line = line.removeEndSpace();
if ( line.empty() )
continue;
// read the section, first entry is section, second is size
while ( !line.empty() )
{
line = line.removeFirstSpace();
if ( line.empty() )
break;
if ( !insection && !insubsection )
{
Utils::WString section = line.token(L";", 1);
insection = line.token(L";", 2).toInt();
// search for the sections
currentSection = NULL;
for ( CListNode<SComponantEntry> *node = dummyList.Front(); node; node = node->next() )
{
SComponantEntry *d = node->Data();
if ( d->sSection.Compare(section) )
{
currentSection = node->Data();
break;
}
}
if ( !currentSection )
{
currentSection = new SComponantEntry;
currentSection->sSection = section;
dummyList.push_back(currentSection);
}
// we have some more ?
line = line.remTokens(L";", 1, 2);
}
else if ( !insubsection )
{
--insection;
Utils::WString section = line.token(L";", 1);
insubsection = line.token(L";", 2).toInt();
currentSubSection = new SComponantEntry2;
currentSubSection->sSection = section;
currentSection->lEntries.push_back(currentSubSection);
line = line.remTokens(L";", 1, 2);
}
else
{
--insubsection;
line = line.remove(' ');
if(!currentSubSection->lEntries.contains(line))
currentSubSection->lEntries.pushBack(line);
line = L"";
}
}
}
}
File.remove();
}
// add the new entries for the ships
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyComponents() )
continue;
// add each dummy to list
for ( CListNode<SComponent> *dNode = s->pPackage->GetComponents()->Front(); dNode; dNode = dNode->next() )
{
SComponent *dummy = dNode->Data();
SComponantEntry *found = NULL;
SComponantEntry2 *found2 = NULL;
for ( CListNode<SComponantEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
{
if ( eNode->Data()->sSection.Compare(dummy->sSection) )
{
found = eNode->Data();
break;
}
}
if ( !found )
{
found = new SComponantEntry;
found->sSection = dummy->sSection;
dummyList.push_back(found);
found2 = new SComponantEntry2;
found2->sSection = dummy->sSection2;
found->lEntries.push_back(found2);
}
// else check for the 2nd section
else
{
for ( CListNode<SComponantEntry2> *cNode = found->lEntries.Front(); cNode; cNode = cNode->next() )
{
if ( cNode->Data()->sSection.Compare(dummy->sSection2) )
{
found2 = cNode->Data();
break;
}
}
if ( !found2 )
{
found2 = new SComponantEntry2;
found2->sSection = dummy->sSection2;
found->lEntries.push_back(found2);
}
else
{
bool f = false;
for(auto itr = found2->lEntries.begin(); itr != found2->lEntries.end(); itr++)
{
if ( dummy->sData.remove(' ').Compare((*itr)->str) )
{
f = true;
break;
}
}
if ( f )
continue;
}
}
found2->lEntries.pushBack(dummy->sData.remove(' '));
}
}
// finally, write the file
std::vector<Utils::WString> lines;
lines.push_back(L"// Components file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
for ( SComponantEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
{
lines.push_back(L"");
lines.push_back(L"// Section: " + dummy->sSection + L" Entries: " + Utils::WString::Number((long)dummy->lEntries.size()));
lines.push_back(dummy->sSection + L";" + Utils::WString::Number(dummy->lEntries.size()) + L";");
for ( CListNode<SComponantEntry2> *comp = dummy->lEntries.Front(); comp; comp = comp->next() )
{
lines.push_back(comp->Data()->sSection + L";" + Utils::WString::Number((long)comp->Data()->lEntries.size()) + L";");
for(auto itr = comp->Data()->lEntries.begin(); itr != comp->Data()->lEntries.end(); itr++)
{
Utils::WString cStr = (*itr)->str;
cStr.removeEndSpace();
cStr.removeChar(9);
cStr.removeChar('\r');
if ( cStr.right(1) != L";" )
cStr += L";";
lines.push_back(cStr);
}
}
}
// write the file to disk
CFileIO WriteFile(m_sTempDir + L"/Components.txt");
if ( WriteFile.writeFile(lines) )
{
this->packFile(&WriteFile, L"types\\Components.pck");
WriteFile.remove();
}
}
}
bool CPackages::readWares(int iLang, CLinkList<SWareEntry> &list)
{
if ( iLang == 0 ) iLang = m_iLanguage;
Utils::WString empWares = this->empWaresForGame();
for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
if ( !node->Data()->IsEnabled() ) continue;
node->Data()->readWares(iLang, list, empWares);
}
return true;
}
bool CPackages::readCommands(int iLang, CLinkList<SCommandSlot> &list)
{
if ( iLang == 0 ) iLang = m_iLanguage;
for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
if ( !node->Data()->IsEnabled() ) continue;
node->Data()->readCommands(iLang, list);
}
return true;
}
bool CPackages::readWingCommands(int iLang, CLinkList<SCommandSlot> &list)
{
if ( iLang == 0 ) iLang = m_iLanguage;
for(CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next()) {
if ( !node->Data()->IsEnabled() ) continue;
node->Data()->readWingCommands(iLang, list);
}
return true;
}
int CPackages::_warePriceOverride(enum WareTypes type, int pos, const Utils::WString &id)
{
for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
if ( node->Data()->type == type ) {
if ( node->Data()->type == Ware_Custom && id.Compare(node->Data()->id) ) return node->Data()->relval;
else if ( node->Data()->type != Ware_Custom && node->Data()->pos == pos ) return node->Data()->relval;
}
}
return 0;
}
bool CPackages::_wareNotoOverride(enum WareTypes type, int pos, const Utils::WString &id, int *noto)
{
for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
if ( node->Data()->type == type && node->Data()->bNotority ) {
if ( node->Data()->type == Ware_Custom && !id.Compare(node->Data()->id) ) continue;
else if ( node->Data()->type != Ware_Custom && node->Data()->pos != pos ) continue;
(*noto) = node->Data()->notority;
return true;
}
}
return false;
}
void CPackages::_removeWareOverride(enum WareTypes type, int pos, const Utils::WString &id)
{
for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
if ( node->Data()->type == type ) {
if ( node->Data()->type == Ware_Custom && !id.Compare(node->Data()->id) ) continue;
else if ( node->Data()->type != Ware_Custom && node->Data()->pos != pos ) continue;
m_lWarePrices.remove(node);
break;
}
}
}
int CPackages::empOveridePrice(int id)
{
return _warePriceOverride(Ware_EMP, id, Utils::WString::Null());
}
bool CPackages::empOverideNoto(int id, int *noto)
{
return _wareNotoOverride(Ware_EMP, id, Utils::WString::Null(), noto);
}
int CPackages::builtInWareOveridePrice(int id)
{
return _warePriceOverride(Ware_BuiltIn, id, Utils::WString::Null());
}
bool CPackages::builtInWareOverideNoto(int id, int *noto)
{
return _wareNotoOverride(Ware_BuiltIn, id, Utils::WString::Null(), noto);
}
int CPackages::customWareOveridePrice(const Utils::WString &id)
{
return _warePriceOverride(Ware_Custom, 0, id);
}
bool CPackages::customWareOverideNoto(const Utils::WString &id, int *noto)
{
return _wareNotoOverride(Ware_Custom, 0, id, noto);
}
void CPackages::removeEmpOverride(int pos)
{
_removeWareOverride(Ware_EMP, pos, Utils::WString::Null());
}
void CPackages::removeBuiltinWareOverride(int pos)
{
_removeWareOverride(Ware_BuiltIn, pos, Utils::WString::Null());
}
void CPackages::removeCustomWareOverride(const Utils::WString &id)
{
_removeWareOverride(Ware_Custom, 0, id);
}
bool CPackages::readGlobals(Utils::WStringList &globals) const
{
int e = extractGameFile(L"types/Globals.pck", m_sTempDir);
if ( e )
{
CFileIO File((e == -1) ? L"Globals.txt" : m_sTempDir + L"/Globals.txt");
if ( File.exists() )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int entries = -1;
for(auto itr = lines.begin(); itr != lines.end(); itr++)
{
Utils::WString str = itr->remove('\r').remove(9);
str.removeFirstSpace();
if ( str.empty() )
continue;
if ( str[0] == L'/' )
continue;
// remove comments
if (str.contains(L"/") )
str = str.token(L"/", 1);
if ( entries == -1 )
entries = str.token(L";", 1).toInt();
else
globals.pushBack(str.token(L";", 1), str.token(L";", 2));
}
return true;
}
}
}
return false;
}
void CPackages::CreateGlobals()
{
if (_lGlobals.empty())
{
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 settings
for (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 settings
for (auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
{
if(!globals.changeData((*itr)->str, (*itr)->data))
globals.pushBack((*itr)->str, (*itr)->data);
}
// now write it
Utils::WStringList writeList;
for(auto itr = globals.begin(); itr != globals.end(); itr++)
writeList.pushBack((*itr)->str + L";" + (*itr)->data + L";");
// finally, write the file
writeList.pushFront(Utils::WString::Number(writeList.size()) + L"; /globals amount", L"");
writeList.pushFront(L"// Globals file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2), L"");
CFileIO WriteFile(m_sTempDir + L"/Globals.txt");
if ( WriteFile.writeFile(&writeList) )
{
this->packFile(&WriteFile, L"types/Globals.pck");
WriteFile.remove();
}
}
}
void CPackages::CreateTShips()
{
// no ships ?
if ( m_lGameShips.empty() )
return;
// get the cockpit list to match with ships turrets
Utils::WStringList Cockpits;
Utils::WStringList cockpitList;
if(_createCockpits(cockpitList))
{
for(auto itr = cockpitList.begin(); itr != cockpitList.end(); itr++)
{
Utils::WString id = (*itr)->str.token(L";", 19);
Cockpits.pushBack(id);
}
}
CLinkList<SGameShip> shipOverrides;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( node->Data()->iType != WARETYPE_ADDED || !node->Data()->pPackage )
continue;
if ( !node->Data()->pPackage->IsExistingShip() )
continue;
shipOverrides.push_back(node->Data());
}
// read the existing tships file
int e = extractGameFile(L"types/TShips.pck", m_sTempDir + L"/TShips.txt");
if ( e )
{
int fileType = 51;
std::vector<Utils::WString> tshipsList;
// if we have no buffer, lets create one
CFileIO File;
if ( File.open((e == -1) ? L"TShips.txt" : m_sTempDir + L"/TShips.txt") )
{
int shiptext = SHIPSTARTTEXT;
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int count = -1;
for ( int j = 0; j < (int)lines.size(); j++ )
{
Utils::WString line(lines.at(j));
if ( line[0] == '/' )
continue;
line.removeChar('\r');
line.removeChar(9);
line = line.removeFirstSpace();
line = line.removeEndSpace();
if ( line.empty() )
continue;
if ( count == -1 )
{
fileType = line.token(L";", 1).toInt();
count = line.token(L";", 2).toInt();
}
else
{
if ( line.right(1) != L";" )
line += L";";
// check for any ship overrides
bool added = false;
if ( !shipOverrides.empty() )
{
CShipData shipData;
if ( shipData.readShipData(line) )
{
for ( CListNode<SGameShip> *node = shipOverrides.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( !s->pPackage->shipID().Compare(shipData.sID))
continue;
s->iText = shiptext;
if ( !s->pPackage->GetOriginalDescription() )
shiptext += 2;
s->iPos = tshipsList.size();
added = true;
tshipsList.push_back(s->pPackage->formatShipData(Cockpits, &s->iText, m_iGame));
shipOverrides.remove(node);
break;
}
}
}
if ( !added )
tshipsList.push_back(line);
--count;
if ( count < 0 )
break;
}
}
}
File.remove();
// assign the ship buffer
if ( !m_iShipBuffer )
m_iShipBuffer = tshipsList.size() + 15;
// there seems to be too many additional entries, we have no choise but to change the buffer
else if ( static_cast<size_t>(m_iShipBuffer) <= tshipsList.size() )
m_iShipBuffer = tshipsList.size() + 15;
Utils::WString bufferStart;
if ( m_iGame == GAME_X3 )
bufferStart = L"0;0;0;0;0;2;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;2092;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
else
bufferStart = L"0;0;0;0;0;SG_SH_M5;499999;0;0;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 now
for ( int i = tshipsList.size(); i < m_iShipBuffer; i++ )
tshipsList.push_back(bufferStart + L"SHIP_BUFFER;");
// now lets add our tships line
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->pPackage && s->pPackage->IsExistingShip() )
continue;
s->iPos = tshipsList.size();
if ( s->iType == WARETYPE_ADDED && s->pPackage )
{
s->iText = shiptext;
if ( !s->pPackage->GetOriginalDescription() )
shiptext += 2;
tshipsList.push_back(s->pPackage->formatShipData(Cockpits, &s->iText, m_iGame));
}
else if ( s->iType == WARETYPE_DELETED )
tshipsList.push_back(bufferStart + L"SHIP_DELETED;");
else if ( s->iType == WARETYPE_DISABLED )
tshipsList.push_back(bufferStart + L"SHIP_DISABLED;");
else
tshipsList.push_back(bufferStart + L"SHIP_SPACER;");
}
// finally, write the file
tshipsList.insert(tshipsList.begin(), Utils::WString::Number(fileType) + L";" + Utils::WString::Number(tshipsList.size()) + L";");
tshipsList.insert(tshipsList.begin(), L"// TShips file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
CFileIO WriteFile(m_sTempDir + L"/TShips.txt");
if ( WriteFile.writeFile(tshipsList) )
{
this->packFile(&WriteFile, L"types/TShips.pck");
WriteFile.remove();
}
}
}
}
bool CPackages::packFile(const Utils::WString &filename) const
{
// compress the file
CFileIO File(filename);
size_t fileSize;
char *fileData = File.ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
Utils::WString ext = L"pck";
if ( File.isFileExtension(L"bob") )
ext = L"pbb";
else if ( File.isFileExtension(L"bod") )
ext = L"pbd";
CFileIO pckFile(File.changeFileExtension(ext));
if ( !CDirIO(pckFile.dir()).exists() )
CDirIO(pckFile.dir()).create();
pckFile.WriteData((char *)pckData, newFileSize);
return true;
}
}
return false;
}
bool CPackages::unPackFile(const Utils::WString &filename, bool checkxml) const
{
// compress the file
CFileIO File(filename);
size_t fileSize;
char *fileData = File.ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, false);
if ( !pckData )
pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
Utils::WString ext = L"txt";
if ( File.isFileExtension(L"pbb") )
ext = L"bob";
else if ( File.isFileExtension(L"pbd") )
ext = L"bod";
CFileIO pckFile(File.changeFileExtension(ext));
if ( !CDirIO(pckFile.dir()).exists() )
CDirIO(pckFile.dir()).create();
pckFile.WriteData((char *)pckData, newFileSize);
// check for xml and rename
if ( checkxml )
{
int readmaxlines = 20;
bool isxml = false;
do {
Utils::WString line = pckFile.readEndOfLine();
if ( line.contains(L"<language id=") )
{
isxml = true;
break;
}
else if ( line.contains(L"<page id=") )
{
isxml = true;
break;
}
else if ( line.contains(L"<?xml") || line.contains(L"<script>") )
{
isxml = true;
break;
}
--readmaxlines;
if ( readmaxlines <= 0 )
break;
} while (pckFile.isOpened());
if ( pckFile.isOpened() )
pckFile.close();
if ( isxml )
pckFile.Rename(pckFile.changeFileExtension(L"xml"));
}
return true;
}
}
return false;
}
bool CPackages::packFile(CFileIO* File, const Utils::WString &sFilename) const
{
Utils::WString filename = sFilename.findReplace(L"\\", L"/");
if ( m_iGame == GAME_X3 )
{
CCatFile catFile;
int error = catFile.open(m_sCurrentDir + L"/mods/PluginManager.cat", this->getAddonDir(), CATREAD_CATDECRYPT, true);
if ( error == CATERR_NONE || error == CATERR_CREATED )
{
// it it wrote ok, remove the old ones
if ( !catFile.appendFile(File->fullFilename(), filename, true, true) )
return false;
return true;
}
}
else
{
// compress the file
size_t fileSize;
char *fileData = CFileIO(File->fullFilename()).ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
// if ( !this->GetAddonDir().Empty() && CCatFile::IsAddonDir(filename) )
// filename = this->GetAddonDir() + "/" + filename;
CFileIO pckFile(m_sCurrentDir + L"/" + filename);
if ( !CDirIO(pckFile.dir()).exists() )
CDirIO(pckFile.dir()).create();
pckFile.WriteData((char *)pckData, newFileSize);
const_cast<CPackages *>(this)->addCreatedFile(pckFile.fullFilename());
return true;
}
}
}
return false;
}
size_t CPackages::_createCockpits(Utils::WStringList &list)
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->countPackages(TYPE_XSP, true) )
return NULL;
// now extract the existing cockpits
int fileType = 51;
int e = extractGameFile(L"types/TCockpits.pck", m_sTempDir + L"/TCockpits.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? L"TCockpits.txt" : m_sTempDir + L"/TCockpits.txt") )
{
std::vector<Utils::WString> lines;
if(File.readLines(lines))
{
int count = -1;
for(auto itr = lines.begin(); itr != lines.end(); itr++)
{
Utils::WString line(*itr);
line.removeChar('\r');
line.removeChar(9);
line = line.removeFirstSpace();
line = line.removeEndSpace();
if ( line.empty() )
continue;
if ( line[0] == '/' )
continue;
if ( count == -1 )
{
fileType = line.token(L";", 1).toInt();
count = line.token(L";", 2).toInt();
}
else
{
while ( !line.empty() )
{
Utils::WString data = line.tokens(L";", 1, 19);
list.pushBack(data + L";");
line = line.remTokens(L";", 1, 19);
--count;
if ( count < 1 )
break;
}
}
}
}
File.remove();
}
// now add the new ones
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
if ( !s->pPackage->AnyCockpits() )
continue;
for ( CListNode<SCockpit> *cn = s->pPackage->GetCockpits()->Front(); cn; cn = cn->next() )
{
bool foundEntry = false;
Utils::WString cockpitStr = cn->Data()->sCockpit;
// search for matching game entry
for ( CListNode<SWeaponMask> *wm = cn->Data()->lWeaponMask.Front(); wm; wm = wm->next() )
{
if ( wm->Data()->iGame == (m_iGame - 1) )
{
if ( wm->Data()->iMask != -1 )
cockpitStr = cockpitStr.replaceToken(L";", 9, Utils::WString::Number(wm->Data()->iMask));
foundEntry = true;
break;
}
}
bool found = false;
for(auto itr = list.begin(); itr != list.end(); itr++)
{
if ((*itr)->str.token(L";", 19).Compare(cn->Data()->sCockpit.token(L";", 19)))
{
// only replace existing entry if we have sepeperate weapon masks set
if ( foundEntry )
(*itr)->str = cockpitStr;
found = true;
break;
}
}
if ( !found )
list.pushBack(cockpitStr);
}
}
// finally, write the file
list.pushFront(Utils::WString::Number(fileType) + L";" + Utils::WString::Number(list.size()) + L";");
list.pushFront(L"// TCockpits file, created by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2));
CFileIO WriteFile(m_sTempDir + L"/TCockpits.txt");
if ( WriteFile.writeFile(&list) )
{
this->packFile(&WriteFile, L"types\\TCockpits.pck");
WriteFile.remove();
}
// remove those entrys
list.popFront();
list.popFront();
}
return list.size();
}
CBaseFile *CPackages::findScriptByAuthor(const Utils::WString &author, CBaseFile *prev)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( prev )
{
if ( p == prev )
prev = NULL;
continue;
}
if ( p->author().Compare(author) )
return p;
}
return NULL;
}
bool CPackages::extractAll(CBaseFile *baseFile, const Utils::WString &dir, int game, bool includedir, CProgressInfo *progress) const
{
if (!baseFile)
return false;
Utils::WStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.game(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
}
return baseFile->extractAll(dir, game, gameAddons, includedir, progress);
}
bool CPackages::generatePackagerScript(CBaseFile *baseFile, bool wildcard, Utils::WStringList *list, int game, bool datafile) const
{
if (!baseFile)
return false;
Utils::WStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.game(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
}
return baseFile->GeneratePackagerScript(wildcard, list, game, gameAddons, datafile);
}
CBaseFile *CPackages::loadPackagerScript(const Utils::WString &filename, int compression, Utils::WString (*askFunc)(const Utils::WString &), Utils::WStringList *malformedLines, Utils::WStringList *unknownCommands, Utils::WStringList *variables, CProgressInfo *progress)
{
// check the file exists
if ( !CFileIO::Exists(filename) )
return NULL;
// read all the lines
CFileIO File(filename);
if ( !File.startRead() )
return NULL;
Utils::WStringList fileData;
int iLine = 0;
CBaseFile *package = NULL;
while(!File.atEnd()) {
// read the next line in the file
Utils::WString line = File.readEndOfLine();
// filter out any characters we dont really want
line.removeChar(L"\t\r");
line.removeFirstSpace();
if ( line.empty() ) continue;
// check for any comments (so we can ignore them)
if ( line.left(2).Compare(L"//") || line[0] == L'#' ) continue;
++iLine;
// all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
if ( !line.contains(L':') )
{
// there are some exeptions, and these are one word entrys only
line.removeEndSpace();
if ( line.contains(L" ") )
{
if ( malformedLines )
malformedLines->pushBack(line, Utils::WString::Number(iLine));
continue;
}
}
// check for the type line
if ( !package && line.token(L":", 1).Compare(L"FileType") )
{
Utils::WString sFileType = line.tokens(L":", 2).removeFirstSpace();
if ( sFileType.Compare(L"Ship") )
package = new CXspFile();
else if ( sFileType.Compare(L"Script") )
package = new CSpkFile();
else if ( sFileType.Compare(L"Base") )
package = new CBaseFile();
continue;
}
fileData.pushBack(line, Utils::WString::Number(iLine));
}
// assume its a script if no type is set (all old versions are scripts)
if ( !package )
package = new CSpkFile();
if ( compression != -1 )
package->SetDataCompression(compression);
Utils::WString ftpaddr;
Utils::WString ftpuser;
Utils::WString ftppass;
Utils::WString ftpdir;
Utils::WString sMainGame;
Utils::WStringList otherGames;
Utils::WStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.game(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
}
// now lets read the rest of the day
Utils::WStringList listVaribles;
for (Utils::WStringNode *line = fileData.first(); line; line = fileData.next())
{
Utils::WString cmd = line->str.token(L":", 1);
Utils::WString rest = line->str.tokens(L":", 2).removeFirstSpace();
if (cmd.Compare(L"Varible") || cmd.Compare(L"Variable"))
{
Utils::WString s1 = rest.token(L" ", 1);
Utils::WString s2 = rest.tokens(L" ", 2);
if(!listVaribles.changeData(s1, s2))
listVaribles.pushBack(s1, s2);
}
else
{
// replace variables
if ( rest.contains(L"$") )
{
for (Utils::WStringNode *strVar = listVaribles.first(); strVar; strVar = listVaribles.next())
{
if ( rest.contains(strVar->str) )
rest = rest.findReplace(strVar->str, strVar->data);
}
if ( variables )
{
for (Utils::WStringNode *strVar = variables->first(); strVar; strVar = variables->next())
{
if ( rest.contains(strVar->str) )
rest = rest.findReplace(strVar->str, strVar->data);
}
}
}
//check for the built in varibles
if ( rest.contains(L"$ASK") )
{
Utils::WString replace = L"$ASK";
Utils::WString result;
if ( askFunc )
result = askFunc(cmd);
if ( rest.contains(L"$ASK(") )
{
replace = rest.tokens(L"$ASK(", 2).token(L")", 1);
if ( result.empty() )
result = replace;
replace = L"$ASK(" + replace + L")";
}
if ( !result.empty() )
rest = rest.findReplace(replace, result);
}
// todays date
if ( rest.contains(L"$DATE") )
{
time_t now;
time(&now);
struct tm *timeinfo = localtime(&now);
Utils::WString result = Utils::WString::Number(timeinfo->tm_mday) + L"." + Utils::WString::Number(timeinfo->tm_mon + 1) + L"." + Utils::WString::Number(timeinfo->tm_year + 1900);
if ( !result.empty() )
rest = rest.findReplace(L"$DATE", result);
}
// mydocuments
if ( rest.contains(L"$MYDOCUMENTS") )
{
if ( !m_sMyDoc.empty() )
rest = rest.findReplace(L"$MYDOCUMENTS", m_sMyDoc);
}
// current path
if ( rest.contains(L"$PATH") )
{
Utils::WString currentDir = CFileIO(filename).dir();
if ( !currentDir.empty() )
rest = rest.findReplace(L"$PATH", currentDir);
}
// now parse the rest of the values
if ( cmd.Compare(L"FtpUpload") )
ftpaddr = rest.token(L" ", 1) + L":" + rest.token(L" ", 2);
else if ( cmd.Compare(L"FtpUser") )
ftpuser = rest;
else if ( cmd.Compare(L"FtpPass") )
ftppass = rest;
else if ( cmd.Compare(L"FtpDir") )
ftpdir = rest;
else if ( cmd.Compare(L"MultiGames") ) {
sMainGame = rest.token(L" ", 1);
otherGames.tokenise(rest.tokens(L" ", 2), L" ");
}
else if ( !package->loadPackageData(cmd, rest, sMainGame, otherGames, gameAddons, progress))
{
if ( unknownCommands )
unknownCommands->pushBack(cmd, rest);
}
}
}
if (package->filename().empty())
{
progress->UpdateDisplay(L"Updating package data...");
package->loadPackageData(L"AutoSave", L"$AUTOSAVE", sMainGame, otherGames, gameAddons, progress);
}
if (package->autoExtraction())
{
progress->UpdateDisplay(L"Extracting data...");
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())
{
progress->UpdateDisplay(L"Exporting to archive...");
for (auto itr = package->autoExporter()->begin(); itr != package->autoExporter()->end(); itr++)
package->saveToArchive(itr->second, itr->first, &m_gameExe);
}
if ( !ftpaddr.empty() )
{
if ( !ftpuser.empty() )
{
if ( !ftppass.empty() )
ftpaddr = ftpuser + L":" + ftppass + L"@" + ftpaddr;
else
ftpaddr = ftpuser + L"@" + ftpaddr;
}
if ( !ftpdir.empty() )
ftpaddr += ftpdir;
package->setFtpAddr(ftpaddr);
}
return package;
}
Utils::WString CPackages::getLanguageName() const
{
return CPackages::ConvertLanguage(m_iLanguage);
}
size_t CPackages::updateFoundPackages(const Utils::WString& dir)
{
m_lFoundPackages.MemoryClear();
if (!m_sCurrentDir.empty())
return findAllPackages(m_lFoundPackages, dir);
return 0;
}
size_t CPackages::addFoundPackages(const Utils::WString& dir)
{
return findPackageDirectories(m_lFoundPackages, dir);
}
int CPackages::findAllPackages(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
int count = 0;
if (!dir.empty())
{
count += findPackageDirectories(packages, dir + L"/Addons");
count += findPackageDirectories(packages, dir + L"/Downloads");
}
count += findPackageDirectories(packages, L"./Addons");
count += findPackageDirectories(packages, L"./Downloads");
count += findPackageDirectories(packages, m_sMyDoc + L"/Egosoft/PluginManager/Addons");
count += findPackageDirectories(packages, m_sMyDoc + L"/Egosoft/PluginManager/Downloads");
if (_pCurrentDir)
{
count += findPackageDirectories(packages, _pCurrentDir->dir + L"/Addons");
count += findPackageDirectories(packages, _pCurrentDir->dir + L"/Downloads");
count += findPackageDirectories(packages, _pCurrentDir->dir + L"/ExtraContent");
}
return count;
}
int CPackages::findPackageDirectories(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
CDirIO Dir(dir);
int count = 0;
Utils::WStringList files;
if (Dir.dirList(files))
{
for (auto itr = files.begin(); itr != files.end(); itr++)
{
Utils::WString d = Dir.file((*itr)->str);
if (CDirIO(d).isDir())
count += findPackageDirectories(packages, d);
}
}
count += findPackageFiles(packages, dir);
return count;
}
int CPackages::findPackageFiles(CLinkList<CBaseFile> &packages, const Utils::WString &dir)
{
CDirIO Dir(dir);
int count = 0;
for (int type = 0; type < 2; type++)
{
Utils::WStringList files;
if (type == 0)
Dir.dirList(files, Utils::WString::Null(), L"*.spk");
else if(type == 1)
Dir.dirList(files, Utils::WString::Null(), L"*.xsp");
else
break;
for(auto itr = files.begin(); itr != files.end(); itr++)
{
Utils::WString f = Dir.file((*itr)->str);
int error = 0;
CBaseFile *p = this->openPackage(f, &error, 0, SPKREAD_NODATA, READFLAG_NOUNCOMPRESS);
if (!p)
continue;
if (p->IsMod() || this->findSpkPackage(p->name(), p->author()))
{
delete p;
continue;
}
// check its for the correct game
if (!p->CheckGameCompatability(this->GetGame()))
{
delete p;
continue;
}
// check if its already on the list
bool found = false;
for (CBaseFile *checkp = packages.First(); checkp; checkp = packages.Next())
{
if (p->name().Compare(checkp->name()) && p->author().Compare(checkp->author()))
{
found = true;
break;
}
}
if (found)
{
delete p;
continue;
}
if (p->icon())
{
bool addedIcon = false;
p->ReadIconFileToMemory();
p->icon()->setFilename(this->tempDirectory().findReplace(L"\\", L"/") + L"/" + p->author() + L"_" + p->name() + L"." + p->iconExt());
p->icon()->setFullDir(this->tempDirectory());
if (p->icon()->UncompressData())
{
if (p->icon()->writeFilePointer())
addedIcon = true;
}
if (!addedIcon)
p->setIcon(NULL, L"");
}
// get an advert to display
if (p->GetFirstFile(FILETYPE_ADVERT))
{
bool done = false;
C_File *f = p->GetFirstFile(FILETYPE_ADVERT);
if (p->ReadFileToMemory(f))
{
f->setFullDir(this->tempDirectory());
if (f->UncompressData())
{
if (f->writeFilePointer())
done = true;
}
}
if (!done)
f->DeleteData();
}
packages.push_back(p);
++count;
}
}
return count;
}
Utils::WString CPackages::ConvertLanguage(int lang)
{
switch ( lang )
{
case 44:
return L"English";
case 49:
return L"German";
case 7:
return L"Russian";
case 33:
return L"French";
case 30:
return L"Greek";
case 31:
return L"Dutch";
case 32:
return L"Belgian";
case 34:
return L"Spanish";
case 36:
return L"Hungarian";
case 39:
return L"Italian";
case 40:
return L"Romanian";
case 41:
return L"Swiss";
case 42:
return L"Czech";
case 43:
return L"Austrian";
case 45:
return L"Danish";
case 46:
return L"Swedish";
case 47:
return L"Norweigen";
case 48:
return L"Polish";
}
return Utils::WString::Number(lang);
}
bool CPackages::checkAccessRights(const Utils::WString &aDir) const
{
Utils::WString dir = aDir;
if (dir.empty())
dir = m_sCurrentDir;
// write a file, then read the contents
CFileIO File(dir + L"/accessrightscheck.dat");
// check if file exists and remove it
if ( File.exists() )
{
// if we cant remove it, we dont have enough rights
if ( !File.remove() )
return false;
// if its still there, we dont have enough rights
if ( File.exists() )
return false;
}
// now create the file
if ( !File.writeString("testing access rights") )
return false;
// now check it exists
if ( !File.exists() )
return false;
// now read the file for the correct contents
std::vector<Utils::WString> lines;
if (!File.readLines(lines))
return false;
// check that one of the lines is correct
for(auto itr = lines.begin(); itr != lines.end(); itr++)
{
if ( *itr == L"testing access rights" )
return true;
}
return false;
}
size_t CPackages::loadShipData(const Utils::WString &file, Utils::WStringList &list) const
{
CFileIO File;
bool deleteFile = false;
// load from cat file
if (CFileIO(file).isFileExtension(L"cat"))
{
CCatFile cat;
if (cat.open(file, this->getAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE)
return false;
if (!cat.extractFile(L"types\\TShips.pck", m_sTempDir + L"/tships.txt"))
return false;
File.open(m_sTempDir + L"/tships.txt");
deleteFile = true;
}
// otherwise its a normal file
else if (CFileIO(file).isFileExtension(L"pck"))
{
C_File f(file);
if (!f.ReadFromFile())
return false;
f.UnPCKFile();
f.setFilename(m_sTempDir + L"/tships.txt");
if (!f.writeFilePointer())
return false;
File.open(m_sTempDir + L"/tships.txt");
deleteFile = true;
}
else
File.open(file);
if (!File.exists())
return false;
bool ret = false;
std::vector<Utils::WString> lines;
if (File.readLines(lines))
{
bool readFirst = false;
for (auto itr = lines.begin(); itr != lines.end(); itr++)
{
if (itr->empty())
continue;
Utils::WString str = itr->remove('\r').remove(9);
str = str.removeFirstSpace();
if (str.empty())
continue;
if (str[0] == '/' || str[0] == '#')
continue;
if (!readFirst)
readFirst = true;
else
{
Utils::WString t = str.tokens(L";", -2);
while (t.right(1) == L";")
t.truncate((int)t.length() - 1);
list.pushBack(t, str);
}
}
ret = true;
}
if (deleteFile)
File.remove();
return ret;
}
Utils::WString CPackages::readShipData(const Utils::WString &file, const Utils::WString &id) const
{
Utils::WStringList list;
if(!this->loadShipData(file, list))
return Utils::WString::Null();
CShipData data;
for(auto itr = list.begin(); itr != list.end(); itr++)
{
if ((*itr)->str.Compare(id))
return (*itr)->data;
}
return Utils::WString::Null();
}
bool CPackages::readTextPage(const Utils::WString &file, Utils::WStringList &list, bool search, int page) const
{
CFileIO File;
bool deleteFile = false;
// read all text files from mod
if ( CFileIO(file).isFileExtension(L"cat") )
{
bool done = false;
CCatFile cat;
if ( cat.open(file, this->getAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE)
return false;
// extract 1 at a time
for (unsigned int i = 0; i < cat.GetNumFiles(); i++ )
{
SInCatFile *f = cat.GetFile(i);
Utils::WString sF = f->sFile;
// is a text file
sF = sF.findReplace(L"\\", L"/");
if ( !sF.token(L"/", 1).Compare(L"t") )
continue;
Utils::WString baseFile = CFileIO(sF).baseName();
// check language
int lang = 0;
if ( baseFile.findPos(L"-L") != -1 ) // new language file
lang = baseFile.right(3).toInt();
else
{
baseFile.truncate((int)baseFile.length() - 4);
lang = baseFile.toInt();
}
if ( lang != m_iLanguage )
continue;
// now extract and parse
if ( cat.extractFile(f->sFile, m_sTempDir + L"/" + CFileIO(f->sFile).baseName() + L".xml"))
{
if ( this->readTextPage(m_sTempDir + L"/" + CFileIO(f->sFile).baseName() + L".xml", list, search, page))
done = true;
}
}
return done;
}
// otherwise its a normal file
else if ( CFileIO(file).isFileExtension(L"pck") )
{
C_File f(file);
if ( !f.ReadFromFile() )
return false;
f.UnPCKFile();
f.setFilename(m_sTempDir + L"/textfile.xml");
if ( !f.writeFilePointer() )
return false;
File.open(m_sTempDir + L"/textfile.xml");
deleteFile = true;
}
else
File.open(file);
if ( !File.exists() )
return false;
// open and read file
std::vector<Utils::WString> lines;
if(!File.readLines(lines))
return false;
bool inPage = false;
for(auto itr = lines.begin(); itr != lines.end(); itr++)
{
// search for page
if ( !inPage )
{
if (itr->findPos(L"<page") > -1 )
{
// find the page id
int pos = itr->findPos(L"\"");
if ( pos > -1 )
{
int endpos = itr->findPos(L"\"", pos + 1);
if ( endpos > -1 )
{
Utils::WString p = itr->mid(pos + 2, endpos - pos - 1);
if ( p.length() > 4 )
p = p.right(4);
int checkPage = p.toInt();
if ( checkPage == page )
inPage = true;
}
}
}
}
// add each id
else
{
if (itr->findPos(L"</page") > -1 )
break;
if (itr->findPos(L"<t id") > -1 )
{
int pos = itr->findPos(L"\"");
if ( pos > -1 )
{
int endpos = itr->findPos(L"\"", pos + 1);
if ( endpos > -1 )
{
int id = itr->mid(pos + 2, endpos - pos - 1).toInt();
pos = itr->findPos(L">", endpos);
if ( pos > -1 )
{
++pos;
endpos = itr->findPos(L"</", pos);
if ( endpos > -1 )
{
Utils::WString text = itr->mid(pos + 1, endpos - pos);
while ( text.findPos(L'(') != -1 && text.findPos(L')') != -1 )
{
int s = text.findPos(L'(');
text = text.erase(s, text.findPos(L')') - s + 1);
}
if(!search || !list.contains(Utils::WString::Number(id)))
list.pushBack(Utils::WString::Number(id), text);
}
}
}
}
}
}
}
return true;
}
FileType CPackages::adjustFileType(const Utils::WString &file, FileType filetype) const
{
CFileIO File(file);
Utils::WString dir = File.GetDirIO().topDir();
Utils::WString basename = File.baseName();
Utils::WString ext = File.extension();
// mod files
if (ext.Compare(L"cat") || ext.Compare(L"dat"))
return FILETYPE_MOD;
// check for text files
if ( File.filename().contains(L"-L") && File.filename().left(4).isNumber() )
return FILETYPE_TEXT;
if ( File.baseName().Compare(L"conversations") )
return FILETYPE_TEXT;
if ( basename.length() <= 4 && basename.isNumber() && (File.isFileExtension(L"xml") || File.isFileExtension(L"pck")) )
return FILETYPE_TEXT;
// X2/X3 text file
if ( basename.length() >= 5 && basename.length() <= 8 && ((int)File.baseName()) )
return FILETYPE_TEXT;
if ( filetype == FILETYPE_TEXT ) // should no longer be anything text
return FILETYPE_SCRIPT;
if ( File.isFileExtension(L"wav") || File.isFileExtension(L"mp3") )
return FILETYPE_SOUND;
return filetype;
}
void CPackages::RemoveFailedFiles()
{
Utils::WStringList removed;
for (auto itr = _lNonRemovedFiles.begin(); itr != _lNonRemovedFiles.end(); itr++)
{
if (CFileIO::Remove((*itr)->str))
removed.pushBack((*itr)->str);
}
for (auto itr = removed.begin(); itr != removed.end(); itr++)
_lNonRemovedFiles.remove((*itr)->str);
}
CXspFile *CPackages::extractShip(const Utils::WString &sCatFile, const Utils::WString &sId, CProgressInfo *progress)
{
CVirtualFileSystem *pVfs = new CVirtualFileSystem();
if ( !pVfs->addMod(sCatFile) ) {
delete pVfs;
return NULL;
}
CXspFile *newShip = new CXspFile;
if ( !newShip->extractShip(pVfs, sId, progress) ) {
delete newShip;
newShip = NULL;
}
delete pVfs;
return newShip;
}
void CPackages::getMergedFiles(Utils::WStringList &list, CCatFile *cat1, CCatFile *cat2) const
{
// first add all files from the "primary" mod
for (auto itr = cat1->GetFiles()->cbegin(); itr != cat1->GetFiles()->cend(); itr++)
list.pushBack((*itr)->sFile.findReplace(L"\\", L"/"), L"1");
// now add the ones from the secondary
for (auto itr = cat2->GetFiles()->cbegin(); itr != cat2->GetFiles()->cend(); itr++)
{
Utils::WString sFile = (*itr)->sFile.findReplace(L"\\", L"/");
// if its found on the 2nd list, dont add, just adjust the type
if(!list.changeData(sFile, L"-1"))
list.pushBack(sFile, L"2");
}
}
bool CPackages::canWeMerge(const Utils::WString &file) const
{
return CModDiff::CanBeDiffed(file);
}
bool CPackages::needToMerge(const Utils::WString &file) const
{
Utils::WString firstDir = file.token(L"/", 1);
if ( firstDir.Compare(L"t") )
return true;
if ( firstDir.Compare(L"types") )
return true;
if ( firstDir.Compare(L"maps") )
return true;
return false;
}
bool CPackages::mergeMods(CCatFile *mod1, CCatFile *mod2, const Utils::WString &outFile, Utils::WStringList *cantMerge) const
{
CCatFile newCat;
if ( newCat.open(outFile, this->getAddonDir()) != CATERR_CREATED )
return false;
Utils::WStringList list;
this->getMergedFiles(list, mod1, mod2);
if (list.empty())
return false;
// add all the files to the new mod first
Utils::WStringList conflicts;
for(auto itr = list.begin(); itr != list.end(); itr++)
{
int status = (*itr)->data.toInt();
if ( status == 1 )
{
if ( !newCat.writeFromCat(mod1, (*itr)->str) )
{
if ( cantMerge )
cantMerge->pushBack((*itr)->str, L"1");
}
}
else if ( status == 2 )
{
if ( !newCat.writeFromCat(mod2, (*itr)->str) )
{
if ( cantMerge )
cantMerge->pushBack((*itr)->str, L"2");
}
}
else if ( status == -1 )
{
if ( this->needToMerge((*itr)->str) )
{
if ( this->canWeMerge((*itr)->str) )
conflicts.pushBack((*itr)->str);
else if ( cantMerge )
cantMerge->pushBack((*itr)->str, L"-1");
}
else
{
if ( !newCat.writeFromCat(mod1, (*itr)->str) )
{
if ( cantMerge )
cantMerge->pushBack((*itr)->str, L"1");
}
}
}
}
/*
Merging Files
* Text Files: Join all text entries into a single file (excluding page 17)
* Weapons: TBullets and TLaser (grab matching entrys from text files and adjust ids)
*/
// new merge the conflicting files
// first the text files
// Utils::WStringList *text = this->MergeTextFiles(conflicts, mod1, mod2);
// delete text;
// write the cat file when we're done
if ( !newCat.WriteCatFile() )
return false;
return true;
}
/**
* Gets the file list from a mod that might have compatability problems
*
* This includes all types and text files
*
* Returns true if it finds any files
*/
bool CPackages::getModCompatabilityList(C_File *file, Utils::WStringList *list) const
{
// not a valid file
if ( !file ) return false;
if ( file->GetFileType() != FILETYPE_MOD ) return false;
if ( !file->fileExt().Compare(L"cat") ) return false;
// we need to read the file list for the mod
CCatFile cat;
if ( cat.open(file->filePointer(), this->getAddonDir(), CATREAD_JUSTCONTENTS, false) == CATERR_NONE)
{
for (unsigned int i = 0; i < cat.GetNumFiles(); i++ )
{
SInCatFile *f = cat.GetFile(i);
Utils::WString filename = f->sFile;
filename = filename.findReplace(L"\\", L"/");
bool found = false;
//TODO: rework this
if ( filename.left(2).Compare(L"t/") || filename.left(6).Compare(L"types/") )
found = true;
else if (filename.left(8).Compare(L"addon/t/") || filename.left(12).Compare(L"addon/types/"))
found = true;
else if (filename.left(9).Compare(L"addon2/t/") || filename.left(13).Compare(L"addon2/types/"))
found = true;
if ( found ) {
if ( list )
list->pushBack(filename, Utils::WString::Number(f->lSize));
else
return true;
}
}
}
if ( list && !list->empty() )
return true;
return false;
}
/**
* Gets the files that are not compatable with each other
*
* Returns true if theres any files that are not compatable
*
* If list is specified, fills up with all files that were found
*/
bool CPackages::checkCompatabilityBetweenModFiles(C_File *from, C_File *to, Utils::WStringList *list) const
{
// not a valid file
if ( !from || !to ) return false;
if ( from->GetFileType() != FILETYPE_MOD ) return false;
if ( to->GetFileType() != FILETYPE_MOD ) return false;
if (!from->fileExt().Compare(L"cat")) return false;
if (!to->fileExt().Compare(L"cat")) return false;
// get file lists from each file
Utils::WStringList fromList;
if (getModCompatabilityList(from, &fromList))
{
Utils::WStringList toList;
if (getModCompatabilityList(to, &toList))
{
// both have files we need to check, compare them
for(auto itr = fromList.begin(); itr != fromList.end(); itr++)
{
Utils::WString fromFile = (*itr)->str;
fromFile = fromFile.findReplace(L"\\", L"/");
fromFile = fromFile.findReplace(L"//", L"/");
for (auto toItr = toList.begin(); toItr != toList.end(); toItr++)
{
Utils::WString toFile = (*toItr)->str;
toFile = toFile.findReplace(L"\\", L"/");
toFile = toFile.findReplace(L"//", L"/");
if ( fromFile.Compare(toFile) )
{
if ( list )
list->pushBack(from->filename() + L"::" + fromFile, to->filename() + L"::" + toFile);
else
return true;
}
}
}
}
}
if ( list && !list->empty() )
return true;
return false;
}
bool CPackages::checkCompatabilityBetweenMods(CBaseFile *from, CBaseFile *to, Utils::WStringList *list) const
{
if ( !from || !to ) return false;
if ( !from->IsEnabled() || !to->IsEnabled() ) return false;
if ( !from->AnyFileType(FILETYPE_MOD) ) return false;
if ( !to->AnyFileType(FILETYPE_MOD) ) return false;
if ( from == to ) return false; // cant have incompatabilities to itself
// check if one is a depencacy of the other
if (from->isPackageNeeded(to->name(), to->author())) return false;
if (to->isPackageNeeded(from->name(), from->author())) return false;
// check if one is directly connected
if (from->type() == BaseFileType::TYPE_SPK)
{
CSpkFile* fromSpk = dynamic_cast<CSpkFile*>(from);
if (fromSpk->isAnotherMod())
{
if (fromSpk->otherName().Compare(to->name()) && fromSpk->otherAuthor().Compare(to->author()))
return false;
}
}
if (to->type() == BaseFileType::TYPE_SPK)
{
CSpkFile* toSpk = dynamic_cast<CSpkFile*>(to);
if (toSpk->isAnotherMod())
{
if (toSpk->otherName().Compare(from->name()) && toSpk->otherAuthor().Compare(from->author()))
return false;
}
}
int count = 0;
for ( C_File *f = from->GetFirstFile(FILETYPE_MOD); f; f = from->GetNextFile(f) )
{
if ( !f->IsFakePatch() ) continue;
if ( f->fileExt().Compare(L"dat") ) continue;
for ( C_File *compareFile = to->GetFirstFile(FILETYPE_MOD); compareFile; compareFile = to->GetNextFile(compareFile) )
{
if ( compareFile == f ) continue; // same file we're checking against
if ( !compareFile->IsFakePatch() ) continue;
if ( compareFile->fileExt().Compare(L"dat") ) continue;
// now we have to files to compare
if (checkCompatabilityBetweenModFiles(f, compareFile, list))
++count;
}
}
if ( count )
return true;
return false;
}
int CPackages::checkCompatabilityAgainstPackages(CBaseFile *newFile, Utils::WStringList *list, CLinkList<CBaseFile> *packages) const
{
if ( !newFile->IsEnabled() ) return 0;
if ( !newFile->AnyFileType(FILETYPE_MOD) ) return 0;
// we need to extract all mod files
for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *f = fNode->Data();
if ( f->GetFileType() != FILETYPE_MOD ) continue;
if ( !f->IsFakePatch() ) continue;
if (!f->checkFileExt(L"cat")) continue;
if (newFile->extractFile(f, m_sTempDir) )
f->setFullDir(m_sTempDir);
}
// compare mod files against all installed packages
int count = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( !node->Data() ) continue;
CBaseFile *p = node->Data();
if ( !p->IsEnabled() ) continue;
if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
if ( this->isSamePackage(p, newFile) ) continue; // dont include self
if (checkCompatabilityBetweenMods(newFile, p, list))
{
++count;
if ( packages && !packages->FindData(p) )
packages->push_back(p);
}
}
for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *f = fNode->Data();
CFileIO::Remove(f->filePointer());
f->setFullDir(L"");
}
return count;
}
bool CPackages::isSamePackage(CBaseFile *p1, CBaseFile *p2) const
{
if ( !p1 || !p2 ) return false;
if ( p1 == p2 ) return true;
if ( p1->name().Compare(p2->name()) && p1->author().Compare(p2->author()) )
return true;
return false;
}
void CPackages::applyFakePatchOrder(const Utils::WStringList &list)
{
_lFakePatchOrder.clear();
for(auto itr = list.begin(); itr != list.end(); itr++)
_lFakePatchOrder.pushBack((*itr)->str, (*itr)->data);
}
SAvailablePackage *CPackages::CreateAvailablePackageData(CBaseFile *package)
{
if ( !package ) return NULL;
SAvailablePackage *p = new SAvailablePackage;
for ( CListNode<SGameCompat> *node = package->GetGameCompatabilityList()->Front(); node; node = node->next() ) {
SGameCompat *gc = new SGameCompat;
gc->iGame = node->Data()->iGame;
gc->iVersion = node->Data()->iVersion;
gc->sVersion = node->Data()->sVersion;
p->lGames.push_back(gc);
}
p->bSigned = package->IsSigned();
p->iChanging = package->gameChanging();
p->iEase = package->easeOfUse();
p->iPluginType = package->pluginType();
p->iRec = package->recommended();
p->iScriptType = -1;
if ( package->GetType() == TYPE_XSP )
p->iType = PACKAGETYPE_SHIP;
else if ( package->IsMod() )
p->iType = PACKAGETYPE_MOD;
else
{
p->iType = ((CSpkFile *)package)->GetPackageType();
p->iScriptType = ((CSpkFile *)package)->GetScriptType();
}
p->sAuthor = package->author();
p->sDesc = package->description().findReplace(L"\n", L"::newline::");
p->sName = package->name();
p->sUpdated = package->creationDate();
p->sVersion = package->version();
p->sFilename = CFileIO(package->filename()).filename();
return p;
}
Utils::WString CPackages::FormatAvailablePackageData(CBaseFile *package)
{
SAvailablePackage *p = CPackages::CreateAvailablePackageData(package);
Utils::WString ret = CPackages::FormatAvailablePackageData(p);
delete p;
return ret;
}
Utils::WString CPackages::FormatAvailablePackageData(SAvailablePackage *package)
{
Utils::WString ret = (long)package->iType;
Utils::WString gameCompat;
for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
if ( !gameCompat.empty() )
gameCompat += L"!";
gameCompat += Utils::WString::Number(node->Data()->iGame);
}
if ( gameCompat.empty() )
gameCompat = L"0";
ret = ret.addToken(L"::", gameCompat);
ret = ret.addToken(L"::", package->sName);
ret = ret.addToken(L"::", package->sAuthor);
ret = ret.addToken(L"::", package->sVersion);
ret = ret.addToken(L"::", package->sUpdated);
ret = ret.addToken(L"::", package->sFilename);
ret = ret.addToken(L"::", Utils::WString::Number(package->iEase));
ret = ret.addToken(L"::", Utils::WString::Number(package->iChanging));
ret = ret.addToken(L"::", Utils::WString::Number(package->iRec));
ret = ret.addToken(L"::", Utils::WString::Number(package->iPluginType));
ret = ret.addToken(L"::", Utils::WString::Number(package->iScriptType));
ret = ret.addToken(L"::", (package->bSigned) ? L"1" : L"0");
ret = ret.addToken(L"::", package->sDesc);
return ret;
}
void CPackages::parseAvailablePackage(const Utils::WString &str, const Utils::WString &webaddress)
{
// first check game
std::vector<Utils::WString> tok;
if (!str.tokenise(L"::", tok))
return;
// invalid number of entries?
if (tok.size() < 7 ) return;
SAvailablePackage *p = new SAvailablePackage;
p->iType = tok[0].toLong();
p->bSigned = false;
// theres multiple games, so we need to split it
if ( m_iGame ) {
Utils::WString sGame = tok[1];
if ( sGame.contains(L"!") ) {
for(int i = 1; i <= sGame.countToken(L"!"); i++) {
SGameCompat *gc = new SGameCompat;
gc->iVersion = 0;
gc->iGame = sGame.token(L"!", i).toLong();
p->lGames.push_back(gc);
}
}
else {
SGameCompat *gc = new SGameCompat;
gc->iVersion = 0;
gc->iGame = sGame.toLong();
p->lGames.push_back(gc);
}
}
p->sName = tok[2];
p->sAuthor = tok[3];
p->sVersion = tok[4];
p->sUpdated = tok[5];
p->sFilename = tok[6];
if ( !webaddress.empty() )
p->sFilename = webaddress + L"/" + p->sFilename;
p->iChanging = p->iEase = p->iPluginType = p->iRec = p->iScriptType = -1;
// check if we have the extra values
if (tok.size() >= 12 )
{
p->iEase = tok[7].toLong();
p->iChanging = tok[8].toLong();
p->iRec = tok[9].toLong();
p->iPluginType = tok[10].toLong();
p->iScriptType = tok[11].toLong();
if (tok.size()> 13 ) {
p->sDesc = tok[13];
p->bSigned = tok[12].toBool();
}
else
p->sDesc = tok[12];
}
else if (tok.size() > 7 )
p->sDesc = tok[8];
if ( !p->sDesc.empty() )
p->sDesc = p->sDesc.findReplace(L"::newline::", L"\\n");
addAvailablePackage(p);
}
const SAvailablePackage* CPackages::findAvailablePackage(const Utils::WString& filename) const
{
for (CListNode<SAvailablePackage>* node = m_lAvailablePackages.Front(); node; node = node->next())
{
if (node->Data()->sFilename.Compare(filename))
return node->Data();
}
return NULL;
}
const SAvailablePackage* CPackages::findAvailablePackage(const Utils::WString& name, const Utils::WString& author) const
{
for (CListNode<SAvailablePackage>* node = m_lAvailablePackages.Front(); node; node = node->next())
{
if (node->Data()->sName.Compare(name) && node->Data()->sAuthor.Compare(author))
return node->Data();
}
return NULL;
}
CBaseFile* CPackages::findFoundPackage(const Utils::WString& name, const Utils::WString& author) const
{
for (CListNode<CBaseFile>* node = m_lFoundPackages.Front(); node; node = node->next())
{
if (node->Data()->name().Compare(name) && node->Data()->author().Compare(author))
return node->Data();
}
return NULL;
}
bool CPackages::addAvailablePackage(SAvailablePackage *package)
{
if ( !package->lGames.empty() ) {
bool found = false;
for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
if ( !node->Data()->iGame || node->Data()->iGame == m_iGame ) {
found = true;
break;
}
}
if ( !found )
return false;
}
// check if a matching package is already found
const CBaseFile* bf = findFoundPackage(package->sName, package->sAuthor);
if (bf)
{
if (bf->version().compareVersion(package->sVersion) <= 0)
return true;
}
const SAvailablePackage *p = findAvailablePackage(package->sFilename);
if(p)
{
if (p->sVersion.compareVersion(package->sVersion) <= 0)
return true;
m_lAvailablePackages.remove(p);
}
p = findAvailablePackage(package->sName, package->sAuthor);
if (p)
{
if (p->sVersion.compareVersion(package->sVersion) <= 0)
return true;
m_lAvailablePackages.remove(p);
}
m_lAvailablePackages.push_back(package);
saveAvailablePackages();
return true;
}
void CPackages::saveAvailablePackages()
{
std::vector<Utils::WString> lines;
lines.push_back(L"Version;1;" + Utils::WString::Number(m_lAvailablePackages.size()));
for (auto itr = m_lAvailablePackages.First(); itr; itr = m_lAvailablePackages.Next())
{
Utils::WString l = L"Package";
l += L";" + itr->sName.findReplace(L";", L":&COL&:");
l += L";" + itr->sAuthor.findReplace(L";", L":&COL&:");
l += L";" + itr->sVersion.findReplace(L";", L":&COL&:");
l += L";" + itr->sFilename;
l += L";" + itr->sDesc.findReplace(L";", L":&COL&:");
l += L";" + itr->sUpdated;
l += L";" + Utils::WString::Number(itr->lGames.size());
for (auto g = itr->lGames.First(); g; g = itr->lGames.Next())
l += L";" + Utils::WString::Number(g->iGame) + L";" + Utils::WString::Number(g->iVersion) + L";" + g->sVersion;
l += L";" + Utils::WString::Number(itr->iType);
l += L";" + Utils::WString::Number(itr->iPluginType);
l += L";" + Utils::WString::Number(itr->iScriptType);
l += L";" + Utils::WString::Number(itr->iChanging);
l += L";" + Utils::WString::Number(itr->iEase);
l += L";" + Utils::WString::Number(itr->iRec);
l += itr->bSigned ? L";1" : L";0";
lines.push_back(l);
}
// write out the file
CDirIO dir(m_sCurrentDir + L"/PluginManager");
if (!dir.exists())
dir.create();
CFileIO file(dir.file(L"packagecache.new"));
if (file.writeFile(lines))
{
if (CFileIO::Exists(dir.file(L"packagecache.new")))
{
if (CFileIO::Exists(dir.file(L"packagecache.dat")))
CFileIO::Remove(dir.file(L"packagecache.dat"));
file.Rename(dir.file(L"packagecache.dat"));
}
}
}
void CPackages::readAvailablePackages()
{
m_lAvailablePackages.MemoryClear();
CDirIO dir(m_sCurrentDir + L"/PluginManager");
CFileIO file(dir.file(L"packagecache.dat"));
if (file.exists())
{
size_t version = 0;
size_t count = 0;
std::vector<Utils::WString> lines;
if (file.readLines(lines))
{
for (auto itr = lines.begin(); itr != lines.end(); itr++)
{
Utils::WString cmd = itr->token(L";", 1);
if (cmd == L"Version")
{
version = itr->token(L";", 2).toInt();
count = itr->token(L";", 3).toInt();
}
else if (cmd == L"Package")
{
int max = 0;
std::vector<Utils::WString> str;
itr->tokenise(L";", str);
SAvailablePackage* package = new SAvailablePackage;
int pos = 1;
package->sName = str[pos++].findReplace(L":&COL&:", L";");
package->sAuthor = str[pos++].findReplace(L":&COL&:", L";");
package->sVersion = str[pos++].findReplace(L":&COL&:", L";");
package->sFilename = str[pos++].findReplace(L":&COL&:", L";");
package->sDesc = str[pos++].findReplace(L":&COL&:", L";");
package->sUpdated = str[pos++].findReplace(L":&COL&:", L";");
size_t games = str[pos++].toInt();
for (size_t i = 0; i < games; i++)
{
SGameCompat* g = new SGameCompat;
g->iGame = str[pos++].toInt();
g->iVersion = str[pos++].toInt();
g->sVersion = str[pos++];
package->lGames.push_back(g);
}
package->iType = str[pos++].toInt();
package->iPluginType = str[pos++].toInt();
package->iScriptType = str[pos++].toInt();
package->iChanging = str[pos++].toInt();
package->iEase = str[pos++].toInt();
package->iRec = str[pos++].toInt();
package->bSigned = (str[pos] == L"1");
addAvailablePackage(package);
}
}
}
}
}
bool CPackages::AnyAvailablePackages(int type)
{
if ( type == -1 ) return !m_lAvailablePackages.empty();
for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
{
if ( node->Data()->iType == type )
return true;
}
return false;
}
int CPackages::findAllServers(Utils::WStringList *list) const
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if (!node->Data()->webAddress().empty())
{
if(!list->contains(node->Data()->webAddress()))
list->pushBack(node->Data()->webAddress());
}
if ( node->Data()->anyWebMirrors() )
{
auto &l = node->Data()->webMirrors();
for (auto itr = l.begin(); itr != l.end(); itr++)
{
if(!list->contains((*itr)->str))
list->pushBack((*itr)->str);
}
}
}
CFileIO File(L"data\\web");
if ( File.startRead() ) {
while(!File.atEnd()) {
Utils::WString line = File.readEndOfLine();
line.removeChar(L"\n\r\t ");
if (!line.empty())
{
if(!list->contains(line))
list->pushBack(line);
}
}
File.close();
}
if (!list->contains(L"http://xpluginmanager.co.uk/tcscripts"))
list->pushBack(L"http://xpluginmanager.co.uk/tcscripts");
if (!list->contains(L"http://xpluginmanager.co.uk/acscripts"))
list->pushBack(L"http://xpluginmanager.co.uk/apscripts");
if (!list->contains(L"http://xpluginmanager.co.uk/fcscripts"))
list->pushBack(L"http://xpluginmanager.co.uk/flscripts");
return list->size();
}
void CPackages::readArchiveData(const Utils::WString &filename, CBaseFile *archive) const
{
size_t size;
char *data = CFileIO(filename).ReadToData(&size);
if ( size && data )
readArchiveData(data, size, archive);
}
void CPackages::readArchiveData(const char *buf, size_t len, CBaseFile *archive) const
{
Utils::WStringList otherGames;
Utils::WStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.game(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::WString::Number(i + 1), exe->sAddon);
}
Utils::WString data(buf);
std::vector<Utils::WString> str;
if(data.tokenise(L"\n", str))
{
for(size_t i = 0; i < str.size(); i++ )
{
Utils::WString line = str[i];
if ( line.empty() )
continue;
// filter out any spaces, tabs in front
line.removeChar('\t');
line.removeChar('\r');
Utils::WString linenospace = line;
linenospace.removeFirstSpace();
if ( linenospace.empty() )
continue;
// check for any comments
if ( linenospace.left(2) == L"//" )
continue;
if ( linenospace[0] == L'#' )
continue;
// all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
if ( !line.contains(':'))
continue;
Utils::WString first = line.token(L":", 1);
Utils::WString rest = line.tokens(L":", 2).removeFirstSpace();
Utils::WString checkType = first;
bool shared = false;
if ( checkType.left(6).Compare(L"Shared") )
{
checkType = first.right(-6);
shared = true;
}
bool packed = false;
if (checkType.right(3).toStdWString().compare(L"PCK"))
{
checkType = checkType.left(-3);
packed = true;
}
// now check type name
int filetype = GetFileTypeFromString(checkType);
if (filetype == -1)
{
archive->loadPackageData(first, rest, Utils::WString::Null(), otherGames, gameAddons, NULL);
}
}
}
}
CBaseFile *CPackages::_archive_fromRar(const Utils::WString &filename, bool toInstall) const
{
// make sure we can open the zip file
CBaseFile *archive = NULL;
#ifdef _RAR
HANDLE hArcData;
int RHCode,PFCode;
wchar_t CmtBuf[16384];
struct RARHeaderDataEx HeaderData;
struct RAROpenArchiveDataEx OpenArchiveData;
// find the pluginmanager text to covnert to spkfile
if ( 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 file
else
archive = new CSpkFile(); // converting to a spk file
HeaderData.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 types
C_File *f = NULL;
if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
f = archive->addFile(CFileIO(fileName).filename(), L"", FILETYPE_SCRIPT);
if ( f ) {
f->readFromFile(File.fullFilename());
}
type = FILETYPE_UNINSTALL;
}
if ( type == -1 )
f = archive->addFile(CFileIO(fileName).filename(), CFileIO(fileName).dir(), FILETYPE_EXTRA);
else
f = archive->addFile(CFileIO(fileName).filename(), extradir, static_cast<FileType>(type));
f->readFromFile(File.fullFilename());
}
File.remove();
}
}
RARCloseArchive(hArcData);
if (outputDir.exists())
outputDir.removeDir(L".", true, true);
if ( error )
{
delete archive;
archive = NULL;
}
#endif
return 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 package
ZIPENTRY 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 package
ZIPENTRY 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 package
if ( toInstall )
archive = new CArchiveFile(); // just installing an archive file
else
archive = new CSpkFile(); // converting to a spk file
GetZipItem(hz, -1, &ze);
int numitems = ze.index;
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 directories
char *iBuf = new char[ze.unc_size];
UnzipItem(hz, zi, iBuf, ze.unc_size);
Utils::WString Name(ze.name);
// if its the data file, dont add it, but extract to get settings from
if ( Name.Compare(L"pluginmanager.txt") )
{
this->readArchiveData(iBuf, ze.unc_size, archive);
delete[] iBuf;
}
else
{
Utils::WString extradir;
int type = SPK::GetAutomaticFiletype(Name, &extradir, true);
C_File *f = NULL;
Utils::WString filename = CFileIO(Name).filename();
Utils::WString dir = CFileIO(Name).dir();
// check for special file types
if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
f = archive->addFile(filename, dir, FILETYPE_SCRIPT);
if ( f ) {
f->copyData((const unsigned char *)iBuf, ze.unc_size);
}
type = FILETYPE_UNINSTALL;
}
int game = 0;
// check for addons
if (!dir.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 game
if ( type == -1 )
f = archive->addFile(filename, dir, FILETYPE_EXTRA, game > 0 ? 1 << game : 0);
else
f = 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);
}
else
delete[] 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 file
CBaseFile *archive = NULL;
if ( CFileIO(filename).isFileExtension(L"rar") )
archive = this->_archive_fromRar(filename, toInstall);
else if ( CFileIO(filename).isFileExtension(L"zip") )
archive = this->_archive_fromZip(filename, toInstall);
else
archive = 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());
else
archive->setName(CFileIO(filename).baseName());
}
}
return archive;
}
Utils::WString CPackages::CreateFromPackagerScript(CPackages *packages, const Utils::WString &filename)
{
Utils::WString curDir = CFileIO(filename).dir();
Utils::WStringList variables;
variables.pushBack(L"$PATH", curDir);
CBaseFile *package = packages->loadPackagerScript(filename, NULL, NULL, NULL, &variables);
if ( !package )
return Utils::WString::Null();
Utils::WString saveto = package->filename();
saveto = saveto.findReplace(L"$DEFAULTDIR", curDir + L"/");
saveto = saveto.findReplace(L"$PATH", curDir);
saveto = saveto.findReplace(L"\\", L"/");
saveto = saveto.findReplace(L"//", L"/");
if ( !saveto.right(4).Compare(L".spk") && package->GetType() != TYPE_XSP )
saveto += L".spk";
else if ( !saveto.right(4).Compare(L".xsp") && package->GetType() == TYPE_XSP )
saveto += L".xsp";
// write script
if ( package->writeFile(saveto) )
{
if ( package->AutoGenerateUpdateFile() )
package->createUpdateFile(CFileIO(saveto).dir());
return saveto;
}
return Utils::WString::Null();
}
int CPackages::GeneratePackageUpdateData(const Utils::WString &dir, bool includeSingle)
{
Utils::WStringList filedata;
CPackages packages;
CDirIO Dir(dir);
for ( int i = 0; i < 2; i++ )
{
Utils::WString pattern;
if ( i == 0 ) pattern = L"*.spk";
else if ( i == 1 ) pattern = L".xsp";
else break;
Utils::WStringList files;
if(Dir.dirList(files, L"", pattern))
{
for(auto itr = files.begin(); itr != files.end(); itr++)
{
int error = 0;
CBaseFile *p = packages.openPackage(Dir.file((*itr)->str), &error, 0, SPKREAD_NODATA);
if ( !p )
continue;
if ( includeSingle )
p->createUpdateFile(dir);
filedata.pushBack(CPackages::FormatAvailablePackageData(p));
delete p;
}
}
}
if ( !filedata.empty() )
{
CFileIO File(dir + L"/xpackagedata.dat");
if ( File.writeFile(&filedata) )
return filedata.size();
}
return 0;
}
size_t CPackages::verifyInstalledFiles(Utils::WStringList *missingFiles, bool getPackages) const
{
int count = 0;
for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
bool exists = false;
if ( f->filePointer().contains(L"::") ) {
Utils::WString modFile = f->filePointer().token(L"::", 1);
Utils::WString file = f->filePointer().token(L"::", 2);
if ( CFileIO::Exists(modFile)) {
CCatFile catFile;
if ( catFile.open(modFile, L"", CATREAD_CATDECRYPT, false) == CATERR_NONE ) {
if ( catFile.findData(file) )
exists = true;
}
}
}
else {
exists = CFileIO::Exists(f->filePointer());
}
if ( !exists )
{
++count;
if ( missingFiles )
{
Utils::WString packages;
if ( getPackages )
{
for ( CListNode<CBaseFile> *p = m_lPackages.Front(); p; p = p->next() )
{
CBaseFile *package = p->Data();
if ( package->IsFileAdded(f) )
{
if ( !packages.empty() )
packages += L"\n";
packages += package->getFullPackageName(m_iLanguage);
}
}
}
Utils::WString filename = f->filePointer();
filename = filename.findRemove(m_sCurrentDir);
missingFiles->pushBack(filename, packages);
}
}
}
return count;
}