Rev 133 | Rev 152 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "spk.h"
#include "emp.h"
#include <time.h>
#include <vector>
#include "OriginalFiles.h"
#include "spkdef.h"
#ifdef _RAR
#include <unrar.h>
#endif
enum {READ_START, READ_GLOBAL, READ_SCRIPT, READ_SCRIPTFILE, READ_WARES};
enum { EXTRACT, TEST, PRINT, LIST };
typedef struct SDummyEntry {
CyString sSection;
CyStringList lEntries;
} SDummyEntry;
typedef struct SComponantEntry2 {
CyString sSection;
CyStringList lEntries;
} SComponantEntry2;
typedef struct SComponantEntry {
CyString sSection;
CLinkList<SComponantEntry2> lEntries;
} SComponantEntry;
Utils::String 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(CyString dir)
{
m_sCurrentDir = dir;
if (_pCurrentDir) delete _pCurrentDir;
_pCurrentDir = new GameDirectory();
_pCurrentDir->dir = dir.ToString();
_pCurrentDir->id = -1;
m_gameExe.GetDirectoryData(_pCurrentDir);
_pCurrentDir->langid = this->GetGameLanguage(_pCurrentDir->dir);
if (_pCurrentDir->langid > 0)
_pCurrentDir->langname = this->ConvertLanguage(_pCurrentDir->langid);
}
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::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::String::PadNumber(id, 4)))
++id;
Utils::String d = Utils::String::PadNumber(id, 4);
dir.Create(d);
_sSaveDir = d;
CDirIO destDir1(dir.dir(d));
destDir1.Create("Vanilla");
destDir1.cd("Vanilla");
CDirIO destDir2(dir.dir(d));
destDir2.Create("Modified");
destDir2.cd("Modified");
Utils::CStringList files;
if (dir.dirList(files, Utils::String::Null(), "*.sav"))
{
for (auto itr = files.begin(); itr != files.end(); ++itr)
{
Utils::String 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 = "";
for ( int i = 0; i < WAREBUFFERS; i++ )
{
m_iWareBuffer[i] = 0;
m_lGameWares[i].MemoryClear();
}
m_iShipBuffer = 0;
m_lGameShips.MemoryClear();
m_lAvailablePackages.MemoryClear();
m_lInstallList.MemoryClear();
m_lEnableList.MemoryClear();
m_lDisableList.MemoryClear();
m_lCreatedFiles.Clear();
m_lNonRemovedFiles.Clear();
m_lGlobals.Clear();
m_lFakePatchOrder.Clear();
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("cat") ) break;
}
if ( f )
m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), f->filePointer(), 0);
else
m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), 0);
}
else
m_pGameVFS.LoadFilesystem(m_sCurrentDir.ToString(), 0);
}
bool CPackages::isOldDir(const Utils::String &dir)
{
bool oldPlugin = false;
CFileIO datFile(dir + "/PluginManager/pluginmanager.dat");
if ( datFile.exists() )
{
std::vector<Utils::String> *readFile = datFile.readLines();
if ( readFile )
{
for ( int i = 0; i < (int)readFile->size(); i++ )
{
Utils::String line(readFile->at(i));
Utils::String cmd = line.token(":", 1);
if ( cmd.Compare("<script>") || cmd.Compare("</scripts>") )
break;
else if ( cmd.Compare("spkinstaller") || cmd.Compare("globalfiles") )
break;
else if ( cmd.Compare("pluginmanager") )
{
oldPlugin = true;
break;
}
}
delete readFile;
}
}
return oldPlugin;
}
bool CPackages::read(const Utils::String &dir, CProgressInfo *progress)
{
return Read(CyString(dir), progress);
}
bool CPackages::Read(CyString dir, CProgressInfo *progress)
{
m_sCurrentDir = dir;
m_sCurrentDir = m_sCurrentDir.FindReplace("\\", "/");
this->SetCurrentDir(dir);
m_bOldPlugin = false;
m_lCreatedFiles.Clear();
m_bRemoveDir = false;
m_bSurpressProtectedWarning = false;
m_iSaveGame = -1;
m_iSaveGameManager = -1;
m_lNonRemovedFiles.Clear();
m_lGlobals.Clear();
m_lFakePatchOrder.Clear();
if ( _pOriginalFiles ) delete _pOriginalFiles;
_pOriginalFiles = new COriginalFiles(m_sCurrentDir.ToString());
m_bVanilla = true;
// check the pluginmanager data file exists
CFileIO datFile(m_sCurrentDir + "/PluginManager/pluginmanager.dat");
if ( datFile.exists() )
{
std::vector<Utils::String> *readFile = datFile.readLines();
if ( readFile )
{
float fVersion = 0;
bool spkinstaller = false;
float fBeta = 0;
int iStatus = READ_START;
int iCount = 0;
int iWare = -1;
int iShip = -1;
CBaseFile *packageFile = 0;
for ( int i = 0; i < (int)readFile->size(); i++ )
{
Utils::String line(readFile->at(i));
Utils::String cmd = line.token(":", 1).lower();
Utils::String rest = line.tokens(":", 2).removeFirstSpace();
if ( iStatus == READ_GLOBAL )
{
if ( cmd == "<script>" )
{
packageFile = new CSpkFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<ship>" )
{
packageFile = new CXspFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<base>" )
{
packageFile = new CBaseFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<archive>" )
{
packageFile = new CArchiveFile();
iStatus = READ_SCRIPT;
}
else if ( cmd != "</scripts>" )
{
int game = 0;
bool disabled = false;
Utils::String fileName = line.tokens(":", 5);
Utils::String origName;
if ( fileName.left(2) == "D#" )
{
fileName.erase(0, 2);
disabled = true;
}
if ( fileName.left(2) == "G#" ) {
fileName.erase(0, 2);
game = fileName.token("#", 1).toLong();
fileName = fileName.tokens("#", 2);
}
if ( fileName.isin("O#") )
{
origName = fileName.token("O#", 2);
fileName = fileName.token("O#", 1);
}
C_File *newFile = new C_File(m_sCurrentDir.ToString() + fileName);
newFile->setGame(game);
newFile->SetOriginalName(origName);
newFile->SetDisabled(disabled);
newFile->setFileType((FileType)line.token(":", 1).toLong());
newFile->SetCreationTime((time_t)line.token(":", 2).toLong());
newFile->SetDir(line.token(":", 3));
if ( line.token(":", 4).toLong() )
newFile->SetShared(true);
newFile->UpdateSigned();
m_lFiles.push_back(newFile);
}
}
else if ( iStatus == READ_SCRIPT )
{
if ( cmd == "installspk" )
packageFile->setFilename(rest);
else if ( cmd == "files" )
{
iStatus = READ_SCRIPTFILE;
if ( spkinstaller )
{
int max;
Utils::String *files = rest.tokenise(" ", &max);
for ( int i = 0; i < max; i++ )
{
int fileNum = files[i].toLong();
if ( fileNum >= 0 && fileNum < m_lFiles.size() )
packageFile->AddFile(m_lFiles[fileNum]);
}
CLEANSPLIT(files, max);
}
}
else if ( cmd == "disabled" )
packageFile->SetEnabled(false);
else if ( cmd == "modifieddisabled" )
packageFile->SetModifiedEnabled(false);
else if ( cmd == "icon" )
{
C_File *icon = new C_File(rest.tokens(" ", 2));
packageFile->SetIcon(icon, rest.token(" ", 1));
}
else
packageFile->ParseValueLine(line);
}
else if ( iStatus == READ_SCRIPTFILE )
{
if ( cmd == "<script>" )
{
if ( packageFile->IsMod() && packageFile->IsEnabled() )
m_pEnabledMod = packageFile;
m_lPackages.push_back(packageFile);
this->ConvertOldPackage(packageFile);
packageFile = new CSpkFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<ship>" )
{
m_lPackages.push_back(packageFile);
this->ConvertOldPackage(packageFile);
packageFile = new CXspFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<base>" )
{
m_lPackages.push_back(packageFile);
this->ConvertOldPackage(packageFile);
packageFile = new CBaseFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "<archive>" )
{
m_lPackages.push_back(packageFile);
this->ConvertOldPackage(packageFile);
packageFile = new CArchiveFile();
iStatus = READ_SCRIPT;
}
else if ( cmd == "</scripts>" )
{
if ( packageFile->IsMod() && packageFile->IsEnabled() )
m_pEnabledMod = packageFile;
m_lPackages.push_back(packageFile);
this->ConvertOldPackage(packageFile);
}
else
{
int fileNum = line.token("::", 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(" ", 1).toLong();
gm->iType = line.token(" ", 2).toLong();
gm->cType = line.token(" ", 3)[0];
gm->pWare = NULL;
gm->sWareName = line.tokens(" ", 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(" ", 1).toLong();
if ( line.token(" ", 2).left(4) == "$#C:" )
{
gm->sShipClass = line.token(" ", 2).right(-4);
gm->sShipID = line.tokens(" ", 3);
}
else
{
gm->sShipID = line.tokens(" ", 2);
gm->sShipClass = "OBJ_SHIP_M5";
}
gm->pPackage = NULL;
m_lGameShips.push_back(gm);
if ( iCount <= 0 )
iShip = -1;
}
else if ( cmd.Compare("savegamemanager") )
m_iSaveGameManager = rest.toLong();
else if ( cmd.Compare("savegame") )
m_iSaveGame = rest.toLong();
else if ( cmd == "fakepatch" )
m_iFakePatch = rest.toLong();
else if ( cmd == "updatetime" )
m_iLastUpdated = rest.toLong();
else if ( cmd == "surpressprotectedwarning" )
m_bSurpressProtectedWarning = true;
else if ( cmd == "pluginmanager" )
{
fVersion = rest.toFloat();
m_bOldPlugin = true;
}
else if ( cmd == "setmod" )
m_sSetMod = rest;
else if ( cmd == "shipbuffer" )
m_iShipBuffer = rest.toLong();
else if(cmd == "savedir")
_sSaveDir = rest;
else if ( cmd == "warebuffers" )
{
int max = rest.countToken(" ");
for ( int i = 0; i < WAREBUFFERS; i++ )
{
if ( i > max )
m_iWareBuffer[i] = 0;
else
m_iWareBuffer[i] = rest.token(" ", i + 1).toLong();
}
}
else if ( cmd == "createdfile" )
m_lCreatedFiles.PushBack(CyString(rest));
else if ( cmd == "wares" )
{
iWare = rest.token(" ", 1).toLong();
iCount = rest.token(" ", 2).toLong();
}
else if ( cmd == "ships" )
{
iShip = 1;
iCount = rest.token(" ", 1).toLong();
}
else if ( cmd == "modified" )
m_bVanilla = false;
else if ( cmd == "spkinstaller" )
{
fVersion = rest.toFloat();
spkinstaller = true;
}
else if ( cmd == "betaversion" )
fBeta = rest.toFloat();
else if ( cmd == "uninstall" )
{
C_File *uf = new C_File();
uf->SetFileType(FILETYPE_SCRIPT);
uf->SetCreationTime(rest.token(" ", 1).toLong());
uf->SetFilename(m_sCurrentDir + "/Scripts/" + rest.tokens(" ", 2));
m_lUninstallFiles.push_back(uf);
}
else if ( cmd == "nonremovedfile" )
{
if ( !CFileIO::Remove(rest) )
m_lNonRemovedFiles.PushBack(CyString(rest));
}
else if ( cmd == "original" )
_pOriginalFiles->parse(rest);
else if ( cmd.Compare("GlobalSetting") )
m_lGlobals.PushBack(CyString(rest.token(":", 1)), CyString(rest.tokens(":", 2)));
else if ( cmd.Compare("FakePatchOrder") )
m_lFakePatchOrder.PushBack(CyString(rest.token(":", 1)), CyString(rest.tokens(":", 2)));
else if ( cmd.Compare("EMPPriceOverride") )
this->addEMPPriceOverride(rest.token(" ", 1).toLong(), rest.token(" ", 2).toLong());
else if ( cmd.Compare("EMPNotoOverride") )
this->addEMPNotoOverride(rest.token(" ", 1).toLong(), rest.token(" ", 2).toLong());
else if ( cmd.Compare("BuiltInWarePriceOverride") )
this->addBuiltInWarePriceOverride(rest.token(" ", 1).toLong(), rest.token(" ", 2).toLong());
else if ( cmd.Compare("BuiltInWareNotoOverride") )
this->addBuiltInWareNotoOverride(rest.token(" ", 1).toLong(), rest.token(" ", 2).toLong());
else if ( cmd.Compare("CustomWarePriceOverride") )
this->addCustomWarePriceOverride(rest.token(";", 1), rest.token(";", 2).toLong());
else if ( cmd.Compare("CustomWareNotoOverride") )
this->addCustomWareNotoOverride(rest.token(";", 1), rest.token(";", 2).toLong());
else if ( cmd == "globalfiles" )
iStatus = READ_GLOBAL;
if ( progress )
{
progress->UpdateProgress((long)i, (long)readFile->size());
}
}
readFile->clear();
if ( !spkinstaller )
m_bRedo = true;
delete readFile;
}
}
if ( m_pEnabledMod && !m_pEnabledMod->IsEnabled() )
m_pEnabledMod = NULL;
m_iGame = m_gameExe.GetGameType(m_sCurrentDir.ToString()) + 1;
m_pCurrentGameExe = m_gameExe.GetGame(m_gameExe.GetGameType(m_sCurrentDir.ToString()));
Utils::String sGameVersion;
m_iGameVersion = m_gameExe.GetGameVersion(m_sCurrentDir.ToString(), &sGameVersion) + 1;
m_sGameVersion = sGameVersion;
m_iGameFlags = m_gameExe.GetGameFlags(m_iGame - 1);
m_iMaxPatch = m_gameExe.GetMaxPatch(m_iGame - 1);
// find the fake patch
if ( !ReadyFakePatch() )
return false;
this->RemoveCreatedFiles();
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.ToString() )
{
gNode->Data()->pWare = w;
break;
}
}
}
}
else if ( node->Data()->GetType() == TYPE_XSP )
{
CXspFile *p = (CXspFile *)node->Data();
for ( CListNode<SGameShip> *gNode = m_lGameShips.Front(); gNode; gNode = gNode->next() )
{
if ( p->GetShipID().Compare(gNode->Data()->sShipID.ToString()) )
{
gNode->Data()->pPackage = p;
break;
}
}
}
}
// check the purged time
this->PurgeGameObjects();
m_bLoaded = true;
return true;
}
void CPackages::RemoveCreatedFiles()
{
for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
{
if ( CFileIO(node->str).ExistsOld() )
CFileIO::Remove(node->str.ToString());
else if ( CFileIO(m_sCurrentDir + "/" + node->str).ExistsOld() )
CFileIO::Remove((m_sCurrentDir + "/" + node->str).ToString());
}
m_lCreatedFiles.Clear();
}
void CPackages::PurgeGameObjects()
{
// check for the log file
Utils::String logDir = logDirectory();
CFileIO LogFile(logDir + "/log0" + Utils::String::PadNumber(PMTEXTFILE, 4) + ".txt");
if ( LogFile.exists() )
{
// read the log file to memory
std::vector<CyString> *lines = LogFile.ReadLines();
if ( lines )
{
for ( int i = 0; i < (int)lines->size(); i++ )
{
CyString line(lines->at(i));
CyString start = line.GetToken(":", 1, 1).ToLower();
CyString rest = line.GetToken(":", 2, 2).RemoveFirstSpace();
if ( start.Compare("purged") )
{
long time = rest.ToLong();
if ( time == m_iLastUpdated )
{
this->PurgeWares();
this->PurgeShips();
this->RemoveUninstallScripts();
}
}
}
delete lines;
}
// remove the log file
LogFile.remove();
}
}
void CPackages::PurgeWares()
{
// mark all delete wares as available
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( !w ) continue;
if ( w->iType == WARETYPE_DELETED )
w->iType = WARETYPE_NONE;
}
m_lGameWares[i].RemoveEmpty();
}
}
void CPackages::PurgeShips()
{
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->iType == WARETYPE_DELETED )
node->Data()->iType = WARETYPE_NONE;
}
m_lGameShips.RemoveEmpty();
}
Utils::String CPackages::logDirectory()
{
Utils::String logDir = m_sCurrentDir.ToString();
if ( m_iGameFlags & EXEFLAG_MYDOCLOG ) {
SGameExe *exe = m_gameExe.GetGame(m_iGame - 1);
if ( exe ) {
if ( !exe->sMyDoc.empty() )
logDir = m_sMyDoc + "/" + exe->sMyDoc;
}
}
return logDir;
}
Utils::String CPackages::logDirectory(const Utils::String &gameExe)
{
Utils::String logDir = m_sCurrentDir.ToString();
if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
{
SGameExe *exe = m_gameExe.gameExe(CFileIO(gameExe).filename());
if ( exe )
{
if ( !exe->sMyDoc.empty() )
logDir = m_sMyDoc + "/" + exe->sMyDoc;
}
}
return CFileIO(logDir).fullFilename();
}
Utils::String CPackages::saveDirectory()
{
Utils::String logDir = this->logDirectory();
if ( m_iGameFlags & EXEFLAG_NOSAVESUBDIR )
return logDir;
return logDir + "/save";
}
bool CPackages::ReadyFakePatch()
{
// already exists, lets skip it
if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").ExistsOld() && CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").ExistsOld() )
return true;
// if only one of them exists, lets remove them
if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").ExistsOld() )
CFileIO::Remove(m_sCurrentDir.ToString() + "/PluginManager/PlugMan_Fake.cat");
if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").ExistsOld() )
CFileIO::Remove(m_sCurrentDir.ToString() + "/PluginManager/PlugMan_Fake.dat");
// fake patch is not being used
if ( m_iFakePatch < 0 ) return true;
// now lets find the fake patch
CyString useFile;
if ( m_iFakePatch > 0 )
{
CyString file = CyString::Number(m_iFakePatch).PadNumber(2);
if ( CheckValidPluginManagerFile(file) )
useFile = file;
else if ( CheckIfPluginManagerFile(file) )
useFile = file;
}
if ( useFile.Empty() )
{
int nextfree = this->FindNextFakePatch();
--nextfree; // gets the end fake patch
// work backwards till we find a valid one
while ( nextfree > m_iMaxPatch )
{
CyString file = CyString::Number(nextfree).PadNumber(2);
if ( CheckValidPluginManagerFile(file) )
{
useFile = file;
break;
}
--nextfree;
}
}
CyString addonDir = this->GetAddonDir();
// couldn't find the correct file, lets search for it
if ( useFile.Empty() ) {
int nextfree = this->FindNextFakePatch();
--nextfree; // gets the end fake patch
// work backwards till we find a valid one
while ( nextfree > m_iMaxPatch )
{
CyString file = CyString::Number(nextfree).PadNumber(2);
if ( CheckIfPluginManagerFile(file) )
{
useFile = file;
break;
}
--nextfree;
}
}
if ( !useFile.Empty() )
{
// lets check whats in the file first
CCatFile openCat;
if ( openCat.open((m_sCurrentDir + "/" + useFile + ".cat").ToString(), addonDir.ToString(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
{
std::vector<SInCatFile *> *files = openCat.GetFiles();
bool found = false;
if ( files )
{
Utils::String useAddonDir = addonDir.ToString();
if ( !useAddonDir.empty() ) useAddonDir += "\\";
for (auto itr = files->cbegin(); itr != files->cend(); itr++)
{
if ((*itr)->sFile.Compare("PlugMan\\TFake.pck") )
continue;
if ((*itr)->sFile.Compare(useAddonDir + "t\\44" + Utils::String::PadNumber(PMTEXTFILE, 4) + ".pck") )
continue;
if ((*itr)->sFile.Compare(useAddonDir + "t\\" + Utils::String::PadNumber(PMTEXTFILE, 4) + "-L044.pck") )
continue;
found = true;
break;
}
}
// no files, jsut delete them
CFileIO catFile(m_sCurrentDir + "/" + useFile + ".cat");
if ( !files || !found )
{
if ( catFile.remove() )
{
CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");
if ( datFile.remove() )
return true;
}
}
else
{
if ( catFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat") )
{
CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");
if ( datFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat") )
return true;
// TODO: it failed, restore cat file and do error
}
}
}
// if we're here, we tryed, and failed
return false;
}
// no files found, but we have a fake patch ? restore from backup
else if ( m_iFakePatch > 0 ) {
CLog::log(CLog::Log_Directory, 1, "PlugMan FakePatch seems to be missing (" + Utils::String::Number(m_iFakePatch) + "), Restoring from backup");
CFileIO catFile(m_sCurrentDir + "/PluginManager/Backup/PlugMan_Fake.cat");
if ( catFile.exists() ) {
catFile.copy(m_sCurrentDir.ToString() + "/PluginManager/PlugMan_Fake.cat");
CFileIO datFile(m_sCurrentDir + "/PluginManager/Backup/PlugMan_Fake.dat");
if ( datFile.exists() ) datFile.copy(m_sCurrentDir.ToString() + "/PluginManager/PlugMan_Fake.dat");
}
}
// otherwise we didn't need to (hopefully)
return true;
}
bool CPackages::CheckIfPluginManagerFile(CyString filename)
{
bool found = false;
CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
if ( catFile.exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").ExistsOld() )
{
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()->GetFullDir().IsIn("::") ) {
if (CFileIO(node->Data()->GetFullDir().GetToken("::", 1, 1)).filename().Compare("PlugMan_Fake.cat") ) {
Utils::String filename = node->Data()->GetFilePointer().GetToken("::", 2, 2).ToString();
filename = filename.findReplace("/", "\\");
if ( openFile.FindData(filename) )
++count;
else
++noncount;
}
}
}
if ( (count && !noncount) || (count > noncount) )
found = true;
}
}
return found;
}
bool CPackages::CheckValidPluginManagerFile(CyString filename)
{
// both the cat file and dat file exists, lets open it
CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
if ( catFile.exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").ExistsOld() )
{
CCatFile openFile;
if ( openFile.open(catFile.fullFilename(), this->getAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
{
if ( openFile.FindData("PlugMan\\TFake.pck") )
return true;
if ( openFile.FindData("pluginmanagerfake.pck") )
return true;
}
}
return false;
}
/**
* Converts a package in old format
*
* Some entries in older packages need to be changed for the new installer
* These Include:
* - Game Version, old format is an id of position in the Data/exe file, new is 2 values, one for game, one for the version
*/
void CPackages::ConvertOldPackage(CBaseFile *package)
{
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( gNode->Data()->iGame == -1 ) {
// all versions
if ( gNode->Data()->iVersion == 0 )
gNode->Data()->iGame = 0;
else
{
int version = 0;
gNode->Data()->iGame = m_gameExe.ConvertGameType(gNode->Data()->iVersion, &version);
gNode->Data()->iVersion = version;
}
}
}
if ( package->forumLink().empty() && package->webSite().isin("forum.egosoft")) {
package->setForumLink(package->webSite());
package->setWebSite("");
}
// convert the version
if ( package->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)package;
if ( spk->GetScriptType() == CSpkFile::SCRIPTTYPE_CUSTOM )
{
CyString type = spk->GetScriptTypeString(44);
if ( type.Compare("Ship Upgrade") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPUPGRADE);
else if ( type.Compare("Trade Script") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_TRADE);
else if ( type.Compare("Fleet Management") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_FLEET);
else if ( type.Compare("Navigation Script") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_NAVIGATION);
else if ( type.Compare("Piracy") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_PIRACY);
else if ( type.Compare("Other") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_OTHER);
else if ( type.Compare("Ship Command") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPCOMMAND);
else if ( type.Compare("Station Command") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_STATIONCOMMAND);
else if ( type.Compare("al plugin") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_ALPLUGIN);
else if ( type.Compare("combat script") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_COMBAT);
else if ( type.Compare("bbs and missions") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_MISSION);
else if ( type.Compare("extension mod") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_EXTENSION);
else if ( type.Compare("rebalance mod") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_REBALANCE);
else if ( type.Compare("general mod") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_GENERALMOD);
else if ( type.Compare("total conversion") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_TOTAL);
else if ( type.Compare("cheat script") )
spk->SetScriptType(CSpkFile::SCRIPTTYPE_CHEAT);
else if ( type == "Library Script" )
{
spk->SetScriptType("");
spk->SetLibrary();
}
if ( spk->GetScriptType() != CSpkFile::SCRIPTTYPE_CUSTOM )
spk->SetScriptType("");
}
}
else if ( package->GetType() == TYPE_XSP )
{
CXspFile *xsp = (CXspFile *)package;
Utils::String data = xsp->GetShipData();
for ( int i = 17; i <= 18; i++ )
{
Utils::String model = data.token(";", i);
Utils::String modelExt = model.right(4);
// check file extension
if ( modelExt.Compare(".bod") || modelExt.Compare(".pbd") )
data = data.replaceToken(";", i, model.left(-4));
}
xsp->SetShipData(data);
}
// any extra files that are in director folder
if ( package->AnyFileType(FILETYPE_EXTRA) )
{
for ( C_File *f = package->GetFirstFile(FILETYPE_EXTRA); f; f = package->GetNextFile(f) )
{
if ( !f->GetDir().Compare("director") )
continue;
if ( f->CheckFileExt("xml") || f->CheckFileExt("pck") )
{
f->SetDir("");
f->SetFileType(FILETYPE_MISSION);
}
}
}
}
void CPackages::UpdatePackage(CBaseFile *p)
{
if ( p->GetType() != TYPE_SPK )
return;
CSpkFile *package = (CSpkFile *)p;
// update the signed status
package->UpdateSigned(true);
// check for another mod
if ( !package->IsAnotherMod() )
return;
package->SetParent((CSpkFile *)FindSpkPackage(package->GetOtherName(), package->GetOtherAuthor()));
}
/**
* Updates the package list once data has been read
*
* Finds any original Files
* Updates Signed status of all packages
*/
bool CPackages::UpdatePackages(int doStatus, bool individual)
{
// if theres no original files, then set the current structure as the base
if ( doStatus == 0 || doStatus == -1 )
{
_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(CyString file, int *error)
{
// first check if it exists
if ( !CFileIO(file).ExistsOld() )
{
*error = INSTALLERR_NOEXIST;
return -1;
}
// open the spk file
float fVersion = 0.0f;
int check = CBaseFile::CheckFile(file, &fVersion);
// wrong version
if ( fVersion > (float)FILEVERSION )
{
*error = INSTALLERR_VERSION;
return -1;
}
return check;
}
CMultiSpkFile *CPackages::OpenMultiPackage(CyString file, int *error, CProgressInfo *progress)
{
int check = CheckOpenPackage(file, error);
if ( *error == -1 )
return false;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return false;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
bool ret = false;
if ( package->ReadFile(file, true) )
{
if ( package->ReadAllPackages(SPKREAD_NODATA) )
{
for ( CListNode<SMultiSpkFile> *node = package->GetFileList()->Front(); node; node = node->next() )
this->ConvertOldPackage(node->Data()->pFile);
ret = true;
}
}
if ( ret )
return package;
delete package;
return NULL;
}
bool CPackages::OpenMultiPackage ( CyString file, CLinkList<CBaseFile> *packageList, int *error, CProgressInfo *progress )
{
int check = CheckOpenPackage(file, error);
if ( *error == -1 )
return false;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return false;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
bool ret = false;
if ( package->ReadFile(file, true) )
{
if ( package->ReadAllPackages(SPKREAD_ALL, packageList) )
{
for ( CListNode<CBaseFile> *node = packageList->Front(); node; node = node->next() )
this->ConvertOldPackage(node->Data());
ret = true;
}
}
delete package;
return ret;
}
int CPackages::PrepareMultiPackage ( CyString file, CLinkList<CBaseFile> *errorPackageList, int *error, CProgressInfo *progress )
{
int check = CheckOpenPackage(file, error);
if ( *error == -1 )
return 0;
if ( check != SPKFILE_MULTI )
{
*error = INSTALLERR_NOMULTI;
return 0;
}
*error = INSTALLERR_NONE;
CMultiSpkFile *package = new CMultiSpkFile;
int count = 0;
if ( package->ReadFile(file, true) )
{
CLinkList<CBaseFile> packageList;
if ( package->ReadAllPackages(SPKREAD_ALL, &packageList) )
{
for ( CListNode<CBaseFile> *node = packageList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
this->ConvertOldPackage(p);
int error = this->PrepareInstallPackage(p);
if ( error )
{
node->Data()->SetLoadError(error);
errorPackageList->push_back(p);
}
else
++count;
}
}
packageList.clear();
}
for ( SMultiSpkFile *ms = package->GetFileList()->First(); ms; ms = package->GetFileList()->Next() )
ms->pFile = NULL;
delete package;
return count;
}
CBaseFile *CPackages::OpenPackage(CyString file, int *error, CProgressInfo *progress, int readtype, int flags)
{
int check = CheckOpenPackage(file, error);
if ( *error == -1 )
return NULL;
CBaseFile *installFile = 0;
if ( progress )
progress->DoHalf();
switch (check)
{
case SPKFILE_OLD:
*error = INSTALLERR_OLD;
return NULL;
case SPKFILE_INVALID:
// convert xsp
if ( CFileIO(file).isFileExtension("xsp") )
{
installFile = new CXspFile();
if ( !((CXspFile *)installFile)->ConvertOld(file.ToString()) )
{
delete installFile;
return NULL;
}
break;
}
*error = INSTALLERR_INVALID;
return NULL;
case SPKFILE_BASE:
installFile = new CBaseFile();
if ( !installFile->readFile(file.ToString(), readtype, progress) )
{
delete installFile;
return NULL;
}
break;
case SPKFILE_SINGLE:
installFile = new CSpkFile();
if ( !((CSpkFile *)installFile)->readFile(file.ToString(), readtype, progress) )
{
delete installFile;
return NULL;
}
break;
case SPKFILE_MULTI:
*error = INSTALLERR_NOMULTI;
return NULL;
case SPKFILE_SINGLESHIP:
installFile = new CXspFile();
if ( !((CXspFile *)installFile)->ReadFile(file, readtype, progress) )
{
delete installFile;
return NULL;
}
break;
default:
*error = INSTALLERR_UNKNOWN;
return NULL;
}
if ( progress )
progress->SecondHalf();
// now uncomress all files
if ( !(flags & READFLAG_NOUNCOMPRESS) )
installFile->UncompressAllFiles(progress);
this->ConvertOldPackage (installFile);
return installFile;
}
void CPackages::PurgeUninstallScripts(CBaseFile *package, CyStringList *errors)
{
for ( CListNode<C_File> *fNode = m_lUninstallFiles.Front(); fNode; fNode = fNode->next() )
{
C_File *uf = fNode->Data();
// check against any script files
for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
{
C_File *checkFile = checkNode->Data();
if ( !checkFile ) continue;
if ( checkFile->GetFileType() != FILETYPE_UNINSTALL && checkFile->GetFileType() != FILETYPE_SCRIPT )
continue;
if ( uf->GetFilename().Compare(checkFile->GetFilename()) )
{
if ( RemoveUninstallFile(uf, errors) )
fNode->DeleteData();
break;
}
}
}
m_lUninstallFiles.RemoveEmpty();
this->WriteData();
}
int CPackages::InstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *errored, CLinkList<CBaseFile> *installedList)
{
if ( m_lInstallList.empty() ) return false;
int installed = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( this->InstallPackage(p, errors, progress, !p->IsEnabled()) )
{
++installed;
if ( installedList )
installedList->push_back(p);
}
else if ( errored )
{
m_lPackages.remove(p);
errored->push_back(p);
}
}
m_lInstallList.clear();
this->WriteData();
return installed;
}
void CPackages::_addToFakePatch(CBaseFile *pPackage)
{
CCatFile cat;
if ( CCatFile::Opened(cat.open((m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").ToString(), 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).ToString()) ) {
continue;
}
// check if its already in the fake patch
if ( f->Data()->GetFullDir().IsIn("::") ) {
continue;
}
Utils::String toFile;
if ( cat.AppendFile(f->Data()->filePointer(), f->Data()->GetNameDirectory(pPackage).ToString(), true, (m_iGameFlags & EXEFLAG_NOXOR) ? false : true, &toFile) ) {
CLog::logf(CLog::Log_Install, 2, "Adding file: %s into the fake patch", f->Data()->GetNameDirectory(pPackage).c_str());
CFileIO::Remove(f->Data()->filePointer());
f->Data()->SetFilename(m_sCurrentDir + "/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, "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, "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, "Dependacies missing for package, setting to disabled");
}
// search for an old version
if ( oldPackage && oldPackage == m_pEnabledMod && disabled )
disabled = prevDisabled;
return disabled;
}
Utils::String CPackages::getCurrentDirectory() const
{
return (_pCurrentDir) ? _pCurrentDir->dir : Utils::String::Null();
}
bool CPackages::InstallPackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress, bool disabled )
{
CLog::logf(CLog::Log_Install, 1, "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, "Package is an Update, checking for existing package installed");
if ( !oldPackage ) {
CLog::log(CLog::Log_Install, 2, "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("temp");
if ( f->Data()->IsFakePatch() ) {
CLog::logf(CLog::Log_Install, 2, "Moving fake package to temporary location to preper for update, %s", f->Data()->GetFilePointer().c_str());
f->Data()->SetName(CyString("Fake_") + f->Data()->GetName());
}
}
}
}
// no need to backup if we're disabling them
if ( !disabled )
{
// find any uninstall files and remove them
CLog::log(CLog::Log_Install, 3, "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, "Checking for any existing files");
if ( oldPackage )
{
CLog::logf(CLog::Log_Install, 3, "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, "Reading all files into memory");
package->ReadAllFilesToMemory();
CLog::log(CLog::Log_Install, 3, "Starting to install files");
if ( !package->InstallFiles (m_sCurrentDir, progress, &m_lFiles, errors, !disabled, this) )
{
CLog::log(CLog::Log_Install, 2, "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 = false;
// merge the update into the old package
bool dontAdd = false;
if ( package->GetType() == TYPE_SPK )
{
if ( ((CSpkFile *)package)->IsPackageUpdate() )
{
// now copy any files from a mod
for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
{
if ( !f->Data() )
continue;
if ( f->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if ( !f->Data()->CheckFileExt("cat") )
continue;
// if fake patch, find first fake patch in package
C_File *findMatching = NULL;
if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
{
for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if ( !node->Data()->CheckFileExt("cat") )
continue;
if ( !node->Data()->IsFakePatch() )
continue;
findMatching = node->Data();
break;
}
}
// otherwise, just add to the mod of the same name
else
{
for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
{
if ( !node->Data() )
continue;
if ( node->Data()->GetFileType() != FILETYPE_MOD )
continue;
// we only need the cat file
if ( !node->Data()->CheckFileExt("cat") )
continue;
if ( node->Data()->GetName().Compare(f->Data()->GetName()) )
{
findMatching = node->Data();
break;
}
}
}
if ( findMatching )
{
// copy accross all mods
CCatFile catTo, catFrom;
if ( catFrom.open(f->Data()->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() + "::" + c->sFile, c->sFile);
}
}
}
// now remove the files
C_File *m = package->FindMatchingMod(f->Data());
RemoveFile(f->Data(), errors);
m_lFiles.remove(f->Data());
f->ChangeData(NULL);
if ( m )
{
int pos = package->GetFileList()->FindPos(m);
RemoveFile(m, errors);
m_lFiles.remove(m);
if ( pos != -1 )
package->GetFileList()->GetNode(pos)->ChangeData(NULL);
}
}
// no matching file, then we shall just renaming back
else
{
if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
{
shuffle = true;
C_File *match = package->FindMatchingMod(f->Data());
CyString next = CyString::Number(this->FindNextFakePatch()).PadNumber(2);
CyString oldFilePointer = f->Data()->GetFilePointer();
f->Data()->SetDir("");
f->Data()->ChangeBaseName(next);
if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));
if ( match )
{
CyString oldFilePointer = match->GetFilePointer();
match->SetDir("");
match->ChangeBaseName(next);
if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
}
}
else
{
C_File *match = package->FindMatchingMod(f->Data());
f->Data()->SetDir("");
if ( CFileIO(f->Data()->GetFilePointer()).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));
if ( match )
{
match->SetDir("");
if ( CFileIO(match->GetFilePointer()).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
}
}
}
}
package->GetFileList()->RemoveEmpty();
((CSpkFile *)oldPackage)->MergePackage(package);
delete package;
package = oldPackage;
oldPackage = false;
dontAdd = true;
CDirIO(m_sCurrentDir).RemoveDir("temp");
CDirIO(m_sCurrentDir).RemoveDir("Mods/temp");
}
}
// if theres an icon, write it
if ( package->GetIcon() )
{
CLog::log(CLog::Log_Install, 3, "Checking to install icon display file");
C_File *icon = package->GetIcon();
if ( !icon->GetData() || !icon->GetDataSize() ) {
package->SetIcon(NULL, "");
CLog::log(CLog::Log_Install, 2, "Unable to extract icon, clearing");
}
else
{
CDirIO Dir(m_sCurrentDir);
bool ready = true;
if ( !Dir.exists("PluginManager") )
{
if ( !Dir.Create("PluginManager") )
ready = false;
}
if ( ready && !Dir.exists("PluginManager/Icons") )
{
if ( !Dir.Create("PluginManager/Icons") )
ready = false;
}
if ( ready )
{
if ( !icon->UncompressData() )
package->SetIcon(NULL, "");
else
{
CFileIO iconFile(m_sCurrentDir + "/PluginManager/Icons/" + package->author() + "_" + package->name() + "." + package->GetIconExt());
if ( iconFile.WriteData((const char *)icon->GetData(), icon->GetDataSize()) )
{
icon->SetFilename(CyString(package->author()) + "_" + package->name() + "." + package->GetIconExt());
icon->SetFullDir(m_sCurrentDir + "/PluginManager/Icons");
}
else
package->SetIcon(NULL, "");
}
}
if ( package->GetIcon() )
package->GetIcon()->DeleteData();
}
}
// remove all data
CLog::log(CLog::Log_Install, 3, "Clearing all unneeded file data");
package->ClearFileData();
// add to list
if ( !dontAdd )
{
CLog::log(CLog::Log_Install, 1, "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, "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, "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->GetFilename(), FILETYPE_README) )
dontRemove = true;
}
// remove from hard drive
if ( !dontRemove && RemoveFile(f, errors) )
{
CLog::logf(CLog::Log_Install, 1, "Removed unused file: %s", f->GetFilePointer().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, "Shuffling Fake patches");
ShuffleFakePatches(errors);
CLog::log(CLog::Log_Install, 2, "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->GetOtherName(), spk->GetOtherAuthor()));
CLog::logf(CLog::Log_Install, 2, "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, "Setting package as primary mod");
}
package->updateTextDB();
m_bRemoveDir = true;
CLog::log(CLog::Log_Install, 1, "Saving data to file");
this->WriteData();
CLog::log(CLog::Log_Install, 1, "Installation Finished");
return true;
}
bool CPackages::UninstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *uninstalledPackages, CLinkList<CBaseFile> *disabledPackages)
{
if ( m_lInstallList.empty() ) return false;
// update the used status, excluding all packages we are about to remove
UpdateUsedFiles(&m_lInstallList);
CyStringList removeDirs;
CLinkList<C_File> uninstallFiles;
CLinkList<C_File> fileList;
bool readme = false, original = false, shuffle = false;
// find all files that need to be removed
int maxFiles = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
maxFiles += this->GetAllPackageFiles(p, &fileList, true);
// disable any dependants
if ( p->GetType() == TYPE_SPK && ((CSpkFile *)p)->IsLibrary() )
{
CLinkList<CBaseFile> depList;
if ( this->GetDependacyList(p, &depList) )
{
for ( CBaseFile *depP = depList.First(); depP; depP = depList.Next() )
{
if ( depP->IsEnabled() )
this->PrepareDisablePackage(depP);
}
if ( m_lDisableList.size() )
this->DisablePreparedPackages(errors, progress, disabledPackages);
}
}
}
// interate through all the files in the package
int fileCount = 0;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// display progress if needed
if ( progress )
{
progress->UpdateProgress(fileCount++, maxFiles);
progress->UpdateFile(f);
}
// skip uninstall files
if ( f->GetFileType() == FILETYPE_UNINSTALL )
{
uninstallFiles.push_back(f);
continue;
}
// only delete files that are not used
// if its a shared file, we skip it
if ( f->GetUsed() || f->IsShared() )
continue;
if ( f->GetFileType() == FILETYPE_README )
readme = true;
else if ( f->GetFileType() == FILETYPE_UNINSTALL || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND || f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_SHIPSCENE || f->GetFileType() == FILETYPE_COCKPITSCENE || f->GetFileType() == FILETYPE_SHIPOTHER || f->GetFileType() == FILETYPE_SHIPMODEL || f->GetFileType() == FILETYPE_ADVERT )
{
CyString dir = f->GetDirectory(NULL);
removeDirs.PushBack(dir, true);
dir = dir.FindReplace("\\", "/");
if ( dir.IsIn("/") )
{
for ( int i = dir.NumToken("/"); i; i-- )
{
CyString remDir = dir.GetToken("/", 1, i);
removeDirs.PushBack(remDir, true);
}
}
}
if ( f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(6).lower() == "extras" )
removeDirs.PushBack("Extras", true);
if ( RemoveFile(f, errors) )
{
original = _pOriginalFiles->restoreFile(f, errors);
}
else // problem removeing (try when the program closes)
m_lNonRemovedFiles.PushBack(f->GetFilePointer());
// check for fake patchs
if ( f->IsFakePatch() )
shuffle = true;
else if ( f->isAutoTextFile() )
shuffle = true;
// remove the file from the main list as swell
m_lFiles.remove(f, false);
delete f;
}
// remove all the packages from memory
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
p->GetFileList()->clear();
m_lPackages.remove(p);
if ( p == m_pEnabledMod )
m_pEnabledMod = NULL;
if ( uninstalledPackages )
uninstalledPackages->push_back(p);
else
delete p;
}
m_lInstallList.clear();
// check unistall files
if ( !uninstallFiles.empty() )
{
removeDirs.PushBack(CyString("PluginManager/Uninstall"));
// make sure the scripts directory is created, even thou it should always be there anyways
CDirIO scriptDir(m_sCurrentDir);
if ( !scriptDir.exists("scripts") )
{
if ( scriptDir.Create("Scripts") )
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, "Scripts", errors);
else
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, "Scripts", errors);
}
for ( C_File *uf = uninstallFiles.First(); uf; uf = uninstallFiles.Next() )
{
C_File *newFile = new C_File();
newFile->SetFileType(FILETYPE_SCRIPT);
newFile->SetFilename(uf->GetFilename());
newFile->SetCreationTime(uf->GetCreationTime());
// other installed packages use this file as well, copy it
CyString newFilename = m_sCurrentDir + "/" + newFile->GetNameDirectory(NULL);
CFileIO file(uf->GetFilePointer());
if ( uf->GetUsed() )
{
if ( file.copy(newFilename.ToString()) )
this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY, newFile->GetNameDirectory(NULL), errors);
else
{
this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY_FAIL, newFile->GetNameDirectory(NULL), errors);
delete newFile;
newFile = NULL;
}
}
// otherwise just move it
else
{
if ( file.Rename(newFilename) )
this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE, newFile->GetNameDirectory(NULL), errors);
else
{
this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE_FAIL, newFile->GetNameDirectory(NULL), errors);
delete newFile;
newFile = NULL;
}
m_lFiles.remove(uf, false);
delete uf;
}
// add to the list
if ( newFile )
{
// first check if theres a matching one
bool found = false;
for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
{
C_File *checkFile = node->Data();
if ( checkFile->GetFilename().Compare(newFile->GetFilename()) )
{
found = true;
break;
}
}
// not found, so add it
if ( !found )
m_lUninstallFiles.push_back(newFile);
else
delete newFile;
}
}
}
uninstallFiles.clear();
// remove all directies that we're not using
if ( readme )
{
removeDirs.PushBack(CyString("PluginManager/Readme"));
removeDirs.PushBack(CyString("Readme"));
}
if ( original ) {
removeDirs.PushBack(CyString("PluginManager/Original/Replacements"));
removeDirs.PushBack(CyString("PluginManager/Original"));
}
removeDirs.PushBack(CyString("PluginManager/Disabled"));
RemoveUnusedDirectories(removeDirs, errors);
// finally lets shuffle any fake patchs to fill in gaps
if ( shuffle )
{
ShuffleFakePatches(errors);
ShuffleTextFiles(errors);
}
this->WriteData();
return true;
}
/**
* Prepares a package to be uninstalled
*
* Adds the package, and all its children onto the uninstall list to be used by UninstallPreparePackages
*/
void CPackages::PrepareUninstallPackage(CBaseFile *package)
{
// add package to list
if ( !m_lInstallList.FindData(package) )
m_lInstallList.push_back(package);
// add all children
CLinkList<CBaseFile> children;
if ( this->GetChildPackages(package, &children, true) )
{
for ( CBaseFile *p = children.First(); p; p = children.Next() )
{
if ( !m_lInstallList.FindData(p) )
m_lInstallList.push_back(p);
}
}
}
bool CPackages::PrepareEnablePackage(CBaseFile *package)
{
ClearError();
if ( package->GetParent() && !package->GetParent()->IsEnabled() )
{
m_iError = PKERR_NOPARENT;
return false;
}
if ( m_bVanilla && !package->IsSigned() )
{
m_iError = PKERR_MODIFIED;
return false;
}
// check if it needs depancies
if ( package->AnyDependacies() )
{
if ( this->GetMissingDependacies(package, NULL, true) )
{
m_iError = PKERR_MISSINGDEP;
return false;
}
}
if ( !m_lEnableList.FindData(package) )
{
if ( !package->IsEnabled() )
m_lEnableList.push_back(package);
// do all the children as well
if ( m_bAutoEnableChild )
{
CLinkList<CBaseFile> childList;
this->GetChildPackages(package, &childList, true);
// add all disabled packages to list
for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
{
if ( !p->IsEnabled() )
m_lEnableList.push_back(p);
}
}
}
m_bRemoveDir = true;
return true;
}
bool CPackages::PrepareDisableForVanilla()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsSigned() && p->IsEnabled() )
{
this->PrepareDisablePackage(p);
m_bDisableVanilla = true;
}
}
return true;
}
bool CPackages::PrepareEnableLibrarys()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->GetType() != TYPE_SPK )
continue;
if ( !p->IsEnabled() && ((CSpkFile *)p)->IsLibrary() )
this->PrepareEnablePackage(p);
}
return true;
}
bool CPackages::PrepareEnableFromVanilla()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() && p->IsModifiedEnabled() ) {
this->PrepareEnablePackage(p);
}
}
return true;
}
int CPackages::GetDependacyList(CBaseFile *package, CLinkList<CBaseFile> *list)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->IsPackageNeeded(package->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);
}
}
}
char CPackages::ConvertWareTypeBack(int w)
{
switch ( w )
{
case WARES_BIO:
return 'b';
case WARES_ENERGY:
return 'e';
case WARES_FOOD:
return 'f';
case WARES_MINERAL:
return 'm';
case WARES_TECH:
return 't';
case WARES_NATURAL:
return 'n';
}
return 't';
}
int CPackages::ConvertWareType(char w)
{
switch ( LOWER(w) )
{
case 'b':
return WARES_BIO;
case 'e':
return WARES_ENERGY;
case 'f':
return WARES_FOOD;
case 'm':
return WARES_MINERAL;
case 't':
return WARES_TECH;
case 'n':
return WARES_NATURAL;
}
return WARES_TECH;
}
void CPackages::SetupShips()
{
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->iType != WARETYPE_NONE )
wNode->Data()->iType = WARETYPE_DELETED;
}
// find any new ships to add to the list
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
if ( pNode->Data()->GetType() != TYPE_XSP )
continue;
CXspFile *p = (CXspFile *)pNode->Data();
bool found = false;
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->sShipID.Compare(p->GetShipID().c_str()) )
{
if ( !p->IsEnabled() )
wNode->Data()->iType = WARETYPE_DISABLED;
else
wNode->Data()->iType = WARETYPE_ADDED;
found = true;
wNode->Data()->pPackage = p;
wNode->Data()->sShipClass = p->GetShipClass();
break;
}
}
if ( found || !p->IsEnabled() )
continue;
// first find any free
SGameShip *gw = NULL;
for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
{
if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
gw = wNode->Data();
// find an old entry for the ware and add it to the same place
if ( wNode->Data()->sShipID.Compare(p->GetShipID().c_str()) )
{
gw = wNode->Data();
break;
}
}
// none found, create one
if ( !gw )
{
gw = new SGameShip;
gw->sShipID = p->GetShipID();
gw->sShipClass = p->GetShipClass();
gw->pPackage = p;
m_lGameShips.push_back(gw);
}
gw->iType = WARETYPE_ADDED;
}
}
void CPackages::SetupWares()
{
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *wNode = m_lGameWares[i].Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->iType != WARETYPE_NONE )
wNode->Data()->iType = WARETYPE_DELETED;
}
}
// find any new wares to add to the list
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
if ( pNode->Data()->GetType() != TYPE_SPK )
continue;
CSpkFile *p = (CSpkFile *)pNode->Data();
for ( CListNode<SWares> *node = p->GetWaresList()->Front(); node; node = node->next() )
{
SWares *w = node->Data();
int wareType = CPackages::ConvertWareType(w->cType);
// check if its on the list
bool found = false;
for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
{
if ( wNode->Data()->sWareName == w->sID )
{
if ( !p->IsEnabled() )
wNode->Data()->iType = WARETYPE_DISABLED;
else
wNode->Data()->iType = WARETYPE_ADDED;
wNode->Data()->pWare = w;
found = true;
break;
}
}
if ( found || !p->IsEnabled() )
continue;
// first find any free
SGameWare *gw = NULL;
for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
{
if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
gw = wNode->Data();
// find an old entry for the ware and add it to the same place
if ( wNode->Data()->sWareName == w->sID )
{
gw = wNode->Data();
break;
}
}
// none found, create one
if ( !gw )
{
gw = new SGameWare;
gw->sWareName = w->sID;
gw->iPos = m_lGameWares[wareType].size();
gw->pWare = w;
gw->cType = w->cType;
m_lGameWares[wareType].push_back(gw);
}
gw->iType = WARETYPE_ADDED;
}
}
}
/**
* Closing the current directory
*
* When existing, program needs to close the directory
*/
bool CPackages::CloseDir ( CyStringList *errors, CProgressInfo *progress, bool removedir )
{
if ( m_sCurrentDir.Empty() )
return true;
if ( !m_bLoaded )
return true;
CLog::log(CLog::Log_Directory, 1, "closing directory: " + m_sCurrentDir.ToString());
if ( m_bRenameText ) {
CLog::log(CLog::Log_Directory, 2, "Creating other language files for game");
CreateLanguageTextFiles(errors);
}
CLog::log(CLog::Log_Directory, 2, "Backing up save game files");
if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.dat").ExistsOld() ) {
CLog::log(CLog::Log_IO, 3, "Removing old PluginManager.dat file");
CFileIO::Remove(m_sCurrentDir.ToString() + "/mods/PluginManager.dat");
}
if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.cat").ExistsOld() ) {
CLog::log(CLog::Log_IO, 3, "Removing old PluginManager.cat file");
CFileIO::Remove(m_sCurrentDir.ToString() + "/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("dat") )
fDat = m_pEnabledMod->GetNextFile(fDat);
if ( fDat )
{
C_File *fCat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
while ( fCat && !fCat->IsFakePatch() && !fCat->CheckFileExt("cat") && !fCat->GetBaseName().Compare(fDat->GetBaseName()) )
fCat = m_pEnabledMod->GetNextFile(fCat);
if ( fCat )
{
CFileIO(fDat->GetFilePointer()).copy(m_sCurrentDir.ToString() + "/mods/PluginManager.dat");
CFileIO(fCat->GetFilePointer()).copy(m_sCurrentDir.ToString() + "/mods/PluginManager.cat");
}
}
}
else if ( m_iGame == GAME_X3 && !m_sSetMod.Empty() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").ExistsOld() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").ExistsOld() )
{
CLog::log(CLog::Log_Directory, 2, "Copying mod file: " + m_sSetMod.ToString() + ", to PluginManager.cat");
CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").copy(m_sCurrentDir.ToString() + "/mods/PluginManager.dat");
CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").copy(m_sCurrentDir.ToString() + "/mods/PluginManager.cat");
}
if ( !CDirIO(m_sCurrentDir).exists("mods") )
CDirIO(m_sCurrentDir).Create("mods");
SetupWares();
SetupShips();
CreateEMPFile();
CreateWareFiles();
CreateDummies();
CreateComponants();
CreateTShips();
CreateCutData();
CreateBodies();
CreateAnimations();
CreateCustomStarts();
CreateGlobals();
CreatePluginManagerText();
RestoreFakePatch();
}
RemoveFailedFiles();
WriteData();
if ( removedir && m_bRemoveDir )
{
m_bRemoveDir = false;
CyStringList removeDirs;
removeDirs.PushBack(".");
RemoveUnusedDirectories(removeDirs, errors);
}
this->SetCurrentDir("");
m_bLoaded = false;
return true;
}
CyString CPackages::GetModKey()
{
return m_gameExe.GetModKey(m_iGame - 1);
}
CyString CPackages::GetSelectedModName()
{
if ( !m_pEnabledMod )
{
if ( m_sSetMod.Empty() && m_iGame == GAME_X3 )
return "PluginManager";
return m_sSetMod;
}
if ( !m_pEnabledMod->IsEnabled() )
return m_sSetMod;
C_File *f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
if ( !f )
return m_sSetMod;
CyString name = f->GetFilename();
name = name.Left(-4);
return name;
}
bool CPackages::RestoreFakePatch()
{
CLog::log(CLog::Log_Directory, 1, "Restoring PluginManager fake patch into game");
m_iFakePatch = -1;
CFileIO catFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat");
CFileIO datFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat");
// if only 1 exists, remove it
if ( catFile.exists() && !datFile.exists() ) {
CLog::log(CLog::Log_Directory, 1, "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, "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, "Creating pluginmanagerfake.txt file to add to Fake Patch");
// we need to add the plugin manager file in
CyString file = m_sTempDir;
if ( !file.Empty() )
file += "/";
file += "pluginmanagerfake.txt";
file = file.FindReplace("\\", "/");
CFileIO fakeFile(file);
std::vector<CyString> lines;
lines.push_back("//pluginmanager fake patch");
CLog::log(CLog::Log_Directory, 3, "Writing pluginmanagerfake.txt file to add to Fake Patch");
if ( !fakeFile.WriteFile(&lines) ) {
CLog::log(CLog::Log_Directory, 3, "Writing pluginmanagerfake.txt failed!!");
}
else {
/*
CLog::log(CLog::Log_Directory, 2, "Adding TFake.pck file into FakePatch");
CCatFile fakePatch;
if ( fakePatch.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE )
{
if ( fakePatch.AppendFile(file.ToString(), "PlugMan\\TFake.pck") )
fakePatch.WriteCatFile();
} */
}
// backup existing mod files incase something happens to them
if ( !datFile.exists() || !catFile.exists() ) {
CLog::log(CLog::Log_Directory, 2, Utils::String("ERROR: ") + (!catFile.exists() ? "cat" : "dat") + " file appears to be missing");
}
else {
catFile.GetDirIO().Create("Backup");
catFile.copy(catFile.dir() + "/Backup/" + catFile.filename());
datFile.copy(datFile.dir() + "/Backup/" + datFile.filename());
}
// find next available fake patch
m_iFakePatch = this->FindNextFakePatch();
CLog::log(CLog::Log_Directory, 2, "Finding next available fake patch number: " + (long)m_iFakePatch);
Utils::String filename = Utils::String::PadNumber(m_iFakePatch, 2);
CLog::log(CLog::Log_Directory, 2, "Renaming cat file to: " + filename + ".cat");
if ( catFile.Rename(m_sCurrentDir + "/" + filename + ".cat") )
{
CLog::log(CLog::Log_Directory, 2, "Renaming dat file to: " + filename + ".dat");
if ( datFile.Rename(m_sCurrentDir + "/" + filename + ".dat") ){
CLog::log(CLog::Log_Directory, 3, "Deleting pluginmanagerfake.txt temporary file");
fakeFile.remove();
return true;
}
else {
CLog::log(CLog::Log_Directory, 2, "ERROR: failed to rename dat file");
}
// TODO: restore cat file
}
else {
CLog::log(CLog::Log_Directory, 2, "ERROR: failed to rename cat file");
}
CLog::log(CLog::Log_Directory, 3, "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, "Writing data file for current directory: " + m_sCurrentDir.ToString());
CyStringList lines;
CyString version;
version.FromFloat(GetLibraryVersion(), 2);
version.Prepend("SpkInstaller: ");
lines.PushBack(version);
lines.PushBack(CyString("UpdateTime: ") + (long)m_iLastUpdated);
if ( m_iFakePatch != -1 )
lines.PushBack(CyString("FakePatch: ") + (long)m_iFakePatch);
if ( m_iSaveGame != -1 )
lines.PushBack(CyString("SaveGame: ") + (long)m_iSaveGame);
lines.PushBack(CyString("SaveGameManager: ") + (long)m_iSaveGameManager);
if ( !m_bVanilla )
lines.PushBack("Modified");
if ( m_bUsedWare )
lines.PushBack("UsedWare");
if ( m_bSurpressProtectedWarning )
lines.PushBack("SurpressProtectedWarning");
if ( !m_sSetMod.Empty() )
lines.PushBack(CyString("SetMod: ") + m_sSetMod);
if (!_sSaveDir.empty())
lines.PushBack(CyString("SaveDir: ") + _sSaveDir);
lines.PushBack(CyString("ShipBuffer: ") + (long)m_iShipBuffer);
CyString wareBuffer = "WareBuffers:";
for ( int i = 0; i < WAREBUFFERS; i++ )
wareBuffer += CyString(" ") + (long)m_iWareBuffer[i];
lines.PushBack(wareBuffer);
for ( int i = 0; i < WAREBUFFERS; i++ )
{
if ( !m_lGameWares[i].size() )
continue;
lines.PushBack(CyString("Wares: ") + (long)i + " " + (long)m_lGameWares[i].size());
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *gm = node->Data();
lines.PushBack(CyString((long)gm->iPos) + " " + (long)gm->iType + " " + CyString((char)gm->cType) + " " + gm->sWareName);
}
}
for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
lines.PushBack(CyString("NonRemovedFile: ") + str->str);
if ( m_lGameShips.size() )
{
lines.PushBack(CyString("Ships: ") + (long)m_lGameShips.size());
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *gm = node->Data();
lines.PushBack(CyString((long)gm->iType) + " $#C:" + gm->sShipClass + " " + gm->sShipID);
}
}
// write created Files
if ( !m_lCreatedFiles.Empty() )
{
for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
lines.PushBack(CyString("CreatedFile: ") + node->str.Remove(m_sCurrentDir));
}
// write uninstall files
if ( !m_lUninstallFiles.empty() )
{
for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
{
C_File *uf = node->Data();
CyString uString = "Uninstall: ";
uString += CyString::Number((long)uf->GetCreationTime()) + " ";
uString += uf->GetFilename();
lines.PushBack(uString);
}
}
// write the original file data
_pOriginalFiles->writeData(&lines);
// write the global changes
if ( !m_lGlobals.Empty() )
{
for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
lines.PushBack(CyString("GlobalSetting: ") + str->str + ":" + str->data.Remove(";"));
}
// write the fake patch ordering
if ( !m_lFakePatchOrder.Empty() )
{
for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
lines.PushBack(CyString("FakePatchOrder: ") + str->str + ":" + str->data);
}
for(CListNode<SWarePriceOverride> *node = m_lWarePrices.Front(); node; node = node->next()) {
SWarePriceOverride *ware = node->Data();
switch(ware->type) {
case Ware_EMP:
lines.PushBack(CyString(Utils::String("EMPPriceOverride:") + (long)ware->pos + " " + (long)ware->relval));
if ( ware->bNotority )
lines.PushBack(CyString(Utils::String("EMPNotoOverride:") + (long)ware->pos + " " + (long)ware->notority));
break;
case Ware_Custom:
lines.PushBack(CyString(Utils::String("CustomWarePriceOverride:") + ware->id + ";" + (long)ware->relval));
if ( ware->bNotority )
lines.PushBack(CyString(Utils::String("CustomWareNotoOverride:") + ware->id + ";" + (long)ware->notority));
break;
case Ware_BuiltIn:
lines.PushBack(CyString(Utils::String("BuiltInWarePriceOverride:") + (long)ware->pos + " " + (long)ware->relval));
if ( ware->bNotority )
lines.PushBack(CyString(Utils::String("BuiltInWareNotoOverride:") + (long)ware->pos + " " + (long)ware->notority));
break;
}
}
// write the global file list
lines.PushBack("GlobalFiles:");
int num = 0;
for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
f->SetPos(num++);
CyString line = CyString::Number(f->GetFileType()) + ":" + CyString::Number((long)f->GetCreationTime()) + ":" + f->GetDir() + ":";
if ( f->IsShared() && !f->IsFakePatch() )
line += "1:";
else
line += "0:";
CyString filename = f->GetFilePointer();
filename.Remove(m_sCurrentDir);
if ( f->IsDisabled() )
line += "D#";
line += "G#";
line += (long)f->GetGame();
line += "#";
line += filename;
if ( !f->GetOriginalName().Empty() )
{
line += "O#";
line += f->GetOriginalName();
}
lines.PushBack(line);
}
// write the package list
for ( CListNode<CBaseFile> *pn = m_lPackages.Front(); pn; pn = pn->next() )
{
CBaseFile *package = pn->Data();
if ( package->GetType() == TYPE_SPK )
lines.PushBack("<script>");
else if ( package->GetType() == TYPE_XSP )
lines.PushBack("<ship>");
else if ( package->GetType() == TYPE_ARCHIVE )
lines.PushBack("<archive>");
else if ( package->GetType() == TYPE_BASE )
lines.PushBack("<base>");
else
continue;
if ( !package->filename().empty() )
lines.PushBack(CyString("Installspk: ") + package->filename());
CyString valuesline = package->CreateValuesLine();
if ( valuesline.Right(1) == '\n' )
valuesline.Truncate((int)valuesline.Length() - 1);
lines.PushBack(valuesline);
if ( !package->IsEnabled() )
lines.PushBack("Disabled");
if ( !package->IsModifiedEnabled() )
lines.PushBack("ModifiedDisabled");
if ( package->GetIcon() )
lines.PushBack(CyString("Icon: ") + package->GetIconExt() + " " + package->GetIcon()->GetFilePointer() );
CyString fileline("Files:");
for ( CListNode<C_File> *fn = package->GetFileList()->Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
fileline += " ";
fileline += CyString::Number(f->GetPos());
}
lines.PushBack(fileline);
}
lines.PushBack("</scripts>");
CFileIO datFile(m_sCurrentDir + "/PluginManager/PluginManager.new");
CDirIO Dir(m_sCurrentDir);
if ( !Dir.exists("PluginManager") ) {
CLog::log(CLog::Log_IO, 2, "Creating PluginManager directory");
Dir.Create("PluginManager");
}
CLog::log(CLog::Log_IO, 2, "Writing data file: " + m_sCurrentDir.ToString() + "/PluginManager/PluginManager.new");
if ( !datFile.WriteFile(&lines) )
{
CLog::log(CLog::Log_IO, 1, "ERROR: Failed to write data file");
}
else {
CLog::log(CLog::Log_IO, 2, "Removing old data file: " + m_sCurrentDir.ToString() + "/PluginManager/PluginManager.dat");
if ( !CFileIO::Exists(m_sCurrentDir.ToString() + "/PluginManager/PluginManager.dat") || CFileIO::Remove(m_sCurrentDir.ToString() + "/PluginManager/PluginManager.dat") ) {
CLog::log(CLog::Log_IO, 2, "Renaming data file: PluginManager.new => PluginManager.dat");
datFile.Rename(m_sCurrentDir + "/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, CyString args, CyStringList *errors)
{
if ( !errors )
return;
errors->PushBack(args, ERRORLOG_OLD(type));
}
/**
* Enable a package
*
* Enables all files in the package, unless they are already enabled, ie, used by other packages
* Backs up any original files before attempting to enable the file
*
* Fake patches are renamed to the next available slot
*
* Clears up the "PluginManager/Disabled" directory
*
* param: package - The package class to be removed
* param: errors - The string list for all the status for debugging, ie has an entry for whats happened to every file
* param: progress - The progress class, updates a progress screen of the derived class
*
* return: boolen - Returns true if the package enabling was successful
*/
bool CPackages::EnablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
{
ClearError();
// if already enabled, just skip
if ( package->IsEnabled() )
return true;
// check if parent is enabled
if ( package->GetParent() && !package->GetParent()->IsEnabled() )
{
m_iError = PKERR_NOPARENT;
return false;
}
// check for modified
if ( m_bVanilla && !package->IsSigned() )
{
m_iError = PKERR_MODIFIED;
return false;
}
if ( this->PrepareEnablePackage(package) )
return this->EnablePreparedPackages(errors, progress);
return false;
}
bool CPackages::EnablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *enabledPackages )
{
ClearError();
// get all files, including children
CLinkList<C_File> fileList;
int maxFiles = this->GetAllPackageFiles(&m_lEnableList, &fileList, false);
int fileCount = 0;
// we use this list to match cat and dat files
CyStringList fakePatches;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( f->GetGame() && m_iGame ) {
if ( f->GetGame() != m_iGame )
continue;
}
CBaseFile *package = NULL;
for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->IsFileAdded(f) )
{
package = p;
break;
}
}
if ( progress )
{
progress->UpdateProgress(fileCount++, maxFiles);
progress->UpdateFile(f);
}
// only move fiels that are disabled
if ( !f->IsDisabled() )
continue;
// make the directory if it dont exist
CDirIO Dir(m_sCurrentDir);
// fake patches are in the root, no need for directory
if ( !f->IsFakePatch() )
{
if ( !Dir.exists(f->getDirectory(package)) )
{
if ( !Dir.Create(f->getDirectory(package)) )
{
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, f->GetDirectory(package), errors);
continue;
}
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, f->GetDirectory(package), errors);
}
}
// check if theres an original file to backup
_pOriginalFiles->doBackup(f, errors);
CyString newFilename = f->GetNameDirectory(package);
// fake patches need to be renamed
if ( f->IsFakePatch() )
{
// first check if the matching file has been done already
SStringList *foundStr = fakePatches.FindString(f->GetBaseName());
// already done one, simply change this to match
if ( foundStr )
newFilename = foundStr->data + "." + f->GetFileExt();
// we need to find the next available number instead
else
{
CyString newPos = CyString::Number(FindNextFakePatch()).PadNumber(2);
newFilename = newPos + "." + f->GetFileExt();
// and wee need to push this onto the string list so we can adjust the matchign pair
fakePatches.PushBack(f->GetBaseName(), newPos);
}
}
// lets actually move the file back now
// !!error checking!!
CFileIO currentFile(f->GetFilePointer());
CFileIO newFile(m_sCurrentDir + "/" + newFilename);
if ( !currentFile.exists() )
{
// missing file ??
if ( !newFile.exists() )
{
this->AddLogEntry(SPKINSTALL_MISSINGFILE, newFilename, errors);
continue;
}
}
// remove existing file
// file exists, so lets try to move it
else
{
if ( newFile.exists() )
newFile.remove();
if ( !currentFile.Rename(newFile.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 + "/" + 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
CyStringList removeDirs;
removeDirs.PushBack(CyString("PluginManager/Disabled"));
RemoveUnusedDirectories(removeDirs, errors);
m_lEnableList.clear();
this->WriteData();
return true;
}
bool CPackages::DisablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *disabledPackages )
{
if ( progress )
progress->UpdateStatus(PROGRESS_DISABLEFILE);
UpdateUsedFiles(&m_lDisableList, false);
// checks if there are any original files to restore and if any fake patches were disabled to reshuffle
bool original = false, shuffle = false;
// holds our list of directories that we might need to remove, only empty ones from this list will actually be removed
CyStringList removeDirs;
// get all files, including children
CLinkList<C_File> fileList;
int maxFiles = this->GetAllPackageFiles(&m_lDisableList, &fileList, true);
// interate through all the files in the package
int fileCount = 0;
for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
{
C_File *f = node->Data();
CBaseFile *checkPackage = NULL;
for ( CListNode<CBaseFile> *node = m_lDisableList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->IsFileAdded(f) )
{
checkPackage = p;
break;
}
}
// update the progress count for the current file
if ( progress )
{
progress->UpdateProgress(++fileCount, maxFiles);
progress->UpdateFile(f);
}
// only delete files that are not used by any other enabled packages, counter from UpdateUsedFiles()
if ( f->GetUsed() || (f->IsShared() && !m_bDisableVanilla) )
continue;
// file is already disabled, no need to disable again
if ( f->IsDisabled() )
continue;
// readmes, uninstall and extra files dont need to be disabled
// Extra files not in the "Extras" directory could be anywhere, so these should be disabled incase they are game changing files, ie in "types"
if ( f->GetFileType() == FILETYPE_README || f->GetFileType() == FILETYPE_UNINSTALL || (f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(5).lower() == "Extra") )
continue;
// check if there is a matching uninstall file, ie there the script file is also an uninstall script file for a previously uninstalled package that has yet to be removed
if ( f->GetFileType() == FILETYPE_SCRIPT )
{
bool found = false;
for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
{
C_File *uFile = uNode->Data();
if ( uFile->GetFilename().Compare(f->GetFilename()) )
{
found = true;
break;
}
}
if ( found )
continue;
}
if ( f->GetFileType() == FILETYPE_MOD && !f->IsFakePatch() && f->CheckFileExt("cat") )
{
if ( f->GetBaseName().Compare(m_sSetMod) )
m_sSetMod = NullString;
}
// file is not being used by any enabled package
// set disabled and move to disabled directory
CDirIO Dir(m_sCurrentDir);
Utils::String newFilename = "PluginManager/Disabled/";
// fake patches have thier own special directory
if ( f->IsFakePatch() )
newFilename += "FakePatches";
// otherwise we put them in thier usual directory structure inside the disabled dir
else
newFilename += f->getDirectory(NULL);
// make sure the directory exists so we can move the file
if ( !Dir.exists(newFilename) )
{
// we couldn't create the directory for some reason, this is not good
if ( !Dir.Create(newFilename) )
{
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newFilename, errors);
continue;
}
this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, newFilename, errors);
}
// fake patches need a special directory and filename so they dont overright each other as thier filenames are always changes
// if a package with a fake patch is installed while another one is disabled, it will fill the gap left by the disabled one
// they will then end up with the same filename, if it gets disabled they would overright each other, so we change the filename to prevent that from happening
if ( f->IsFakePatch() )
{
// find package the fake patch belongs to
if ( checkPackage )
{
newFilename = Utils::String(m_sCurrentDir.ToString()) + "/PluginManager/Disabled/FakePatches/FakePatch_" + checkPackage->getNameValidFile() + "_" + checkPackage->author() + "_" + f->name();
shuffle = true;
}
}
else if ( f->isAutoTextFile() )
{
if ( checkPackage )
{
newFilename = Utils::String(m_sCurrentDir.ToString()) + "/PluginManager/Disabled/TextFiles/Text_" + checkPackage->getNameValidFile() + "_" + checkPackage->author() + "_" + f->name();
shuffle = true;
}
}
// otherwise we can just use the standard filename
else
newFilename = Utils::String(m_sCurrentDir.ToString()) + "/PluginManager/Disabled/" + f->GetNameDirectory(checkPackage).ToString();
// now to move the file by renameing it to its new location
// !!error checking!!
// check the file, if it doesn't exist, and exists as disabled, we should just adjust the setting instead of an error
CFileIO currentFile(f->GetFilePointer());
if ( !currentFile.exists() )
{
if ( !CFileIO(newFilename).exists() )
{
this->AddLogEntry(SPKINSTALL_MISSINGFILE, f->GetNameDirectory(checkPackage), errors);
continue;
}
}
// otherwise the file must exists, so lets move it
else if ( !currentFile.Rename(newFilename) )
{
this->AddLogEntry(SPKINSTALL_DISABLEFILE_FAIL, f->GetNameDirectory(checkPackage), errors);
continue;
}
// must have been fine
this->AddLogEntry(SPKINSTALL_DISABLEFILE, f->GetNameDirectory(checkPackage), errors);
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 )
removeDirs.PushBack(f->GetDirectory(checkPackage), NullString, true);
// change the filename
f->SetFilename(newFilename);
// finally mark the file as disabled so we know not to try to move it again
f->SetDisabled(true);
}
// a fake patch has been disabled, we need to reshuffle the rest to fill in any gaps
if ( shuffle )
{
if ( progress )
progress->UpdateStatus(PROGRESS_SHUFFLEFAKE);
ShuffleFakePatches(errors);
ShuffleTextFiles(errors);
}
// original files were restored, check to remove the original file directory if its now empty
if ( original )
removeDirs.PushBack(CyString("PluginManager/Original"));
// remove any empty directories that we might have left
if ( !removeDirs.Empty() )
RemoveUnusedDirectories(removeDirs, errors);
// finally mark the whole package as disabled
// recursive, we need to disable all children
for ( CBaseFile *child = m_lDisableList.First(); child; child = m_lDisableList.Next() )
{
if ( m_pEnabledMod == child )
m_pEnabledMod = NULL;
child->SetEnabled(false);
if ( disabledPackages )
disabledPackages->push_back(child);
}
// disabling has completed successfully, we hope
m_bDisableVanilla = false;
m_lDisableList.clear();
this->WriteData();
return true;
}
/**
* Disables the selected package
*
* Disables all enabled files that are not being used by any other enabled package
* Any files that are being used by other enabled packages are skipped and left enabled
*
* Original Files are restored when file is disabled
*
* Fake patches are shuffled to fill in any gaps caused by disabling fake patches
*
* All files go into the Plugin/Disabled directory into thier respective directories.
*
* Any directories left empty when disabling files are then removed
*
* param: package - Package file to be disabled
* param: errors - A string list used to add the status as it progresses, used in debugging output
* param: progress - The progress class, updates the progress of the current disabling. Needs a divered class to report the progress somewhere
*
* return: boolean, true if there was no errors, otherwise false
*/
bool CPackages::DisablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
{
// if already disabled, just skip
if ( !package->IsEnabled() )
return true;
m_lDisableList.clear();
if ( this->PrepareDisablePackage(package) )
return this->DisablePreparedPackages(errors, progress);
return false;
}
/**
* Find a Package
*
* Finds a matching package so we can find if one is already installed
*
* Uses seperate functions for each package type, ie for SPK and XSP packages
*/
CBaseFile *CPackages::FindPackage(CBaseFile *package)
{
// no point checking if we've been sent a null pointer
if ( !package )
return 0;
// we are checking against a SPK package, so we match the name and author
if ( package->GetType() == TYPE_SPK )
return FindSpkPackage(package->name(), package->author());
else if ( package->GetType() == TYPE_XSP )
return FindXspPackage(((CXspFile *)package)->GetShipID());
else if ( package->GetType() == TYPE_ARCHIVE )
return FindArchivePackage(package->name());
// nothing found obviously
return 0;
}
CBaseFile *CPackages::FindFirstPackageWithFile(C_File *f)
{
return FindNextPackageWithFile(NULL, f);
}
CBaseFile *CPackages::FindNextPackageWithFile(CBaseFile *p, C_File *f)
{
bool startCheck = (p) ? false : true;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( startCheck )
{
if ( node->Data()->IsFileAdded(f) )
return node->Data();
}
else if ( p == node->Data() )
startCheck = true;
}
return NULL;
}
/**
* Find a File
*
* Searches for a file matching the filetype and filename
* Optional dir is used for extras files
*/
C_File *CPackages::FindFile(int filetype, CyString filename, CyString dir)
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( f->GetFileType() != filetype )
continue;
if ( !f->GetFilename().Compare(filename) )
continue;
if ( !dir.Empty() && f->GetDir().Compare(dir) )
continue;
return f;
}
return NULL;
}
CArchiveFile *CPackages::FindArchivePackage(CyString name)
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// only look for archive packages
if ( file->GetType() != TYPE_ARCHIVE )
continue;
// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
if ( file->name().Compare(name.ToString()) )
return (CArchiveFile *)file;
}
// nothing found
return 0;
}
/**
* Find a SPK Package
*
* This searching all installed packages for a SPK Package matching the name and author
*/
CBaseFile *CPackages::FindSpkPackage(CyString name, CyString author)
{
return findSpkPackage(name.ToString(), author.ToString());
}
CBaseFile *CPackages::findSpkPackage(const Utils::String &name, const Utils::String &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(CyString name, CyString author)
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
if ( file->name().Compare(name.ToString()) && file->author().Compare(author.ToString()) )
return file;
}
// nothing found
return 0;
}
/**
* Find a XSP Package
*
* This searching all installed packages for a XSP Package matching the object id
*/
CBaseFile *CPackages::FindXspPackage(CyString id)
{
// interate through all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *file = node->Data();
// only look for spk packages
if ( file->GetType() != TYPE_XSP )
continue;
// now compare the id, "Compare" is a non case senseative check, opposed to ==.
if ( ((CXspFile *)file)->GetShipID().Compare(id.ToString()) )
return file;
}
// nothing found
return 0;
}
/**
* Update the used files count
*
* counts how many packages are currently using the file
*/
void CPackages::UpdateUsedFiles(CLinkList<CBaseFile> *ignoreList, bool includedisabled)
{
// clear amounts for all files
CListNode<C_File> *fnode = m_lFiles.Front();
while ( fnode )
{
fnode->Data()->ClearUsed();
fnode = fnode->next();
}
// update for all packages
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node= node->next() )
{
CBaseFile *file = node->Data();
if ( (ignoreList) && (ignoreList->FindData(file)) )
continue;
if ( !includedisabled && !file->IsEnabled() )
continue;
// now mark all files
CListNode<C_File> *fnode = file->GetFileList()->Front();
while ( fnode )
{
fnode->Data()->IncUsed();
fnode = fnode->next();
}
}
}
/**
* Removes all empty directories that might have been created
*/
void CPackages::RemoveUnusedDirectories(CyStringList &dirs, CyStringList *errors)
{
CDirIO Dir(m_sCurrentDir);
for ( SStringList *str = dirs.Head(); str; str = str->next )
{
CyString dir = str->str;
CyStringList removedDir;
if ( Dir.RemoveDir(dir, false, true, &removedDir) || !removedDir.Empty() )
{
for ( SStringList *node = removedDir.Head(); node; node = node->next )
{
CyString displayName = node->str;
displayName = displayName.Remove(m_sCurrentDir);
this->AddLogEntry(SPKINSTALL_REMOVEDIR, displayName, errors);
}
}
}
}
/**
* Update signed status
*
* Updates the signed status of all packages
*/
void CPackages::UpdateSigned()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( p->GetType() != TYPE_SPK )
continue;
((CSpkFile *)p)->UpdateSigned(false);
}
}
/**
* Check validity of package file
*
* Checks if there is a newer version of the package installed
*/
int CPackages::CheckInstallPackage(CBaseFile *package, int check)
{
if ( package->GetType() == TYPE_XSP && m_iGame == GAME_X2 )
return INSTALLCHECK_NOSHIP;
// search for an old version
CBaseFile *oldPackage = FindPackage(package);
// check versions are newer
if (oldPackage && (check & IC_OLDVERSION))
{
if ( oldPackage->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, m_sGameVersion, m_iGameVersion)) )
return INSTALLCHECK_WRONGVERSION;
}
}
// check for modified
if (m_bVanilla && (check & IC_MODIFIED))
{
if ( !package->IsSigned() )
return INSTALLCHECK_MODIFIED;
}
return INSTALLCHECK_OK;
}
bool CPackages::CheckOtherPackage(CBaseFile *package)
{
// check the need for another mod
if ( package->GetType() == TYPE_SPK )
{
CSpkFile *spk = (CSpkFile *)package;
if ( spk->IsAnotherMod() )
{
if ( !FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()) )
{
// check the install list
if ( m_lInstallList.empty() )
return false;
bool found = false;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
if ( spk->GetOtherName().Compare(node->Data()->name()) && spk->GetOtherAuthor().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)->GetOtherName(), ((CSpkFile *)p)->GetOtherAuthor());
if ( parent )
{
if ( !parent->IsEnabled() )
return false;
}
else
return false;
}
}
// check any dependacies
if ( p->AnyDependacies() )
{
for ( CListNode<SNeededLibrary> *dNode = p->GetNeededLibraries()->Front(); dNode; dNode = dNode->next() )
{
if ( dNode->Data()->sName.Compare("<package>") )
continue;
if ( !this->CheckInstalledDependacy(dNode->Data()->sName, dNode->Data()->sAuthor, dNode->Data()->sMinVersion, true, true) )
return false;
}
}
return true;
}
bool CPackages::CheckInstalledDependacy(CyString name, CyString author, CyString version, bool onlyEnabled, bool includePrepared)
{
bool ret = checkInstalledDependacy(name.ToString(), author.ToString(), version.ToString(), onlyEnabled, includePrepared);
m_lInstallList.RemoveEmpty();
return ret;
}
bool CPackages::checkInstalledDependacy(const Utils::String &name, const Utils::String &author, const Utils::String &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("<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare("<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare("<package>")) ? false : onlyEnabled, (nl->sName.Compare("<package>")) ? false : includePrepared))
{
bool found = true;
for (auto itr = packages.Front(); itr; itr = itr->next())
{
if (itr->Data()->name().Compare(nl->sName) && itr->Data()->author().Compare(nl->sAuthor))
{
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;
}
int CPackages::GetMissingDependacies(CBaseFile *p, Utils::CStringList *list, bool onlyEnabled, bool includePrepared)
{
int count = 0;
CLinkList<SNeededLibrary> *neededList = p->GetNeededLibraries();
if ( neededList )
{
for ( CListNode<SNeededLibrary> *node = neededList->Front(); node; node = node->next() )
{
SNeededLibrary *nl = node->Data();
if ( !CheckInstalledDependacy((nl->sName.Compare("<package>")) ? p->name() : nl->sName, (nl->sAuthor.Compare("<author>")) ? p->author() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare("<package>")) ? false : onlyEnabled, (nl->sName.Compare("<package>")) ? false : includePrepared) )
{
if ( list )
list->pushBack(((nl->sName.Compare("<package>")) ? p->name() : nl->sName) + "|" + nl->sMinVersion, (nl->sAuthor.Compare("<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->GetOtherName(), spk->GetOtherAuthor()) )
{
if ( includePrepared )
{
found = false;
for ( CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *checkP = pNode->Data();
if ( p->author().Compare(checkP->author()) && p->name().Compare(checkP->name()) )
{
found = true;
break;
}
}
}
else
found = false;
}
if ( !found )
{
if ( list )
list->pushBack(spk->GetOtherName(), spk->GetOtherAuthor());
++count;
}
}
}
return count;
}
int CPackages::CheckPreparedInstallRequired(CLinkList<CBaseFile> *list)
{
// loop through all packages
int count = 0;
for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
// no requirements found, remove from list
bool missingDep = false;
if ( !this->CheckOtherPackage(p) )
missingDep = true;
else if ( this->GetMissingDependacies(p, NULL, false, true) )
missingDep = true;
if ( missingDep )
{
if ( list )
{
node->ChangeData(NULL);
list->push_back(p);
}
else
node->DeleteData();
++count;
}
}
m_lInstallList.RemoveEmpty();
return count;
}
/**
* Remove uninstall file
*
* Removes a single uninstall file
*/
bool CPackages::RemoveUninstallFile(C_File *file, CyStringList *errors)
{
CFileIO fio(file->GetFilePointer());
if ( fio.exists() )
{
if ( fio.remove() ) {
this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE, file->GetNameDirectory(NULL), errors);
return true;
}
else if ( errors )
this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE_FAIL, file->GetNameDirectory(NULL), errors);
}
return false;
}
/**
* Remove uninstall scripts
*
* Removes any unused unisntall scripts
* Finds if any scripts are in the current package
*/
int CPackages::RemoveUninstallScripts(CyStringList *errors, CProgressInfo *progress)
{
if ( m_lUninstallFiles.empty() )
return 0;
UpdateUsedFiles();
int files = 0;
for ( CListNode<C_File> *node = m_lUninstallFiles.Back(); node; node = node->prev() )
{
if ( progress )
progress->UpdateProgress(files, m_lUninstallFiles.size());
files++;
C_File *file = node->Data();
// first check if there is a matching file
bool found = false;
for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
{
C_File *checkFile = fNode->Data();
if ( checkFile->GetFileType() != FILETYPE_SCRIPT )
continue;
if ( checkFile->GetFilename().Compare(file->GetFilename()) )
{
found = true;
break;
}
}
// not found a matching file, we can safetly remove it
if ( !found )
{
if ( RemoveUninstallFile(file) )
node->DeleteData();
}
}
m_lUninstallFiles.RemoveEmpty();
return files;
}
/**
* Remove unused shared file
*
* Removes a single file
*/
bool CPackages::RemoveSharedFile(C_File *file, CyStringList *errors)
{
CFileIO fio(file->GetFilePointer());
if ( fio.exists() )
{
if ( fio.remove() ) {
this->AddLogEntry(SPKINSTALL_SHARED, file->GetNameDirectory(NULL), errors);
delete file;
return true;
}
else if ( errors )
this->AddLogEntry(SPKINSTALL_SHARED_FAIL, file->GetNameDirectory(NULL), errors);
}
return false;
}
/**
* Remove Unused Shared Files
*
* Files that have been marked as shared will not be removed or disabled when the last package is removed
* This function will remove any that are no longer connected with packages
*
* Marked shared fiels are mainly used for library scripts that arn't always added to packages
*/
int CPackages::RemoveUnusedSharedFiles(CyStringList *errors, CProgressInfo *progress)
{
UpdateUsedFiles();
int files = 0;
int done = 0;
for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
{
if ( progress )
progress->UpdateProgress(files, m_lFiles.size());
files++;
C_File *file = node->Data();
// only do marked shared files
if ( !file->IsShared() )
continue;
// only do ones that are no longer needed
if ( file->GetUsed() )
continue;
if ( RemoveSharedFile(file, errors) )
++done;
node->ChangeData(NULL);
}
m_lFiles.RemoveEmpty();
return done;
}
/**
* Any Unused Shared
*
* Checks if theres any unused shared files available
*
* Any file thats marked as shared, and is no longer connected to any installed package
*/
bool CPackages::AnyUnusedShared()
{
UpdateUsedFiles();
for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
{
C_File *file = node->Data();
// only do marked shared files
if ( !file->IsShared() )
continue;
// only do ones that are no longer needed
if ( file->GetUsed() )
continue;
return true;
}
return false;
}
void CPackages::ShuffleTextFiles(CyStringList *errors)
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// only do files that are enabled
if ( f->IsDisabled() )
continue;
if ( f->GetFileType() != FILETYPE_TEXT )
continue;
// check if the file is an auto text file
if ( !f->isAutoTextFile() )
continue;
// we need to rename it
int current = findNextTextFile();
if ( current < f->GetTextFileID() )
{
CFileIO moveFile(f->filePointer());
Utils::String newName = SPK::FormatTextName(current, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT)) + "." + moveFile.extension();
if ( moveFile.Rename(m_sCurrentDir + "/t/" + newName) )
{
this->AddLogEntry(SPKINSTALL_AUTOTEXT, f->name() + "~" + newName, errors);
f->SetName(newName);
}
else
this->AddLogEntry(SPKINSTALL_AUTOTEXT_FAIL, f->name() + "~" + newName, errors);
}
}
}
/**
* Shuffle Fake Patches
*
* Rename the fake patches so they are always in sequence to be loaded into the game
*
* IE, when one is removed, the rest need to be shuffled down so theres no gaps
*/
void CPackages::ShuffleFakePatches(CyStringList *errors)
{
int check = FindNextFakePatch();
// lets make sure our order is correct
// find Lowest Fake Patch Installed
CLinkList<C_File> doneList;
int lowest = FindLowestFakePatchInstalled();
if ( check < lowest ) lowest = check; // gap at the beginning, lets use that
// lets define the order
CyStringList fakePatchOrder;
for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
fakePatchOrder.PushBack(str->str, str->data);
// add any other packages that need to be ordered
// we need to work out the correct order
CLinkList<CBaseFile> packages;
for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *p = pNode->Data();
// search for any fake patches in package
if ( !p->IsEnabled() ) continue;
if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
// must have an order define
if ( !p->AnyFakePatchOrder() ) continue;
// check if theres any already ordered (manual)
bool added = false;
for ( SStringList *sCheck = fakePatchOrder.Head(); sCheck; sCheck = sCheck->next )
{
if ( sCheck->str.Compare(CyString(p->name())) && sCheck->data.Compare(CyString(p->author())) )
{
added = true;
break;
}
}
if ( added ) continue;
bool anyFound = false;
for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
{
if ( !file->IsFakePatch() ) continue;
if ( !file->CheckFileExt("cat") ) continue;
if ( doneList.FindData(file) ) continue;
anyFound = true;
break;
}
// we have some fake patches that need to be shuffled
if ( anyFound )
packages.push_back(p);
}
// lets adjust the order (only if theres more than 1
if ( packages.size() > 1 )
{
CLinkList<CBaseFile> sortedPackages;
// first add all the packages that dont need to be installed after
for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *p = pNode->Data();
// we have a before and not after
if ( !p->GetFakePatchBeforeOrder().Empty() && p->GetFakePatchAfterOrder().Empty() )
{
// if we have to install before, check if any on the list
int earliestPos = -1;
bool notAdded = true;
for ( SStringList *str = p->GetFakePatchBeforeOrder().Head(); str; str = str->next )
{
int pos = 0;
for ( CListNode<CBaseFile> *sNode = sortedPackages.Front(); sNode; sNode = sNode->next() )
{
if ( str->str.Compare(CyString(sNode->Data()->name())) && str->data.Compare(CyString(sNode->Data()->author())) )
{
if ( earliestPos == -1 || pos < earliestPos )
earliestPos = pos;
break;
}
++pos;
}
}
if ( earliestPos > -1 )
sortedPackages.insert(earliestPos, p);
// otherwise just add it at the back
else
sortedPackages.push_back(p);
// remove from the list
pNode->ChangeData(NULL);
}
}
// now do the packages that have both before and after
packages.RemoveEmpty();
for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *p = pNode->Data();
if ( !p->GetFakePatchBeforeOrder().Empty() && !p->GetFakePatchAfterOrder().Empty() )
{
}
}
// add them onto the list
for ( CListNode<CBaseFile> *pNode = sortedPackages.Front(); pNode; pNode = pNode->next() )
fakePatchOrder.PushBack(CyString(pNode->Data()->name()), CyString(pNode->Data()->author()));
}
// now add to do list
for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
fakePatchOrder.PushBack(CyString(pNode->Data()->name()), CyString(pNode->Data()->author()));
for ( SStringList *str = fakePatchOrder.Head(); str; str = str->next )
{
CBaseFile *package = FindPackage(str->str, str->data);
if ( package )
{
// might have more than 1 fake patch for the file, so we'll need the lowest
while ( true )
{
C_File *lowestFile = NULL;
for ( C_File *file = package->GetFirstFile(FILETYPE_MOD); file; file = package->GetNextFile(file) )
{
if ( !file->IsFakePatch() ) continue;
if ( !file->CheckFileExt("cat") ) continue; // only do the cat file, we can shuffle the dat file to match later
if ( doneList.FindData(file) ) continue; // already done?
if ( !lowestFile )
lowestFile = file;
else
{
if ( file->GetBaseName().ToInt() < lowestFile->GetBaseName().ToInt() )
lowestFile = file;
}
}
if ( !lowestFile ) // no more files ?
break;
// check its filename, it might already be in the correct place
if ( lowestFile->GetBaseName().ToInt() != lowest )
{
// if the file already exists, we need to move it elsewhere
CyString nextName = CyString::Number(lowest).PadNumber(2);
if ( CFileIO(m_sCurrentDir + "/" + nextName + ".cat").ExistsOld() )
{
// find the file in our internal file list
C_File *moveFile = FindFile(FILETYPE_MOD, nextName + ".cat");
if ( !moveFile ) // must not have it in our list ? lets move to the next
{
lowest++;
continue;
}
// now we can move the the cat/dat file elsewhere, lets shuffle it to the highest free number
ShufflePatchTo(moveFile, FindLastFakePatch(), errors);
}
// space should be free, now lets shuffle it
ShufflePatchTo(lowestFile, lowest, errors);
}
doneList.push_back(lowestFile); // we've done this file now
lowest++; // move up the lowest ready for the next patch
}
}
}
// now lets shuffle the rest
// now find any packages with greater fake patchs and fill the gaps
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// already done?
if ( doneList.FindData(f) )
continue;
// only do files that are enabled
if ( f->IsDisabled() )
continue;
// check if the file is a fake patch
if ( !f->IsFakePatch() )
continue;
// we only want cat and dat files, all fake patchs should be, but incase theres an error in the package somewhere we can check
if ( !f->CheckFileExt("cat") )
continue;
// now lets check if its greater than our gap
int check = FindNextFakePatch();
int patchNum = f->GetFilename().GetToken(".", 1, 1).ToInt();
if ( patchNum <= check )
continue;
ShufflePatchTo(f, check, errors);
}
}
void CPackages::ShufflePatchTo(C_File *file, int to, CyStringList *errors)
{
// it is, we need to shift this to fill the gap
CyString newName = CyString::Number(to).PadNumber(2) + "." + file->GetFileExt();
// now rename the file
CFileIO moveFile(file->GetFilePointer());
if ( moveFile.Rename(m_sCurrentDir + "/" + newName) )
{
// display moveing
this->AddLogEntry(SPKINSTALL_FAKEPATCH, file->GetName() + "~" + newName, errors);
// now find the matching pairing if it exists
for ( CListNode<C_File> *node2 = m_lFiles.Front(); node2; node2 = node2->next() )
{
C_File *f2 = node2->Data();
if ( f2->IsDisabled() || !f2->IsFakePatch() || f2->CheckFileExt(file->GetFileExt()) )
continue;
// needs to be the same file
if ( f2->GetBaseName() != file->GetBaseName() )
continue;
CyString newName2 = CyString::Number(to).PadNumber(2) + "." + f2->GetFileExt();
CFileIO moveFile(f2->GetFilePointer());
if ( moveFile.Rename(m_sCurrentDir + "/" + newName2) )
{
this->AddLogEntry(SPKINSTALL_FAKEPATCH, f2->GetName() + "~" + newName2, errors);
f2->SetName(newName2);
}
else
this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->GetName() + "~" + newName2, errors);
}
// finally make sure the internal name matches the new one
file->SetName(newName);
}
else
this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, file->GetName() + "~" + newName, errors);
}
int CPackages::FindLowestFakePatchInstalled()
{
int lowest = 99;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() ) continue;
if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
// now get all fake patches
for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
{
if ( !file->IsFakePatch() ) continue;
if ( file->CheckFileExt("dat") ) continue;
int check = file->GetBaseName().ToInt();
if ( check < lowest )
lowest = check;
}
}
return lowest;
}
int CPackages::FindLastFakePatch(int start, CyString dir)
{
CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
int check = start;
while ( check > 0 )
{
Utils::String checkStr = Utils::String::PadNumber(check, 2);
// check if a cat file exists
if ( !Dir.exists(checkStr + ".cat") )
{
// it doen't, check if theres a dat file (incase of package error)
if ( !Dir.exists(checkStr + ".dat") )
break;
}
--check;
}
return check;
}
/**
* Find next fake patch
*
* Searching for the next gap in patches, starting with 01.cat to 99.cat
*/
int CPackages::FindNextFakePatch(int start, CyString dir)
{
CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
int check = start;
while ( check < 99 )
{
++check;
Utils::String checkStr = Utils::String::PadNumber(check, 2);
// check if a cat file exists
if ( !Dir.exists(checkStr + ".cat") )
{
// it doen't, check if theres a dat file (incase of package error)
if ( !Dir.exists(checkStr + ".dat") )
break;
}
}
return check;
}
/**
* Find next text file
*
* Searching for the next gap in automatic text files, start with 0004-LXXX or XX0004
*/
unsigned int CPackages::findNextTextFile(unsigned int start) const
{
return findNextTextFile(Utils::String::Null(), start);
}
unsigned int CPackages::findNextTextFile(const Utils::String &dir, unsigned int start) const
{
int check = start;
if ( check < 2 ) check = 2;
while ( check < 9999 )
{
++check;
Utils::String newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
// check the vfs
if ( m_pGameVFS.isFileAvailable("t/" + newFilename + ".pck") ) continue;
if ( m_pGameVFS.isFileAvailable("t/" + newFilename + ".xml") ) continue;
break;
}
return check;
}
int CPackages::FindLastTextFile(int start, CyString dir)
{
CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
Dir.cd("t");
int check = start;
while ( check < 9999 )
{
++check;
Utils::String newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
// check if a packed file exists
if ( !Dir.exists(newFilename + ".pck") )
{
// it doen't, check if theres an unpacked file
if ( !Dir.exists(newFilename + ".xml") )
break;
}
}
return check;
}
/**
* Read game language
*
* Reads the lang.dat file from the game directory for the language id
*/
void CPackages::ReadGameLanguage(bool force)
{
// check if lauguage is already set
if ( !force && m_iLanguage )
return;
CDirIO Dir(m_sCurrentDir);
// check for lang.dat file
if ( Dir.exists("lang.dat") )
{
CFileIO File(Dir.File("lang.dat"));
size_t size;
char *data = File.ReadToData(&size);
if ( data )
{
CyString str(data);
m_iLanguage = str.GetToken("\n", 1, 1).GetToken(" ", 1, 1).ToInt();
}
}
}
/**
* Create Language Text File
*
* Creates text files for all packages into the correct language
*/
void CPackages::CreateLanguageTextFiles(CyStringList *errors)
{
// no need to create them if theres no language to use
if ( !m_iLanguage )
return;
// find all text files
CyStringList ids;
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
// only do text fiels
if ( f->GetFileType() != FILETYPE_TEXT )
continue;
CyString id, lang;
if ( m_iGameFlags & EXEFLAG_TCTEXT )
{
id = f->GetBaseName().GetToken("-", 1, 1);
lang = f->GetBaseName().GetToken("-", 2, 2);
if ( lang.Empty() )
lang = "NULL";
else
lang = lang.Erase(0, 1); // remove the "L"
}
else
{
lang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
}
SStringList *strList = ids.FindString(id);
// not added, add a new one
if ( !strList )
strList = ids.PushBack(id, lang);
else
{
if ( strList->data.Empty() )
strList->data = lang;
else
{
strList->data += ":";
strList->data += lang;
}
}
}
// we should now have a list of all text files, we need to remove those that have a matching language
for ( SStringList *sNode = ids.Head(); sNode; sNode = sNode->next )
{
int size = 0;
CyString *data = sNode->data.SplitToken(':', &size);
// huh ? we shouldn't have this
if ( !size )
continue;
// lets search for a matching id
int useId = 0;
bool found = false;
for ( int i = 0; i < size; i++ )
{
if ( data[i] == "NULL" )
useId = -1;
else
{
int num = data[i].ToInt();
if ( num == m_iLanguage )
{
found = true;
useId = m_iLanguage;
break;
}
// prioities the text file to use, no language first, then 44, then 49, otherwise, we pick the first available
else if ( num == 44 && useId != -1)
useId = 44;
// if we have a german language, and its not yet found english or null
else if ( num == 49 && useId != 44 && useId != -1 )
useId = 49;
// if we have not found a valid language yet, we will use this one
else if ( !useId )
useId = num;
}
}
if ( found )
continue;
if ( !useId )
useId = data[0].ToInt();
if ( !useId )
useId = -1;
CLEANSPLIT(data, size);
RenameTextFile(sNode->str, useId, errors);
}
}
/**
* Rename a text file
*
* Creates a new text file and copies an existing one
*/
bool CPackages::RenameTextFile(CyString textid, int languageid, CyStringList *errors)
{
// lets check if the file already exists
Utils::String newFilename = SPK::FormatTextName(textid.ToInt(), m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
C_File *addFile = FindFile(FILETYPE_TEXT, newFilename + ".xml");
if ( !addFile )
addFile = FindFile(FILETYPE_TEXT, newFilename + ".pck");
// we have found the file, lets just add it to our scripts
if ( addFile )
{
AddTextFileToScripts(addFile, textid);
return true;
}
// first we need to find our text file
Utils::String filename;
if ( languageid != -1 )
filename = SPK::FormatTextName(textid.ToInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));
else
filename = textid.ToString();
C_File *textFile = FindFile(FILETYPE_TEXT, filename + ".xml");
if ( !textFile )
{
textFile = FindFile(FILETYPE_TEXT, filename + ".pck");
if ( !textFile )
return false;
}
// now lets create out new text file
CFileIO readFile(textFile->GetFilePointer());
std::vector<CyString> *lines = readFile.ReadLines();
// find the language id in the lines
std::vector<CyString> frontLines;
for(std::vector<CyString>::iterator it = lines->begin(); it != lines->end(); ++it)
{
CyString line = *it;
int pos = line.FindPos("<language id");
if ( pos != -1)
{
CyString newLine = "<language id=\"";
newLine += CyString::Number(m_iLanguage);
newLine += "\">";
newLine += line.GetToken(">", 2);
frontLines.insert(frontLines.begin(), newLine);
lines->erase(lines->begin(), ++it);
break;
}
frontLines.insert(frontLines.begin(), line);
}
for(std::vector<CyString>::iterator it = frontLines.begin(); it != frontLines.end(); ++it)
lines->insert(lines->begin(), *it);
addFile = new C_File(newFilename + ".xml");
addFile->SetFileType(FILETYPE_TEXT);
CFileIO writeFile(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
if ( writeFile.WriteFile(lines) )
{
this->AddLogEntry(SPKINSTALL_WRITEFILE, addFile->GetNameDirectory(NULL), errors);
addFile->SetFilename(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
// now we have the file wrriten, we need to add it to all scripts that need it
// first we add it to the global list
m_lFiles.push_back(addFile);
// now add it to the scripts
AddTextFileToScripts(addFile, textid);
}
else
{
this->AddLogEntry(SPKINSTALL_WRITEFILE_FAIL, addFile->GetNameDirectory(NULL), errors);
if ( lines )
delete lines;
return false;
}
if ( lines )
delete lines;
return true;
}
/**
* Add file to scripts
*
* Adds a file to all scripts that need it
*/
void CPackages::AddTextFileToScripts(C_File *file, CyString textid)
{
// now we need to find all scripts that have a matching file and add this to the list
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *package = node->Data();
// first make sure it doesn't already exist
if ( package->GetFileList()->FindData(file) )
continue;
bool found = false;
for ( CListNode<C_File> *fNode = package->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *tFile = fNode->Data();
if ( tFile->GetFileType() != FILETYPE_TEXT )
continue;
CyString id = tFile->GetBaseName().GetToken("-", 1, 1);
if ( id == textid )
{
found = true;
break;
}
}
if ( found )
package->GetFileList()->push_back(file);
}
}
bool CPackages::RemoveFile(C_File *file, CyStringList *errors)
{
if ( !file )
return true;
CyString remFileStr = file->GetFilePointer();
remFileStr.FindReplace(m_sCurrentDir, "");
if ( file->GetFilePointer().IsIn("::") ) {
CFileIO CatFile(file->GetFilePointer().GetToken("::", 1, 1));
if ( CatFile.exists() ) {
CCatFile cat;
if ( cat.open(CatFile.fullFilename(), this->getAddonDir(), CATREAD_DAT, false) == CATERR_NONE ) {
CyString fileName = file->GetFilePointer().GetToken("::", 2, 2);
if ( cat.FindData(fileName) ) {
if ( cat.removeFile(fileName.ToString()) ) {
this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
}
else {
this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
return false;
}
}
}
}
}
else {
CFileIO f(file->GetFilePointer());
if ( f.exists() )
{
if ( f.remove() ) this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
else if ( errors )
{
this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
return false;
}
else
return false;
}
}
return true;
}
int CPackages::RemoveAllPackages(CyStringList *errors, CProgressInfo *progress)
{
int files = 0;
// remove all files
int max = m_lFiles.size() + _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
*/
CyString CPackages::GetInstallBeforeText(CBaseFile *package)
{
return package->installText(m_iLanguage, true);
}
CyString CPackages::GetInstallAfterText(CBaseFile *package)
{
return package->installText(m_iLanguage, false);
}
CyString CPackages::GetUninstallBeforeText(CBaseFile *package)
{
return package->uninstallText(m_iLanguage, true);
}
CyString CPackages::GetUninstallAfterText(CBaseFile *package)
{
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::String CPackages::findDataDir(const Utils::String &dir, const Utils::String &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("Data/" + file))
return Dir.file("Data/" + file);
else if (Dir.exists("../" + file))
return Dir.file("../" + file);
else if (Dir.exists("../Data/" + file))
return Dir.file("../Data/" + file);
return Utils::String::Null();
}
void CPackages::startup(const Utils::String &dir, const Utils::String &tempDir, const Utils::String &myDoc)
{
this->setTempDirectory(tempDir);
this->setMyDocuments(myDoc);
// need to read the game exe versions
m_gameExe.Reset();
Utils::String exeFile = this->findDataDir(dir, "exe");
// if file exists, read it, otherwise, just add
if (!exeFile.empty() && CFileIO::Exists(exeFile))
m_gameExe.ReadFile(exeFile);
else
{
m_gameExe.ParseExe("x2.exe|0:5:NOSAVESUBDIR:HKCU/Software/EgoSoftware/X2/ModName:X2 The Threat:!GAMEDIR!:2:1604608!2150400:1.4 Artifical Life:1974272:1.5 Uplink");
m_gameExe.ParseExe("x3.exe|30:5:0:HKCU/Software/Egosoft/X3/ModName:X3 Reunion:Egosoft/X3:2:2347008:2.0 Bala Gi:2367488!2375680:2.5 Uplink");
m_gameExe.ParseExe("x3tc.exe|35:5:NO_XOR|TC_TEXT|MYDOCLOG:HKCU/Software/Egosoft/X3TC/ModName:X3 Terran Conflict:Egosoft/X3TC:3:1933464!1933520:2.0 Aldrin Expansion:-1:2.5 A New Home (Superbox):-1:3.0 Balance of Power");
m_gameExe.ParseExe("x3ap.exe|38:2:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3AP/ModName:X3 Albion Prelude:Egosoft/X3AP:addon!x3tc.exe:3:-1:2.0 The War Continues:-1:2.5 Operation Loose Ends:-1:3.0 Shady Business");
m_gameExe.ParseExe("x3fl.exe|39:3:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3FL/ModName:X3 Farnham's Legacy:Egosoft/X3FL:addon2!x3tc.exe:0");
}
}
void CPackages::startup(const Utils::String &dir, const Utils::String &tempDir, const Utils::String &myDoc, const Utils::String &mod)
{
startup(dir, tempDir, myDoc);
m_sSetMod = mod;
}
void CPackages::Startup(CyString dir, CyString tempDir, CyString myDoc, CyString mod)
{
startup(dir.ToString(), tempDir.ToString(), myDoc.ToString(), mod.ToString());
}
int CPackages::GetGameLanguage() const
{
return this->GetGameLanguage(m_sCurrentDir.ToString());
}
int CPackages::GetGameLanguage(const Utils::String &sDir) const
{
Utils::String dir = sDir;
if (dir.empty())
dir = m_sCurrentDir.ToString();
else
dir = this->getProperDir(dir);
CDirIO Dir(dir);
// check for lang.dat file
if ( Dir.exists("lang.dat") )
{
CFileIO File(Dir.file("lang.dat"));
size_t size;
char *data = File.ReadToData(&size);
if ( data )
{
Utils::String str(data);
return str.token("\n", 1).token(" ", 1).toLong();
}
}
return 0;
}
Utils::String CPackages::GetGameRunExe(const Utils::String &dir)
{
return m_gameExe.GetGameRunExe(dir);
}
Utils::String CPackages::GetGameRunExe()
{
return m_gameExe.GetGameRunExe(m_sCurrentDir.ToString());
}
CyString CPackages::GetGameNameFromType(int game)
{
return m_gameExe.GetGameNameFromType(game - 1);
}
Utils::String CPackages::getGameName() const
{
return getGameName(m_sCurrentDir.ToString());
}
Utils::String CPackages::getGameName(const Utils::String &dir) const
{
return m_gameExe.GetGameName(dir.empty() ? m_sCurrentDir.ToString() : dir);
}
CyString CPackages::GetGameName(CyString dir)
{
return m_gameExe.GetGameName((dir.Empty()) ? m_sCurrentDir.ToString() : dir.ToString());
}
CyString CPackages::GetProperDir(CyString dir)
{
return m_gameExe.GetProperDir((dir.Empty()) ? m_sCurrentDir.ToString() : dir.ToString());
}
CyString CPackages::GetAddonDir(CyString dir)
{
return m_gameExe.GetAddonDir((dir.Empty()) ? m_sCurrentDir.ToString() : dir.ToString());
}
Utils::String CPackages::getProperDir() const
{
return getProperDir(m_sCurrentDir.ToString());
}
Utils::String CPackages::getProperDir(const Utils::String &dir) const
{
return m_gameExe.GetProperDir((dir.empty()) ? m_sCurrentDir.ToString() : dir);
}
Utils::String CPackages::getAddonDir() const
{
return getAddonDir(m_sCurrentDir.ToString());
}
Utils::String CPackages::getAddonDir(const Utils::String &dir) const
{
return m_gameExe.GetAddonDir((dir.empty()) ? m_sCurrentDir.ToString() : dir);
}
int CPackages::GetGameAddons(Utils::CStringList &exes, const Utils::String &dir)
{
return m_gameExe.GetGameAddons((dir.empty()) ? m_sCurrentDir.ToString() : dir, exes);
}
int CPackages::GetGameAddons(Utils::CStringList &exes)
{
return m_gameExe.GetGameAddons(m_sCurrentDir.ToString(), exes);
}
CyString CPackages::GetGameTypesString(CBaseFile *package, bool includeVersion)
{
if ( !package->AnyGameCompatability() )
return NullString;
Utils::String sGames;
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( !sGames.empty() )
sGames += ", ";
sGames += m_gameExe.GetGameNameFromType(gNode->Data()->iGame - 1);
if ( includeVersion ) {
Utils::String version = m_gameExe.GetGameVersionFromType(gNode->Data()->iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
if ( !version.empty() ) {
sGames += " (";
sGames += version;
sGames += ")";
}
}
}
return sGames;
}
CyString CPackages::GetGameVersionString(CBaseFile *package)
{
for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
if ( gNode->Data()->iGame == m_iGame ) {
return m_gameExe.GetGameVersionFromType(m_iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
}
}
return NullString;
}
CyString CPackages::GetGameVersionFromType(int game, int version, CyString sVersion)
{
return m_gameExe.GetGameVersionFromType(game - 1, version - 1, sVersion.ToString());
}
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("PluginManager") )
++count;
}
return count;
}
int CPackages::CountPackages(int type, bool onlyEnabled)
{
int count = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( (type != TYPE_BASE) && (p->GetType() != type) )
continue;
if ( (onlyEnabled) && (!p->IsEnabled()) )
continue;
++count;
}
return count;
}
int CPackages::ExtractGameFile(CyString aFilename, CyString aTo, CyString dir, CyString addon)
{
// first check the enabled mod
if ( dir.Empty() )
dir = m_sCurrentDir;
CyString addonDir = addon;
if ( addonDir.Empty() ) addonDir = this->GetAddonDir(dir);
if ( m_pEnabledMod && m_pEnabledMod->AnyFileType(FILETYPE_MOD) )
{
for ( C_File *file = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); file; file = m_pEnabledMod->GetNextFile(file) )
{
if ( !file->CheckFileExt("cat") )
continue;
CCatFile catFile;
if ( catFile.open(file->filePointer(), addonDir.ToString(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
{
if ( catFile.ExtractFile(aFilename, aTo) )
return 1;
if ( catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR )
{
if ( catFile.ExtractFile(aFilename) )
return -1;
}
}
}
}
else if ( !m_sSetMod.Empty() )
{
if ( CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").ExistsOld() )
{
CCatFile catFile;
if (catFile.open((m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").ToString(), addonDir.ToString(), CATREAD_CATDECRYPT, false) == CATERR_NONE)
{
if ( catFile.ExtractFile(aFilename, aTo) )
return 1;
if ( catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR )
{
if ( catFile.ExtractFile(aFilename) )
return -1;
}
}
}
}
// find the highest cat number
int catNumber = 1;
while ( CFileIO(dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat").ExistsOld() && catNumber < 99 )
++catNumber;
// get the last one, not the next free one
--catNumber;
// work backwards until we find the file
for ( ; catNumber; catNumber-- )
{
CCatFile catFile;
if (catFile.open((dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat").ToString(), addonDir.ToString(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
{
// check for the file
if ( catFile.ExtractFile(aFilename, aTo) )
return 1;
if ( catFile.error() == CATERR_INVALIDDEST || catFile.error() == CATERR_CANTCREATEDIR )
{
if ( catFile.ExtractFile(aFilename) )
return -1;
}
}
}
return 0;
}
void CPackages::CreateWareFiles()
{
// do each ware
int wareTextID = WARETEXTSTART;
for ( int i = 0; i < WAREBUFFERS; i++ )
{
// its empty, no need to create ware files
if ( !m_lGameWares[i].size() )
continue;
// lets extract the ware file
char wareType = CPackages::ConvertWareTypeBack(i);
Utils::String wareFile = "TWare";
wareFile += (char)UPPER(wareType);
CyString openFile;
int e;
if ( i == WARES_TECH && CFileIO::Exists(m_sTempDir + "/TWareT.txt") )
openFile = m_sTempDir + "/TWareT.txt";
else {
e = ExtractGameFile("types/" + wareFile + ".pck", m_sTempDir + "/" + wareFile + ".txt");
if ( e == 1 )
openFile = m_sTempDir + "/" + wareFile + ".txt";
else if ( e == -2 )
openFile = wareFile + ".txt";
}
if ( !openFile.Empty() )
{
// read the file into memory
CyStringList wareLines;
int oldSize = -1;
int version = -1;
// read first number
CFileIO readFile(m_sTempDir + "/" + wareFile + ".txt");
CyStringList *lines = readFile.ReadLinesStr();
if ( lines )
{
for ( SStringList *str = lines->Head(); str; str = str->next )
{
CyString line(str->str);
line.RemoveFirstSpace();
line.RemoveChar(9);
line.RemoveChar('\r');
if ( line.Empty() )
continue;
if ( line[0] == '/' )
continue;
if ( oldSize == -1 )
{
version = line.GetToken(";", 1, 1).ToInt();
oldSize = line.GetToken(";", 2, 2).ToInt();
}
else
{
line.RemoveEndSpace();
if ( line.Right(1) != ';' )
line += ";";
wareLines.PushBack(line);
if ( wareLines.Count() >= oldSize )
break;
}
}
delete lines;
// apply the buffer
if ( m_iWareBuffer[i] <= 0 )
m_iWareBuffer[i] = wareLines.Count() + 10;
// last resort, readjust the buffer
else if ( wareLines.Count() > m_iWareBuffer[i] )
m_iWareBuffer[i] = wareLines.Count();
// add the buffers
while ( wareLines.Count() < m_iWareBuffer[i] )
wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
// add the ware lines
bool create = false;
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->iType == WARETYPE_NONE )
continue;
else if ( w->iType == WARETYPE_DISABLED )
{
create = true;
wareLines.PushBack(CyString("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;") + w->sWareName + "_DISABLED;");
}
else if ( w->iType == WARETYPE_DELETED || !w->pWare )
{
create = true;
wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_DELETED;");
}
else if ( w->iType == WARETYPE_ADDED )
{
create = true;
w->pWare->iDescID = wareTextID;
w->iText = wareTextID;
wareTextID += 10;
w->iPos = wareLines.Count();
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(CyString("28;0;0;0;0;") + (long)wareLines.Count() + ";" + (long)(w->iText + 3) + ";" + (long)w->pWare->iVolumn + ";" + price + ";1;1;" + (long)w->pWare->iSize + ";" + price + ";" + (long)notority + ";0;0;" + w->sWareName.ToUpper() + ";");
}
}
if ( create )
{
wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
CyString strV;
strV.FromFloat(GetLibraryVersion(), 2);
wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
if ( readFile.WriteFile(&wareLines) )
this->PackFile(&readFile, CyString("types\\") + wareFile + ".pck");
}
}
readFile.remove();
}
}
}
Utils::String CPackages::empWaresForGame(int *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::String::Null();
}
void CPackages::_addWareOverride(enum WareTypes type, int pos, const Utils::String &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::String::Null(), price, false);
}
void CPackages::addEMPNotoOverride(int empId, int noto)
{
_addWareOverride(Ware_EMP, empId, Utils::String::Null(), noto, true);
}
void CPackages::addBuiltInWarePriceOverride(int empId, int price)
{
_addWareOverride(Ware_BuiltIn, empId, Utils::String::Null(), price, false);
}
void CPackages::addBuiltInWareNotoOverride(int empId, int noto)
{
_addWareOverride(Ware_BuiltIn, empId, Utils::String::Null(), noto, false);
}
void CPackages::addCustomWarePriceOverride(const Utils::String &id, int price)
{
_addWareOverride(Ware_Custom, 0, id, price, false);
}
void CPackages::addCustomWareNotoOverride(const Utils::String &id, int noto)
{
_addWareOverride(Ware_Custom, 0, id, noto, true);
}
void CPackages::CreateEMPFile(CyString progDir)
{
// do emp wares
int maxsize = 0;
Utils::String empWares = empWaresForGame(&maxsize);
if ( maxsize )
{
int e = ExtractGameFile("types/TWareT.pck", m_sTempDir + "/TWareT.txt");
if ( e )
{
// read the file into memory
CyStringList wareLines;
int oldSize = -1;
int version = -1;
// read first number
CFileIO readFile((e == -1) ? "TWareT.txt" : m_sTempDir + "/TWareT.txt");
std::vector<Utils::String> *lines = readFile.readLines();
if ( lines )
{
for ( int i = 0; i < (int)lines->size(); i++ )
{
Utils::String line(lines->at(i));
line.removeFirstSpace();
line.removeChar('\r');
line.removeChar(9);
if ( line[0] == '/' )
continue;
if ( oldSize == -1 )
{
version = line.token(";", 1).toLong();
oldSize = line.token(";", 2).toLong();
}
else
{
line.removeEndSpace();
if ( line.right(1) != ";" )
line += ";";
// 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(";", 9, Utils::String::Number(price));
line = line.replaceToken(";", 13, Utils::String::Number(price));
}
int noto = 0;
if ( this->builtInWareOverideNoto(pos, ¬o) ) {
line = line.replaceToken(";", 14, Utils::String::Number(noto));
}
}
// check for any override values for EMP
if ( i >= 116 ) {
int price = this->empOveridePrice(i - 116);
if ( price ) {
line = line.replaceToken(";", 9, Utils::String::Number(price));
line = line.replaceToken(";", 13, Utils::String::Number(price));
}
int noto = 0;
if ( this->empOverideNoto(i - 116, ¬o) ) {
line = line.replaceToken(";", 14, Utils::String::Number(noto));
}
}
wareLines.PushBack(CyString(line));
if ( wareLines.Count() >= oldSize )
break;
}
}
delete lines;
}
// now we too add/remove entries to match
// need filler entries
while ( wareLines.Count() < maxsize )
wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
int empEntries = 0;
Utils::String *empStr = empWares.tokenise("\n", &empEntries);
if ( empEntries && empStr )
{
// apply any price overrides
for(int i = 0; i < empEntries; i++) {
int price = this->empOveridePrice(i);
if ( price ) {
empStr[i] = empStr[i].replaceToken(";", 9, Utils::String::Number(price));
empStr[i] = empStr[i].replaceToken(";", 13, Utils::String::Number(price));
}
int noto = 0;
if ( this->empOverideNoto(i, ¬o) ) {
empStr[i] = empStr[i].replaceToken(";", 14, Utils::String::Number(noto));
}
}
// remove any empty end entries
while ( empStr[empEntries - 1].empty() )
--empEntries;
CyStringList addAfter;
if ( wareLines.Count() > maxsize )
{
// force emp, remove entries to allow them to be added
if ( m_bForceEMP )
{
// more after emp
if ( wareLines.Count() > (maxsize + empEntries) )
{
SStringList *node = wareLines.GetAt(maxsize + empEntries);
while ( node )
{
addAfter.PushBack(node->str);
node = node->next;
}
}
// no remove them all
wareLines.DeleteFrom(maxsize);
}
else if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL) // check if old emp is included, and convert it
{
if ( wareLines.Count() > 128 )
{
CyString test = wareLines.GetAt(128)->str;
if ( test.GetToken(";", -2).Compare("SS_WARE_SW_CUSTOM16_1;") )
{
// if theres any at the end, remove the last emp entry
if ( wareLines.Count() > (maxsize + empEntries - 1) )
{
SStringList *node = wareLines.GetAt(maxsize + empEntries - 2);
if ( node )
node->remove = true;
}
wareLines.Insert(128, "0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;128;");
wareLines.RemoveMarked();
}
}
}
}
// too many entries, need to remove the first set of EMP wares
int i = 0;
if ( wareLines.Count() > maxsize )
i = wareLines.Count() - maxsize;
for ( ; i < empEntries; i++ )
{
CyString str = empStr[i];
str.RemoveEndSpace();
str.RemoveChar(9);
str.RemoveChar('\r');
if ( str.Empty() )
continue;
if ( str.Right(1) != ";" )
str += ";";
wareLines.PushBack(str);
}
for ( SStringList *afterNode = addAfter.Head(); afterNode; afterNode = afterNode->next )
wareLines.PushBack(afterNode->str);
// finally we write the whole file
wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
CyString strV;
strV.FromFloat(GetLibraryVersion(), 2);
wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
if ( readFile.WriteFile(&wareLines) )
this->PackFile(&readFile, "types\\TWareT.pck");
}
CLEANSPLIT(empStr, empEntries);
}
}
}
Utils::String parseXmlText(const Utils::String &str)
{
Utils::String newStr(str);
CyStringList changes;
// find all XML commands, &<command>;
Utils::String sStr = str;
Utils::String::size_type pos = sStr.find_first_of("&", 0);
while ( pos != Utils::String::npos ) {
// find the next space and next ;. If ; comes first, assume its acommand
Utils::String::size_type spacePos = sStr.find_first_of(" ", pos);
Utils::String::size_type colonPos = sStr.find_first_of(";", pos);
if ( colonPos != Utils::String::npos && colonPos < spacePos ) {
// replace with <::command::> so they the & doesn't get replaced
Utils::String repStr = sStr.substr(pos, (colonPos + 1) - pos);
Utils::String repWithStr = "<::" + sStr.substr(pos + 1, colonPos - pos - 1) + "::>";
newStr = newStr.findReplace(repStr, repWithStr);
changes.PushBack(CyString(repStr), CyString(repWithStr));
}
// find the next command
pos = sStr.find_first_of("&", pos + 1);
}
// replace the & now
newStr = newStr.findReplace("&", "&");
// restore the commands
for ( SStringList *strNode = changes.Head(); strNode; strNode = strNode->next ) {
newStr = newStr.findReplace(strNode->data.ToString(), strNode->str.ToString());
}
return newStr;
}
Utils::String CPackages::ConvertTextString(const Utils::String &sText)
{
//process any &
Utils::String text = parseXmlText(sText);
// change special cases
text = text.findReplace("(", "\\(");
text = text.findReplace(")", "\\)");
text = text.findReplace("[", "{");
text = text.findReplace("]", "}");
text = text.findReplace(">", ">");
text = text.findReplace("<", "<");
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("t") )
Dir.Create("t");
CyString filename = SPK::FormatTextName(PMTEXTFILE, lang, (m_iGameFlags & EXEFLAG_TCTEXT));
CFileIO textFile(m_sCurrentDir + "/t/" + filename + ".xml");
std::vector<CyString> writeData;
writeData.push_back(CyString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"));
writeData.push_back(CyString("<language id=\"") + CyString::Number(lang) + "\">");
if ( !gameNumber )
writeData.push_back(CyString(" <page id=\"") + CyString::Number(PMTEXTFILE) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
else
writeData.push_back(CyString(" <page id=\"") + (long)gameNumber + CyString::Number(PMTEXTFILE).PadNumber(4) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
writeData.push_back(CyString(" <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(CyString(" <t id=\"99999\">2</t>"));
writeData.push_back(CyString(" </page>"));
writeData.push_back(CyString("</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 + "/t/" + filename + ".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("t") )
Dir.Create("t");
m_iLastUpdated = (int)time(NULL);
CyString filename = SPK::FormatTextName(PMTEXTFILE, lang, (m_iGameFlags & EXEFLAG_TCTEXT));
CFileIO textFile(m_sCurrentDir + "/t/" + filename + ".xml");
std::vector<CyString> writeData;
CLinkList<SGameWare> lWares;
CLinkList<SGameShip> lShips;
for ( int i = 0; i < WAREBUFFERS; i++ )
{
for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->iType == WARETYPE_NONE )
continue;
lWares.push_back(w);
}
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( node->Data()->iType == WARETYPE_NONE )
continue;
lShips.push_back(node->Data());
}
writeData.push_back(CyString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"));
writeData.push_back(CyString("<language id=\"") + CyString::Number(lang) + "\">");
if ( !gameNumber )
writeData.push_back(CyString(" <page id=\"") + CyString::Number(PMTEXTFILE) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
else
writeData.push_back(CyString(" <page id=\"") + (long)gameNumber + CyString::Number(PMTEXTFILE).PadNumber(4) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
// write the heading
int start = 10000;
writeData.push_back(CyString(" <t id=\"1\">") + CyString::Number(m_iLastUpdated) + "</t>");
writeData.push_back(CyString(" <t id=\"2\">") + CyString::Number(this->CountPackages(TYPE_SPK, true)) + "</t>");
writeData.push_back(CyString(" <t id=\"3\">") + CyString::Number(start) + "</t>");
writeData.push_back(CyString(" <t id=\"4\">") + CyString::Number(lWares.size()) + "</t>");
writeData.push_back(CyString(" <t id=\"6\">") + CyString::Number(lShips.size()) + "</t>");
// write some generic texts
writeData.push_back(CyString(" <t id=\"110\">") + (long)m_iLanguage + "</t>");
writeData.push_back(CyString(" <t id=\"109\">Plugin Manager: \\033GPoll Gui Data\\033X</t>"));
writeData.push_back(CyString(" <t id=\"107\">Plugin Manager: \\033GExport Game Data\\033X </t>"));
writeData.push_back(CyString(" <t id=\"100\">\\n</t>"));
writeData.push_back(CyString(" <t id=\"101\">\\033B</t>"));
writeData.push_back(CyString(" <t id=\"102\">\\033G</t>"));
writeData.push_back(CyString(" <t id=\"103\">\\033B</t>"));
writeData.push_back(CyString(" <t id=\"104\">\\033X</t>"));
writeData.push_back(CyString(" <t id=\"105\">\\033Y</t>"));
writeData.push_back(CyString(" <t id=\"106\">\\033C</t>"));
writeData.push_back(CyString(" <t id=\"108\">\\033</t>"));
writeData.push_back(CyString(" <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(CyString(" <t id=\"99999\">1</t>"));
// now write each package
int settingStart = 100000;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( !p->IsEnabled() )
continue;
if ( p->GetType() != TYPE_SPK )
continue;
CSpkFile *spk = (CSpkFile *)p;
// count text files
CyString textEntries;
int textCount = 0;
C_File *f = p->GetFirstFile(FILETYPE_TEXT);
while ( f )
{
CyString sLang;
CyString id;
if ( m_iGameFlags & EXEFLAG_TCTEXT )
{
id = f->GetBaseName().GetToken("-", 1, 1);
sLang = f->GetBaseName().GetToken("-", 2, 2);
if ( sLang.Empty() )
sLang = "NULL";
else
sLang = sLang.Erase(0, 1); // remove the "L"
}
else
{
sLang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
}
if ( sLang != "NULL" )
{
if ( sLang.ToInt() == lang )
{
++textCount;
if ( !textEntries.Empty() )
textEntries += " ";
textEntries += id;
}
}
f = p->GetNextFile(f);
}
CyString sTextCount((long)textCount);
writeData.push_back(CyString(" <t id=\"") + (long)start + "\">" + this->ConvertTextString(p->name()) + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 1) + "\">" + this->ConvertTextString(p->author()) + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 2) + "\">" + this->ConvertTextString(p->version()) + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 3) + "\">" + this->ConvertTextString(p->GetLanguageName(lang).ToString()) + "</t>");
CLinkList<SSettingType> *settings = spk->GetSettingsList();
if ( settings && settings->size() )
{
writeData.push_back(CyString(" <t id=\"") + (long)(start + 4) + "\">" + (long)settings->size() + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 5) + "\">" + (long)settingStart + "</t>");
for ( CListNode<SSettingType> *sNode = settings->Front(); sNode; sNode = sNode->next() )
{
SSettingType *st = sNode->Data();
writeData.push_back(CyString(" <t id=\"") + (long)(settingStart++) + "\">" + st->sKey + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(settingStart++) + "\">" + spk->GetSetting(st) + "</t>");
}
}
else
writeData.push_back(CyString(" <t id=\"") + (long)(start + 4) + "\">0</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 6) + "\">" + sTextCount + "</t>");
if ( textCount )
writeData.push_back(CyString(" <t id=\"") + (long)(start + 7) + "\">" + textEntries + "</t>");
start += 10;
}
// write ware names
if ( lWares.size() )
{
writeData.push_back(CyString(" <t id=\"5\">") + CyString::Number(start) + "</t>");
for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( w->pWare && w->iType == WARETYPE_ADDED )
writeData.push_back(CyString(" <t id=\"") + (long)start + "\">" + this->ConvertTextString(w->sWareName.ToString()) + "</t>");
else
writeData.push_back(CyString(" <t id=\"") + (long)start + "\">-1</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 1) + "\">" + CyString((char)w->cType) + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 2) + "\">" + (long)w->iPos + "</t>");
start += 10;
}
}
if ( lShips.size() )
{
writeData.push_back(CyString(" <t id=\"7\">") + CyString::Number(start) + "</t>");
for ( CListNode<SGameShip> *node = lShips.Front(); node; node = node->next() )
{
SGameShip *gs = node->Data();
if ( gs->iType == WARETYPE_NONE )
continue;
if ( gs->pPackage && gs->iType == WARETYPE_ADDED )
writeData.push_back(CyString(" <t id=\"") + (long)start + "\">" + gs->sShipID + "</t>");
else
writeData.push_back(CyString(" <t id=\"") + (long)start + "\">-1</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 1) + "\">" + (long)gs->iPos + "</t>");
writeData.push_back(CyString(" <t id=\"") + (long)(start + 2) + "\">Ship</t>");
// write shipyard info
if ( gs->pPackage )
{
int doStart = start + 5;
for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
{
writeData.push_back(CyString(" <t id=\"") + (long)(doStart) + "\">" + ((gs->pPackage->IsShipyard(i)) ? CyString::Number(1) : CyString::Number(0)) + "</t>");
++doStart;
}
}
start += 20;
}
}
writeData.push_back(CyString(" </page>"));
// wares
if ( m_iGame == GAME_X3AP || m_iGame == GAME_X3TC || m_iGame == GAME_X3FL || m_iGame == GAME_X3 || lWares.size() || lShips.size() )
{
if ( !gameNumber )
writeData.push_back(CyString(" <page id=\"17\" title=\"Plugin Manager Objects\">"));
else
writeData.push_back(CyString(" <page id=\"") + (long)gameNumber + "0017\" title=\"Plugin Manager Objects\">");
writeData.push_back(CyString(" <t id=\"") + (long)(SHIPSTARTTEXT - 1) + "\">ZZ_BLANKSHIP</t>");
// do emp
if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3 || m_iGame == GAME_X3AP || m_iGame == GAME_X3FL)
writeData.push_back(GetEMPText());
// object names
for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
{
SGameWare *w = node->Data();
if ( !w->pWare || w->iType != WARETYPE_ADDED )
continue;
// find the correct text for the language
Utils::String name = CSpkFile::GetWareText(w->pWare, m_iLanguage);
Utils::String desc = CSpkFile::GetWareDesc(w->pWare, m_iLanguage);
if ( !name.empty() )
writeData.push_back(CyString(" <t id=\"") + (long)(w->iText + 3) + "\">" + this->ConvertTextString(name) + "</t>");
if ( !desc.empty() )
writeData.push_back(CyString(" <t id=\"") + (long)(w->iText + 4) + "\">" + this->ConvertTextString(desc) + "</t>");
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( !s->pPackage || s->iType != WARETYPE_ADDED )
continue;
if ( s->pPackage->GetOriginalDescription() )
continue;
Utils::String name = s->pPackage->GetTextName(m_iLanguage);
Utils::String desc = s->pPackage->GetTextDescription(m_iLanguage);
if ( !name.empty() )
writeData.push_back(CyString(" <t id=\"") + (long)s->iText + "\">" + this->ConvertTextString(name) + "</t>");
if ( !desc.empty() )
writeData.push_back(CyString(" <t id=\"") + (long)(s->iText + 1) + "\">" + this->ConvertTextString(desc) + "</t>");
}
writeData.push_back(CyString(" </page>"));
}
writeData.push_back(CyString("</language>"));
textFile.WriteFileUTF(&writeData);
size_t fileSize;
char *fileData = CFileIO(textFile.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 + "/t/" + filename + ".pck");
pckFile.WriteData((char *)pckData, newFileSize);
this->AddCreatedFile(pckFile.fullFilename());
}
}
textFile.remove();
}
bool CPackages::isCurrentDir(const Utils::String &dir) const
{
Utils::String cur = m_sCurrentDir.ToString();
if ( dir.Compare(cur) )
return true;
Utils::String checkDir = cur;
checkDir = checkDir.findReplace("/", "\\");
if ( checkDir.Compare(dir) )
return true;
checkDir = checkDir.findReplace("\\", "/");
if ( checkDir.Compare(dir) )
return true;
return false;
}
void CPackages::backupSaves(bool vanilla)
{
if (!_sSaveDir.empty())
{
// copy any saves into the vanilla directory
Utils::String dir = (vanilla) ? "Vanilla" : "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::CStringList files;
if(saveDir.dirList(files, Utils::String::Null(), "*.sav"))
{
for(auto itr = files.begin(); itr != files.end(); ++itr)
{
CFileIO File(saveDir.file((*itr)->str));
if (!File.CheckFileExtension("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::String dir = (vanilla) ? "Vanilla" : "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) {
CyStringList *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::CStringList files;
if(restoreDir.dirList(files, Utils::String::Null(), "*.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("PluginManager", true, true, 0);
Dir.RemoveDir("dds", false, true, 0);
Dir.RemoveDir("objects", false, true, 0);
Dir.RemoveDir("types", false, true, 0);
Dir.RemoveDir("textures", false, true, 0);
// remove the plugin manager mod files
if ( Dir.exists("mods/PluginManager.cat") ) CFileIO::Remove(Dir.file("mods/PluginManager.cat"));
if ( Dir.exists("mods/PluginManager.dat") ) CFileIO::Remove(Dir.file("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("types/Dummies.pck", m_sTempDir + "/Dummies.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? "Dummies.txt" : m_sTempDir + "/Dummies.txt") )
{
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
int insection = 0;
SDummyEntry *currentSection = NULL;
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
line.RemoveChar(9);
line.RemoveChar('\r');
line.RemoveFirstSpace();
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 )
{
CyString section = line.GetToken(";", 1, 1);
insection = line.GetToken(";", 2, 2).ToInt();
// search for the sections
currentSection = NULL;
for ( CListNode<SDummyEntry> *node = dummyList.Front(); node; node = node->next() )
{
SDummyEntry *d = node->Data();
if ( d->sSection.Compare(section) )
{
currentSection = node->Data();
break;
}
}
if ( !currentSection )
{
currentSection = new struct SDummyEntry;
currentSection->sSection = section;
dummyList.push_back(currentSection);
}
// we have some more ?
line = line.DelToken(";", 1, 2);
}
else
{
--insection;
// check the last entry for number of states
if ( currentSection->sSection.Compare("SDTYPE_GUN") )
{
int states = line.GetToken(";", 3, 3).ToInt();
int parts = line.GetToken(";", 4 + (states * 2), 4 + (states * 2)).ToInt();
CyString data = line.GetToken(";", 1, 4 + (states * 2) + (parts * 2)) + ";";
currentSection->lEntries.PushBack(data);
// remove done
line = line.DelToken(";", 1, 4 + (states * 2) + (parts * 2));
}
else
{
int states = line.GetToken(";", 3, 3).ToInt();
CyString data = line.GetToken(";", 1, 3 + (states * 2)) + ";";
currentSection->lEntries.PushBack(data);
// remove done
line = line.DelToken(";", 1, 3 + (states * 2));
}
}
}
}
delete lines;
}
File.remove();
}
// add the new entries for the ships
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyDummies() )
continue;
// add each dummy to list
for ( CListNode<SDummy> *dNode = s->pPackage->GetDummies()->Front(); dNode; dNode = dNode->next() )
{
SDummy *dummy = dNode->Data();
SDummyEntry *found = NULL;
for ( CListNode<SDummyEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
{
if ( eNode->Data()->sSection.Compare(CyString(dummy->sSection)) )
{
found = eNode->Data();
break;
}
}
if ( !found )
{
found = new SDummyEntry;
found->sSection = dummy->sSection;
dummyList.push_back(found);
}
// check if its already on the list
else
{
bool f = false;
for ( SStringList *strNode = found->lEntries.Head(); strNode; strNode = strNode->next )
{
if ( strNode->str.GetToken(";", 1, 1).Compare(CyString(dummy->sData.token(";", 1))) )
{
f = true;
break;
}
}
if ( f )
continue;
}
found->lEntries.PushBack(CyString(dummy->sData));
}
}
// finally, write the file
std::vector<CyString> lines;
lines.push_back(CyString("// Dummies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
for ( SDummyEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
{
lines.push_back("");
lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.Count());
lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.Count()) + ";");
for ( SStringList *str = dummy->lEntries.Head(); str; str = str->next )
{
CyString strLine = str->str;
strLine.RemoveChar(9);
strLine.RemoveChar('\r');
strLine.RemoveEndSpace();
strLine.RemoveFirstSpace();
strLine = strLine.FindReplace("<::PiPe::>", "|");
if ( strLine.Right(1) != ";" )
strLine += ";";
lines.push_back(strLine);
}
}
lines.push_back("");
// write the file to disk
CFileIO WriteFile(m_sTempDir + "/dummies.txt");
if ( WriteFile.WriteFile(&lines) )
{
this->PackFile(&WriteFile, "types\\dummies.pck");
WriteFile.remove();
}
}
}
void CPackages::CreateCutData()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyCutData() )
continue;
found = true;
break;
}
if ( !found )
return;
CyStringList cutList;
int e = ExtractGameFile("types/CutData.pck", m_sTempDir + "/CutData.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? "CutData.txt" : m_sTempDir + "/CutData.txt") )
{
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
int entries = -1;
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
line.RemoveChar(9);
line.RemoveChar('\r');
line.RemoveChar(' ');
if ( line.Empty() || line[0] == '/' )
continue;
if ( entries == -1 )
entries = line.GetToken(";", 1, 1).ToInt();
else
{
if ( line.Right(1) != ";" )
line += ";";
cutList.PushBack(line);
if ( cutList.Count() == entries )
break;
}
}
delete lines;
}
File.remove();
}
}
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyCutData() )
continue;
// add each dummy to list
for ( SStringList *strNode = s->pPackage->GetCutData()->Head(); strNode; strNode = strNode->next )
{
CyString str = strNode->str;
str.RemoveChar(' ');
if ( str.Right(1) != ";" )
str += ";";
cutList.PushBack(str, true);
}
}
cutList.PushFront(CyString::Number(cutList.Count()) + ";");
cutList.PushFront("/cut id;filename (leave blank to use id)");
cutList.PushFront(CyString("// Cut Data file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
// write the file to disk
CFileIO WriteFile(m_sTempDir + "/CutData.txt");
if ( WriteFile.WriteFile(&cutList) )
{
this->PackFile(&WriteFile, "types\\CutData.pck");
WriteFile.remove();
}
}
void CPackages::CreateAnimations()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyAnimations() )
continue;
found = true;
break;
}
if ( !found )
return;
CyStringList aniList;
int e = ExtractGameFile("types/Animations.pck", m_sTempDir + "/Animations.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? "Animations.txt" : m_sTempDir + "/Animations.txt") )
{
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
aniList.PushBack(line);
}
delete lines;
}
File.remove();
}
}
CyStringList parsedAniList;
CXspFile::ReadAnimations(&aniList, &parsedAniList, 0);
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyAnimations() )
continue;
// add each dummy to list
for ( SStringList *strNode = s->pPackage->GetAnimations()->Head(); strNode; strNode = strNode->next )
parsedAniList.PushBack(strNode->str);
}
// format the list with added spaces
CyStringList formatedAniList;
int lineCount = -1;
for ( SStringList *strNode = parsedAniList.Head(); strNode; strNode = strNode->next )
{
// format the comment to match the line number
lineCount++;
CyString oldComment = strNode->str.GetToken("//", 2);
CyString comment = CyString("//") + CyString::Number(lineCount);
if ( !oldComment.Empty() )
{
comment += " ";
oldComment.RemoveFirstSpace();
if ( oldComment.GetToken(" ", 1, 1).IsNumber() )
comment += oldComment.GetToken(" ", 2);
else
comment += oldComment;
}
CyString line = strNode->str.GetToken("//", 1, 1);
// split into seperate lines
CyString first = line.GetToken(";", 1, 1);
if ( first.Compare("TAT_TAGSINGLESTEP") )
{
formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
int max;
CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
if ( max && sLines )
{
if ( sLines[max - 1].Empty() )
--max; // remove the last ";"
for ( int i = 0; i < max; i++ )
{
CyString l = CyString("\t") + sLines[i] + ";";
if ( i == (max - 1) )
formatedAniList.PushBack(l + comment);
else
formatedAniList.PushBack(l);
}
}
CLEANSPLIT(sLines, max);
}
else if ( (first.Compare("TAT_TAGONESHOT") || first.Compare("TAT_TAGLOOP")) && (line.IsIn("TATF_COORDS")) )
{
formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
int max;
CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
if ( max && sLines )
{
if ( sLines[max - 1].Empty() )
--max; // remove the last ";"
CyString prevLine;
for ( int i = 0; i < max; i++ )
{
CyString l = sLines[i] + ";";
if ( l.IsIn("TATF_COORDS") && !prevLine.Empty() )
{
formatedAniList.PushBack(CyString("\t") + prevLine);
prevLine = "";
}
prevLine += l;
}
if ( !prevLine.Empty() )
formatedAniList.PushBack(CyString("\t") + prevLine + comment);
}
CLEANSPLIT(sLines, max);
}
else
formatedAniList.PushBack(line + comment);
}
formatedAniList.PushFront(CyString::Number(parsedAniList.Count()) + ";");
formatedAniList.PushFront(CyString("// Animations, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
// write the file to disk
CFileIO WriteFile(m_sTempDir + "/Animations.txt");
if ( WriteFile.WriteFile(&formatedAniList) )
{
this->PackFile(&WriteFile, "types\\Animations.pck");
WriteFile.remove();
}
}
void CPackages::CreateBodies()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
return;
bool found = false;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyBodies() )
continue;
found = true;
break;
}
if ( !found )
return;
// lets read our current bodies file
CLinkList<SBodies> bodiesList;
SBodies *currentSection = NULL;
int e = ExtractGameFile("types/Bodies.pck", m_sTempDir + "/Bodies.txt");
if ( e )
{
CFileIO File;
if ( File.open((e == -1) ? "Bodies.txt" : m_sTempDir + "/Bodies.txt") )
{
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
int entries = 0;
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
line.RemoveChar(' ');
line.RemoveChar(9);
if ( line.Empty() || line[0] == '/' )
continue;
if ( entries <= 0 )
{
entries = line.GetToken(";", 2, 2).ToInt();
currentSection = new SBodies;
currentSection->sSection = line.GetToken(";", 1, 1);
bodiesList.push_back(currentSection);
}
else if ( currentSection )
{
int num;
CyString *strs = line.SplitToken(";", &num);
if ( num && strs )
{
for ( int i = 0; i < num; i++ )
{
if ( strs[i].Empty() )
continue;
currentSection->lEntries.PushBack(strs[i] + ";", true);
--entries;
}
}
CLEANSPLIT(strs, num);
}
}
delete lines;
}
File.remove();
}
}
// lets now add any new entries
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyBodies() )
continue;
// add each dummy to list
for ( SStringList *strNode = s->pPackage->GetBodies()->Head(); strNode; strNode = strNode->next )
{
CyString section = strNode->str.GetToken(";", 1, 1);
CyString body = strNode->str.GetToken(";", 2).Remove(' ');
if ( body.Right(1) != ";" )
body += ";";
// find the section to add into
SBodies *foundSection = NULL;
for ( CListNode<SBodies> *checkBody = bodiesList.Front(); checkBody; checkBody = checkBody->next() )
{
if ( checkBody->Data()->sSection.Compare(section) )
{
foundSection = checkBody->Data();
break;
}
}
if ( !foundSection )
{
foundSection = new SBodies;
foundSection->sSection = section;
bodiesList.push_back(foundSection);
}
foundSection->lEntries.PushBack(body, true);
}
}
// now write the file
CyStringList writeList;
// the header first
writeList.PushBack(CyString("// Bodies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
writeList.PushBack("//body type;num bodies;");
writeList.PushBack("//[body id/name]");
// now our sections
for ( SBodies *bSection = bodiesList.First(); bSection; bSection = bodiesList.Next() )
{
writeList.PushBack("");
writeList.PushBack(CyString("// Section: ") + bSection->sSection);
writeList.PushBack(bSection->sSection + ";" + CyString::Number(bSection->lEntries.Count()) + ";");
for ( SStringList *strNode = bSection->lEntries.Head(); strNode; strNode = strNode->next )
{
CyString str = strNode->str;
str.RemoveChar(9);
str.RemoveChar(' ');
if ( str.Right(1) != ";" )
str += ";";
writeList.PushBack(str);
}
}
// write the file to disk
CFileIO WriteFile(m_sTempDir + "/Bodies.txt");
if ( WriteFile.WriteFile(&writeList) )
{
this->PackFile(&WriteFile, "types\\Bodies.pck");
WriteFile.remove();
}
}
void CPackages::CreateCustomStarts()
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
// find all spk files (only ones that can be custom starts
if ( node->Data()->GetType() != TYPE_SPK )
continue;
CSpkFile *p = (CSpkFile *)node->Data();
// only use custom starts
if ( !p->IsCustomStart() )
continue;
// get the name of the start to use
CyString name = p->GetCustomStartName();
if ( name.Empty() )
continue;
// find if maps file exists
CyStringList createFiles;
createFiles.PushBack(name, "maps/x3_universe");
createFiles.PushBack(name, "types/Jobs");
createFiles.PushBack(name, "types/JobWings");
for ( SStringList *str = createFiles.Head(); str; str = str->next )
{
Utils::String dir = CFileIO(str->data).dir();
int type = FILETYPE_EXTRA;
if ( dir.Compare("maps") )
type = FILETYPE_MAP;
if ( !p->FindFile(str->str + ".xml", type) && !p->FindFile(str->str + ".pck", type) )
{
// create a maps files
int e = this->ExtractGameFile(str->data + ".pck", m_sTempDir + "/" + str->data.ToString() + ".pck");
if ( e )
{
CFileIO File((e == -1) ? (str->data + ".pck") : (m_sTempDir + "/" + str->data.ToString() + ".pck"));
if ( File.exists() )
{
File.Rename(m_sCurrentDir + "/" + dir + "/" + str->str + ".pck");
this->AddCreatedFile(dir + "/" + str->str.ToString() + ".pck");
}
}
}
}
}
}
void CPackages::AddCreatedFile(CyString file)
{
file = file.Remove(m_sCurrentDir);
while ( file[0] == '/' )
file.Erase(0, 1);
while ( file[0] == '\\' )
file.Erase(0, 1);
m_lCreatedFiles.PushBack(file, true);
}
void CPackages::CreateComponants()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
return;
CLinkList<SComponantEntry> dummyList;
// now extract the existing dummies
int e = ExtractGameFile("types/Components.pck", m_sTempDir + "/Components.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? "Components.txt" : m_sTempDir + "/Components.txt") )
{
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
int insection = 0;
int insubsection = 0;
SComponantEntry *currentSection = NULL;
SComponantEntry2 *currentSubSection = NULL;
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
if ( line[0] == '/' )
continue;
line.RemoveChar('\r');
line = line.RemoveFirstSpace();
line = line.RemoveEndSpace();
if ( line.Empty() )
continue;
// read the section, first entry is section, second is size
while ( !line.Empty() )
{
line = line.RemoveFirstSpace();
if ( line.Empty() )
break;
if ( !insection && !insubsection )
{
CyString section = line.GetToken(";", 1, 1);
insection = line.GetToken(";", 2, 2).ToInt();
// search for the sections
currentSection = NULL;
for ( CListNode<SComponantEntry> *node = dummyList.Front(); node; node = node->next() )
{
SComponantEntry *d = node->Data();
if ( d->sSection.Compare(section) )
{
currentSection = node->Data();
break;
}
}
if ( !currentSection )
{
currentSection = new SComponantEntry;
currentSection->sSection = section;
dummyList.push_back(currentSection);
}
// we have some more ?
line = line.DelToken(";", 1, 2);
}
else if ( !insubsection )
{
--insection;
CyString section = line.GetToken(";", 1, 1);
insubsection = line.GetToken(";", 2, 2).ToInt();
currentSubSection = new SComponantEntry2;
currentSubSection->sSection = section;
currentSection->lEntries.push_back(currentSubSection);
line = line.DelToken(";", 1, 2);
}
else
{
--insubsection;
currentSubSection->lEntries.PushBack(line.Remove(' '), true);
line = "";
}
}
}
delete lines;
}
File.remove();
}
// add the new entries for the ships
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
// no dummies to add?
if ( !s->pPackage->AnyComponents() )
continue;
// add each dummy to list
for ( CListNode<SComponent> *dNode = s->pPackage->GetComponents()->Front(); dNode; dNode = dNode->next() )
{
SComponent *dummy = dNode->Data();
SComponantEntry *found = NULL;
SComponantEntry2 *found2 = NULL;
for ( CListNode<SComponantEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
{
if ( eNode->Data()->sSection.Compare(CyString(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(CyString(dummy->sSection2)) )
{
found2 = cNode->Data();
break;
}
}
if ( !found2 )
{
found2 = new SComponantEntry2;
found2->sSection = dummy->sSection2;
found->lEntries.push_back(found2);
}
else
{
bool f = false;
for ( SStringList *strNode = found2->lEntries.Head(); strNode; strNode = strNode->next )
{
if ( dummy->sData.remove(' ').Compare(strNode->str.ToString()) )
{
f = true;
break;
}
}
if ( f )
continue;
}
}
found2->lEntries.PushBack(CyString(dummy->sData.remove(' ')));
}
}
// finally, write the file
std::vector<CyString> lines;
lines.push_back(CyString("// Components file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
for ( SComponantEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
{
lines.push_back("");
lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.size());
lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.size()) + ";");
for ( CListNode<SComponantEntry2> *comp = dummy->lEntries.Front(); comp; comp = comp->next() )
{
lines.push_back(comp->Data()->sSection + ";" + (long)comp->Data()->lEntries.Count() + ";");
for ( SStringList *str = comp->Data()->lEntries.Head(); str; str = str->next )
{
CyString cStr = str->str;
cStr.RemoveEndSpace();
cStr.RemoveChar(9);
cStr.RemoveChar('\r');
if ( cStr.Right(1) != ";" )
cStr += ";";
lines.push_back(cStr);
}
}
}
// write the file to disk
CFileIO WriteFile(m_sTempDir + "/Components.txt");
if ( WriteFile.WriteFile(&lines) )
{
this->PackFile(&WriteFile, "types\\Components.pck");
WriteFile.remove();
}
}
}
bool CPackages::readWares(int iLang, CLinkList<SWareEntry> &list)
{
if ( iLang == 0 ) iLang = m_iLanguage;
Utils::String 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::String &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::String &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::String &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::String::Null());
}
bool CPackages::empOverideNoto(int id, int *noto)
{
return _wareNotoOverride(Ware_EMP, id, Utils::String::Null(), noto);
}
int CPackages::builtInWareOveridePrice(int id)
{
return _warePriceOverride(Ware_BuiltIn, id, Utils::String::Null());
}
bool CPackages::builtInWareOverideNoto(int id, int *noto)
{
return _wareNotoOverride(Ware_BuiltIn, id, Utils::String::Null(), noto);
}
int CPackages::customWareOveridePrice(const Utils::String &id)
{
return _warePriceOverride(Ware_Custom, 0, id);
}
bool CPackages::customWareOverideNoto(const Utils::String &id, int *noto)
{
return _wareNotoOverride(Ware_Custom, 0, id, noto);
}
void CPackages::removeEmpOverride(int pos)
{
_removeWareOverride(Ware_EMP, pos, Utils::String::Null());
}
void CPackages::removeBuiltinWareOverride(int pos)
{
_removeWareOverride(Ware_BuiltIn, pos, Utils::String::Null());
}
void CPackages::removeCustomWareOverride(const Utils::String &id)
{
_removeWareOverride(Ware_Custom, 0, id);
}
bool CPackages::ReadGlobals(CyStringList &globals)
{
int e = ExtractGameFile("types/Globals.pck", m_sTempDir);
if ( e )
{
CFileIO File((e == -1) ? "Globals.txt" : m_sTempDir + "/Globals.txt");
if ( File.exists() )
{
CyStringList *lines = File.ReadLinesStr();
if ( lines )
{
int entries = -1;
for ( SStringList *str = lines->Head(); str; str = str->next )
{
str->str.RemoveChar('\r');
str->str.RemoveChar(9);
str->str.RemoveFirstSpace();
if ( str->str.Empty() )
continue;
if ( str->str[0] == '/' )
continue;
// remove comments
CyString l = str->str;
if ( l.IsIn("/") )
l = l.GetToken("/", 1, 1);
if ( entries == -1 )
entries = l.GetToken(";", 1, 1).ToInt();
else
globals.PushBack(l.GetToken(";", 1, 1), l.GetToken(";", 2, 2));
}
delete lines;
return true;
}
}
}
return false;
}
void CPackages::CreateGlobals()
{
if ( m_lGlobals.Empty() )
return; // no global settings
CyStringList globals;
if ( ReadGlobals(globals) )
{
// apply out settings
for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
{
SStringList *found = globals.FindString(str->str);
if ( found )
found->data = str->data;
}
// now write it
CyStringList writeList;
for ( SStringList *str = globals.Head(); str; str = str->next )
writeList.PushBack(str->str + ";" + str->data + ";");
// finally, write the file
writeList.PushFront(CyString::Number(writeList.Count()) + "; /globals amount", "");
writeList.PushFront(CyString("// Globals file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
CFileIO WriteFile(m_sTempDir + "/Globals.txt");
if ( WriteFile.WriteFile(&writeList) )
{
this->PackFile(&WriteFile, "types/Globals.pck");
WriteFile.remove();
}
}
}
void CPackages::CreateTShips()
{
// no ships ?
if ( m_lGameShips.empty() )
return;
// get the cockpit list to match with ships turrets
CyStringList Cockpits;
CyStringList *cockpitList = this->CreateCockpits();
if ( cockpitList )
{
for ( SStringList *str = cockpitList->Head(); str; str = str->next )
{
CyString id = str->str.GetToken(";", 19, 19);
Cockpits.PushBack(id);
}
delete cockpitList;
}
CLinkList<SGameShip> shipOverrides;
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
if ( node->Data()->iType != WARETYPE_ADDED || !node->Data()->pPackage )
continue;
if ( !node->Data()->pPackage->IsExistingShip() )
continue;
shipOverrides.push_back(node->Data());
}
// read the existing tships file
int e = ExtractGameFile("types/TShips.pck", m_sTempDir + "/TShips.txt");
if ( e )
{
int fileType = 51;
CyStringList tshipsList;
// if we have no buffer, lets create one
CFileIO File;
if ( File.open((e == -1) ? "TShips.txt" : m_sTempDir + "/TShips.txt") )
{
int shiptext = SHIPSTARTTEXT;
std::vector<CyString> *lines = File.ReadLines();
if ( lines )
{
int count = -1;
for ( int j = 0; j < (int)lines->size(); j++ )
{
CyString line(lines->at(j));
if ( line[0] == '/' )
continue;
line.RemoveChar('\r');
line.RemoveChar(9);
line = line.RemoveFirstSpace();
line = line.RemoveEndSpace();
if ( line.Empty() )
continue;
if ( count == -1 )
{
fileType = line.GetToken(";", 1, 1).ToInt();
count = line.GetToken(";", 2, 2).ToInt();
}
else
{
if ( line.Right(1) != ";" )
line += ";";
// check for any ship overrides
bool added = false;
if ( !shipOverrides.empty() )
{
CShipData shipData;
if ( shipData.ReadShipData(line) )
{
for ( CListNode<SGameShip> *node = shipOverrides.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( !s->pPackage->GetShipID().Compare(shipData.sID.ToString()) )
continue;
s->iText = shiptext;
if ( !s->pPackage->GetOriginalDescription() )
shiptext += 2;
s->iPos = tshipsList.Count();
added = true;
tshipsList.PushBack(CyString(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame)));
shipOverrides.remove(node);
break;
}
}
}
if ( !added )
tshipsList.PushBack(line);
--count;
if ( count < 0 )
break;
}
}
delete lines;
}
File.remove();
// assign the ship buffer
if ( !m_iShipBuffer )
m_iShipBuffer = tshipsList.Count() + 15;
// there seems to be too many additional entries, we have no choise but to change the buffer
else if ( m_iShipBuffer <= tshipsList.Count() )
m_iShipBuffer = tshipsList.Count() + 15;
CyString bufferStart;
if ( m_iGame == GAME_X3 )
bufferStart = "0;0;0;0;0;2;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;2092;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
else
bufferStart = "0;0;0;0;0;SG_SH_M5;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;OBJ_BEACON;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
// add the buffers now
for ( int i = tshipsList.Count(); i < m_iShipBuffer; i++ )
tshipsList.PushBack(bufferStart + "SHIP_BUFFER;");
// now lets add our tships line
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->pPackage && s->pPackage->IsExistingShip() )
continue;
s->iPos = tshipsList.Count();
if ( s->iType == WARETYPE_ADDED && s->pPackage )
{
s->iText = shiptext;
if ( !s->pPackage->GetOriginalDescription() )
shiptext += 2;
tshipsList.PushBack(CyString(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame)));
}
else if ( s->iType == WARETYPE_DELETED )
tshipsList.PushBack(bufferStart + "SHIP_DELETED;");
else if ( s->iType == WARETYPE_DISABLED )
tshipsList.PushBack(bufferStart + "SHIP_DISABLED;");
else
tshipsList.PushBack(bufferStart + "SHIP_SPACER;");
}
// finally, write the file
tshipsList.PushFront(CyString::Number(fileType) + ";" + CyString::Number(tshipsList.Count()) + ";", "");
tshipsList.PushFront(CyString("// TShips file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
CFileIO WriteFile(m_sTempDir + "/TShips.txt");
if ( WriteFile.WriteFile(&tshipsList) )
{
this->PackFile(&WriteFile, "types/TShips.pck");
WriteFile.remove();
}
}
}
}
bool CPackages::PackFile(CyString filename)
{
// compress the file
CFileIO File(filename);
size_t fileSize;
char *fileData = File.ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
CyString ext = "pck";
if ( File.CheckFileExtension("bob") )
ext = "pbb";
else if ( File.CheckFileExtension("bod") )
ext = "pbd";
CFileIO pckFile(File.ChangeFileExtension(ext));
if ( !CDirIO(pckFile.dir()).exists() )
CDirIO(pckFile.dir()).Create();
pckFile.WriteData((char *)pckData, newFileSize);
return true;
}
}
return false;
}
bool CPackages::UnPackFile(CyString filename, bool checkxml)
{
// compress the file
CFileIO File(filename);
size_t fileSize;
char *fileData = File.ReadToData(&fileSize);
if ( fileData && fileSize)
{
size_t newFileSize;
unsigned char *pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, false);
if ( !pckData )
pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
if ( pckData )
{
CyString ext = "txt";
if ( File.CheckFileExtension("pbb") )
ext = "bob";
else if ( File.CheckFileExtension("pbd") )
ext = "bod";
CFileIO pckFile(File.ChangeFileExtension(ext));
if ( !CDirIO(pckFile.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 {
CyString line = pckFile.readEndOfLine();
if ( line.IsIn("<language id=") )
{
isxml = true;
break;
}
else if ( line.IsIn("<page id=") )
{
isxml = true;
break;
}
else if ( line.IsIn("<?xml") || line.IsIn("<script>") )
{
isxml = true;
break;
}
--readmaxlines;
if ( readmaxlines <= 0 )
break;
} while (pckFile.isOpened());
if ( pckFile.isOpened() )
pckFile.close();
if ( isxml )
pckFile.Rename(pckFile.ChangeFileExtension("xml"));
}
return true;
}
}
return false;
}
bool CPackages::PackFile(CFileIO *File, CyString filename)
{
filename = filename.FindReplace("\\", "/");
if ( m_iGame == GAME_X3 )
{
CCatFile catFile;
int error = catFile.open((m_sCurrentDir + "/mods/PluginManager.cat").ToString(), 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.ToString(), 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 + "/" + filename);
if ( !CDirIO(pckFile.dir()).exists() )
CDirIO(pckFile.dir()).Create();
pckFile.WriteData((char *)pckData, newFileSize);
this->AddCreatedFile(pckFile.fullFilename());
return true;
}
}
}
return false;
}
CyStringList *CPackages::CreateCockpits()
{
// first check we have any ships
if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
return NULL;
CyStringList *cockpitList = new CyStringList;
// now extract the existing cockpits
int fileType = 51;
int e = ExtractGameFile("types/TCockpits.pck", m_sTempDir + "/TCockpits.txt");
if ( e )
{
// read the dummies
CFileIO File;
if ( File.open((e == -1) ? "TCockpits.txt" : m_sTempDir + "/TCockpits.txt") )
{
CyStringList *lines = File.ReadLinesStr();
if ( lines )
{
int count = -1;
for ( SStringList *str = lines->Head(); str; str = str->next )
{
CyString line(str->str);
line.RemoveChar('\r');
line.RemoveChar(9);
line = line.RemoveFirstSpace();
line = line.RemoveEndSpace();
if ( line.Empty() )
continue;
if ( line[0] == '/' )
continue;
if ( count == -1 )
{
fileType = line.GetToken(";", 1, 1).ToInt();
count = line.GetToken(";", 2, 2).ToInt();
}
else
{
while ( !line.Empty() )
{
CyString data = line.GetToken(";", 1, 19);
cockpitList->PushBack(data + ";");
line = line.DelToken(";", 1, 19);
--count;
if ( count < 1 )
break;
}
}
}
delete lines;
}
File.remove();
}
// now add the new ones
for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
{
SGameShip *s = node->Data();
if ( s->iType != WARETYPE_ADDED || !s->pPackage )
continue;
if ( !s->pPackage->AnyCockpits() )
continue;
for ( CListNode<SCockpit> *cn = s->pPackage->GetCockpits()->Front(); cn; cn = cn->next() )
{
bool foundEntry = false;
CyString cockpitStr = cn->Data()->sCockpit;
// search for matching game entry
for ( CListNode<SWeaponMask> *wm = cn->Data()->lWeaponMask.Front(); wm; wm = wm->next() )
{
if ( wm->Data()->iGame == (m_iGame - 1) )
{
if ( wm->Data()->iMask != -1 )
cockpitStr = cockpitStr.RepToken(";", 9, CyString::Number(wm->Data()->iMask));
foundEntry = true;
break;
}
}
bool found = false;
for ( SStringList *str = cockpitList->Head(); str; str = str->next )
{
if ( str->str.GetToken(";", 19, 19).Compare(CyString(cn->Data()->sCockpit.token(";", 19))) )
{
// only replace existing entry if we have sepeperate weapon masks set
if ( foundEntry )
str->str = cockpitStr;
found = true;
break;
}
}
if ( !found )
cockpitList->PushBack(cockpitStr);
}
}
// finally, write the file
cockpitList->PushFront(CyString::Number(fileType) + ";" + CyString::Number(cockpitList->Count()) + ";", "");
cockpitList->PushFront(CyString("// TCockpits file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
CFileIO WriteFile(m_sTempDir + "/TCockpits.txt");
if ( WriteFile.WriteFile(cockpitList) )
{
this->PackFile(&WriteFile, "types\\TCockpits.pck");
WriteFile.remove();
}
// remove those entrys
cockpitList->PopFront();
cockpitList->PopFront();
}
return cockpitList;
}
CBaseFile *CPackages::FindScriptByAuthor(CyString author, CBaseFile *prev)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
CBaseFile *p = node->Data();
if ( prev )
{
if ( p == prev )
prev = NULL;
continue;
}
if ( p->author().Compare(author.ToString()) )
return p;
}
return NULL;
}
bool CPackages::extractAll(CBaseFile *baseFile, const Utils::String &dir, int game, bool includedir, CProgressInfo *progress) const
{
if (!baseFile)
return false;
Utils::CStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.GetGame(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::String::Number(i + 1), exe->sAddon);
}
return baseFile->extractAll(dir, game, gameAddons, includedir, progress);
}
bool CPackages::generatePackagerScript(CBaseFile *baseFile, bool wildcard, Utils::CStringList *list, int game, bool datafile) const
{
if (!baseFile)
return false;
Utils::CStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.GetGame(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::String::Number(i + 1), exe->sAddon);
}
return baseFile->GeneratePackagerScript(wildcard, list, game, gameAddons, datafile);
}
CBaseFile *CPackages::LoadPackagerScript(const Utils::String &filename, int compression, Utils::String (*askFunc)(const Utils::String &), Utils::CStringList *malformedLines, Utils::CStringList *unknownCommands, Utils::CStringList *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::CStringList fileData;
int iLine = 0;
CBaseFile *package = NULL;
while(!File.atEnd()) {
// read the next line in the file
Utils::String line = File.readEndOfLine();
// filter out any characters we dont really want
line.removeChar("\t\r");
line.removeFirstSpace();
if ( line.empty() ) continue;
// check for any comments (so we can ignore them)
if ( line.left(2).Compare("//") || line[0] == '#' ) 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(':') )
{
// there are some exeptions, and these are one word entrys only
line.removeEndSpace();
if ( line.contains(" ") )
{
if ( malformedLines )
malformedLines->pushBack(line, Utils::String::Number(iLine));
continue;
}
}
// check for the type line
if ( !package && line.token(":", 1).Compare("FileType") )
{
Utils::String sFileType = line.tokens(":", 2).removeFirstSpace();
if ( sFileType.Compare("Ship") )
package = new CXspFile();
else if ( sFileType.Compare("Script") )
package = new CSpkFile();
else if ( sFileType.Compare("Base") )
package = new CBaseFile();
continue;
}
fileData.pushBack(line, Utils::String::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);
CyString ftpaddr;
CyString ftpuser;
CyString ftppass;
CyString ftpdir;
Utils::String sMainGame;
Utils::CStringList otherGames;
Utils::CStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.GetGame(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::String::Number(i + 1), exe->sAddon);
}
// now lets read the rest of the day
Utils::CStringList listVaribles;
for (Utils::SStringList *line = fileData.first(); line; line = fileData.next())
{
Utils::String cmd = line->str.token(":", 1);
Utils::String rest = line->str.tokens(":", 2).removeFirstSpace();
if (cmd.Compare("Varible") || cmd.Compare("Variable"))
{
Utils::String s1 = rest.token(" ", 1);
Utils::String s2 = rest.tokens(" ", 2);
if(!listVaribles.changeData(s1, s2))
listVaribles.pushBack(s1, s2);
}
else
{
// replace variables
if ( rest.isin("$") )
{
for (Utils::SStringList *strVar = listVaribles.first(); strVar; strVar = listVaribles.next())
{
if ( rest.contains(strVar->str) )
rest = rest.findReplace(strVar->str, strVar->data);
}
if ( variables )
{
for (Utils::SStringList *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("$ASK") )
{
Utils::String replace = "$ASK";
Utils::String result;
if ( askFunc )
result = askFunc(cmd);
if ( rest.contains("$ASK(") )
{
replace = rest.tokens("$ASK(", 2).token(")", 1);
if ( result.empty() )
result = replace;
replace = "$ASK(" + replace + ")";
}
if ( !result.empty() )
rest = rest.findReplace(replace, result);
}
// todays date
if ( rest.isin("$DATE") )
{
time_t now;
time(&now);
struct tm *timeinfo = localtime(&now);
Utils::String result = Utils::String::Number(timeinfo->tm_mday) + "." + Utils::String::Number(timeinfo->tm_mon + 1) + "." + Utils::String::Number(timeinfo->tm_year + 1900);
if ( !result.empty() )
rest = rest.findReplace("$DATE", result);
}
// mydocuments
if ( rest.isin("$MYDOCUMENTS") )
{
if ( !m_sMyDoc.empty() )
rest = rest.findReplace("$MYDOCUMENTS", m_sMyDoc);
}
// current path
if ( rest.isin("$PATH") )
{
Utils::String currentDir = CFileIO(filename).dir();
if ( !currentDir.empty() )
rest = rest.findReplace("$PATH", currentDir);
}
// now parse the rest of the values
if ( cmd.Compare("FtpUpload") )
ftpaddr = rest.token(" ", 1) + ":" + rest.token(" ", 2);
else if ( cmd.Compare("FtpUser") )
ftpuser = rest;
else if ( cmd.Compare("FtpPass") )
ftppass = rest;
else if ( cmd.Compare("FtpDir") )
ftpdir = rest;
else if ( cmd.Compare("MultiGames") ) {
sMainGame = rest.token(" ", 1);
otherGames.tokenise(rest.tokens(" ", 2), " ");
}
else if ( !package->LoadPackageData(cmd, rest, sMainGame, otherGames, gameAddons, progress) )
{
if ( unknownCommands )
unknownCommands->pushBack(cmd, rest);
}
}
}
if ( package->filename().empty() )
package->LoadPackageData("AutoSave", "$AUTOSAVE", sMainGame, otherGames, gameAddons, progress);
if (package->autoExtraction())
{
for (auto itr = package->autoExtraction()->begin(); itr != package->autoExtraction()->end(); itr++)
{
unsigned int game = itr->first;
for (auto node = package->fileList()->Front(); node; node = node->next())
{
C_File *f = node->Data();
if (f->game() && f->game() != GAME_ALLNEW && !(f->game() & (1 << game)))
continue;
package->extractFile(f, itr->second, game, gameAddons);
}
}
}
if (package->autoExporter())
{
for (auto itr = package->autoExporter()->begin(); itr != package->autoExporter()->end(); itr++)
package->saveToArchive(itr->second, itr->first, &m_gameExe);
}
if ( !ftpaddr.Empty() )
{
if ( !ftpuser.Empty() )
{
if ( !ftppass.Empty() )
ftpaddr = ftpuser + ":" + ftppass + "@" + ftpaddr;
else
ftpaddr = ftpuser + "@" + ftpaddr;
}
if ( !ftpdir.Empty() )
ftpaddr += ftpdir;
package->SetFtpAddr(ftpaddr);
}
return package;
}
CyString CPackages::GetLanguageName() const
{
return CPackages::ConvertLanguage(m_iLanguage);
}
int CPackages::findAllPackages(CLinkList<CBaseFile> &packages, const Utils::String &dir)
{
int count = 0;
if (!dir.empty())
{
count += findPackageDirectories(packages, dir + "/Addons");
count += findPackageDirectories(packages, dir + "/Downloads");
}
count += findPackageDirectories(packages, "./Addons");
count += findPackageDirectories(packages, "./Downloads");
if (_pCurrentDir)
{
count += findPackageDirectories(packages, _pCurrentDir->dir + "/Addons");
count += findPackageDirectories(packages, _pCurrentDir->dir + "/Downloads");
count += findPackageDirectories(packages, _pCurrentDir->dir + "/ExtraContent");
}
return count;
}
int CPackages::findPackageDirectories(CLinkList<CBaseFile> &packages, const Utils::String &dir)
{
CDirIO Dir(dir);
int count = 0;
Utils::CStringList files;
if (Dir.dirList(files))
{
for (auto itr = files.begin(); itr != files.end(); itr++)
{
Utils::String 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::String &dir)
{
CDirIO Dir(dir);
int count = 0;
for (int type = 0; type < 2; type++)
{
Utils::CStringList files;
if (type == 0)
Dir.dirList(files, Utils::String::Null(), "*.spk");
else if(type == 1)
Dir.dirList(files, Utils::String::Null(), "*.xsp");
else
break;
for(auto itr = files.begin(); itr != files.end(); itr++)
{
Utils::String 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->GetIcon())
{
bool addedIcon = false;
p->ReadIconFileToMemory();
p->GetIcon()->SetFilename(this->tempDirectory().findReplace("\\", "/") + "/" + p->author() + "_" + p->name() + "." + p->GetIconExt().ToString());
p->GetIcon()->SetFullDir(this->tempDirectory());
if (p->GetIcon()->UncompressData())
{
if (p->GetIcon()->writeFilePointer())
addedIcon = true;
}
if (!addedIcon)
p->SetIcon(NULL, "");
}
// 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::String CPackages::ConvertLanguage(int lang)
{
switch ( lang )
{
case 44:
return "English";
case 49:
return "German";
case 7:
return "Russian";
case 33:
return "French";
case 30:
return "Greek";
case 31:
return "Dutch";
case 32:
return "Belgian";
case 34:
return "Spanish";
case 36:
return "Hungarian";
case 39:
return "Italian";
case 40:
return "Romanian";
case 41:
return "Swiss";
case 42:
return "Czech";
case 43:
return "Austrian";
case 45:
return "Danish";
case 46:
return "Swedish";
case 47:
return "Norweigen";
case 48:
return "Polish";
}
return Utils::String::Number(lang);
}
bool CPackages::CheckAccessRights(CyString dir)
{
if ( dir.Empty() )
dir = m_sCurrentDir;
// write a file, then read the contents
CFileIO File(dir + "/accessrightscheck.dat");
// check if file exists and remove it
if ( File.exists() )
{
// if we cant remove it, we dont have enough rights
if ( !File.remove() )
return false;
// if its still there, we dont have enough rights
if ( File.exists() )
return false;
}
// now create the file
if ( !File.writeString("testing access rights") )
return false;
// now check it exists
if ( !File.exists() )
return false;
// now read the file for the correct contents
CyStringList *lines = File.ReadLinesStr();
if ( !lines )
return false;
// check that one of the lines is correct
for ( SStringList *s = lines->Head(); s; s = s->next )
{
if ( s->str == "testing access rights" )
{
delete lines;
return true;
}
}
delete lines;
return false;
}
bool CPackages::LoadShipData(CyString file, CyStringList *list)
{
CFileIO File;
bool deleteFile = false;
// load from cat file
if ( CFileIO(file).CheckFileExtension("cat") )
{
CCatFile cat;
if ( cat.open(file.ToString(), this->getAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
return false;
if ( !cat.ExtractFile("types\\TShips.pck", m_sTempDir + "/tships.txt") )
return false;
File.open(m_sTempDir + "/tships.txt");
deleteFile = true;
}
// otherwise its a normal file
else if ( CFileIO(file).CheckFileExtension("pck") )
{
C_File f(file.ToString());
if ( !f.ReadFromFile() )
return false;
f.UnPCKFile();
f.SetFilename(m_sTempDir + "/tships.txt");
if ( !f.writeFilePointer() )
return false;
File.open(m_sTempDir + "/tships.txt");
deleteFile = true;
}
else
File.open(file.ToString());
if ( !File.exists() )
return false;
bool ret = false;
CyStringList *lines = File.ReadLinesStr();
if ( lines )
{
bool readFirst = false;
for ( SStringList *str = lines->Head(); str; str = str->next )
{
if ( str->str.Empty() )
continue;
str->str.RemoveChar('\r');
str->str.RemoveChar(9);
str->str.RemoveFirstSpace();
if ( str->str.Empty() )
continue;
if ( str->str[0] == '/' || str->str[0] == '#' )
continue;
if ( !readFirst )
readFirst = true;
else
{
CyString t = str->str.GetToken(";", -2);
while ( t.Right(1) == ";" )
t.Truncate((int)t.Length() - 1);
list->PushBack(t, str->str);
}
}
delete lines;
ret = true;
}
if ( deleteFile )
File.remove();
return ret;
}
CyString CPackages::ReadShipData(CyString file, CyString id)
{
CyStringList *list = this->LoadShipData(file);
if ( !list )
return NullString;
CShipData data;
for ( SStringList *str = list->Head(); str; str = str->next )
{
if ( str->str.Compare(id) )
{
delete list;
return str->data;
}
}
delete list;
return NullString;
}
CyStringList *CPackages::LoadShipData(CyString file)
{
CyStringList *list = new CyStringList;
if ( this->LoadShipData(file, list) )
return list;
delete list;
return NULL;
}
bool CPackages::ReadTextPage(CyString file, CyStringList *list, bool search, int page)
{
CFileIO File;
bool deleteFile = false;
// read all text files from mod
if ( CFileIO(file).CheckFileExtension("cat") )
{
bool done = false;
CCatFile cat;
if ( cat.open(file.ToString(), 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);
CyString sF = f->sFile;
// is a text file
sF = sF.FindReplace("\\", "/");
if ( !sF.GetToken("/", 1, 1).Compare("t") )
continue;
CyString baseFile = CFileIO(sF.ToString()).baseName();
// check language
int lang = 0;
if ( baseFile.FindPos("-L") != -1 ) // new language file
lang = baseFile.Right(3).ToInt();
else
{
baseFile.Truncate((int)baseFile.Length() - 4);
lang = baseFile.ToInt();
}
if ( lang != m_iLanguage )
continue;
// now extract and parse
if ( cat.ExtractFile(f->sFile, m_sTempDir + "/" + CFileIO(f->sFile).baseName() + ".xml") )
{
if ( this->ReadTextPage(m_sTempDir + "/" + CFileIO(f->sFile).baseName() + ".xml", list, search, page) )
done = true;
}
}
return done;
}
// otherwise its a normal file
else if ( CFileIO(file).CheckFileExtension("pck") )
{
C_File f(file.ToString());
if ( !f.ReadFromFile() )
return false;
f.UnPCKFile();
f.SetFilename(m_sTempDir + "/textfile.xml");
if ( !f.writeFilePointer() )
return false;
File.open(m_sTempDir + "/textfile.xml");
deleteFile = true;
}
else
File.open(file.ToString());
if ( !File.exists() )
return false;
// open and read file
CyStringList *lines = File.ReadLinesStr();
if ( !lines )
return false;
bool inPage = false;
for ( SStringList *str = lines->Head(); str; str = str->next )
{
// search for page
if ( !inPage )
{
if ( str->str.FindPos("<page") > -1 )
{
// find the page id
int pos = str->str.FindPos("\"");
if ( pos > -1 )
{
int endpos = str->str.FindPos("\"", pos + 1);
if ( endpos > -1 )
{
CyString p = str->str.Mid(pos + 2, endpos - pos - 1);
if ( p.Length() > 4 )
p = p.Right(4);
int checkPage = p.ToInt();
if ( checkPage == page )
inPage = true;
}
}
}
}
// add each id
else
{
if ( str->str.FindPos("</page") > -1 )
break;
if ( str->str.FindPos("<t id") > -1 )
{
int pos = str->str.FindPos("\"");
if ( pos > -1 )
{
int endpos = str->str.FindPos("\"", pos + 1);
if ( endpos > -1 )
{
int id = str->str.Mid(pos + 2, endpos - pos - 1).ToInt();
pos = str->str.FindPos(">", endpos);
if ( pos > -1 )
{
++pos;
endpos = str->str.FindPos("</", pos);
if ( endpos > -1 )
{
CyString text = str->str.Mid(pos + 1, endpos - pos);
while ( text.FindPos('(') != -1 && text.FindPos(')') != -1 )
{
int s = text.FindPos('(');
text = text.Erase(s, text.FindPos(')') - s + 1);
}
list->PushBack(CyString::Number(id), text, search);
}
}
}
}
}
}
}
delete lines;
return true;
}
CyStringList *CPackages::ReadTextPage(CyString file, bool search, int page)
{
CyStringList *list = new CyStringList;
if ( this->ReadTextPage(file, list, search, page) )
return list;
delete list;
return NULL;
}
int CPackages::AdjustFileType(CyString file, int filetype)
{
CFileIO File(file);
Utils::String dir = File.GetDirIO().topDir();
CyString basename = File.baseName();
// mod files
if ( File.CheckFileExtension("cat") || File.CheckFileExtension("dat") )
return FILETYPE_MOD;
// check for text files
if ( File.filename().isin("-L") && File.filename().left(4).isNumber() )
return FILETYPE_TEXT;
if ( File.baseName().Compare("conversations") )
return FILETYPE_TEXT;
if ( basename.Length() <= 4 && basename.IsNumber() && (File.CheckFileExtension("xml") || File.CheckFileExtension("pck")) )
return FILETYPE_TEXT;
// X2/X3 text file
if ( basename.Length() >= 5 && basename.Length() <= 8 && ((int)File.baseName()) )
return FILETYPE_TEXT;
if ( filetype == FILETYPE_TEXT ) // should no longer be anything text
return FILETYPE_SCRIPT;
if ( File.CheckFileExtension("wav") || File.CheckFileExtension("mp3") )
return FILETYPE_SOUND;
return filetype;
}
void CPackages::RemoveFailedFiles()
{
for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
{
if ( CFileIO::Remove(str->str.ToString()) )
str->remove = true;
}
m_lNonRemovedFiles.RemoveMarked();
}
CXspFile *CPackages::extractShip(const Utils::String &sCatFile, const Utils::String &sId, CProgressInfo *progress)
{
CVirtualFileSystem *pVfs = new CVirtualFileSystem();
if ( !pVfs->addMod(sCatFile) ) {
delete pVfs;
return NULL;
}
CXspFile *newShip = new CXspFile;
if ( !newShip->extractShip(pVfs, sId, progress) ) {
delete newShip;
newShip = NULL;
}
delete pVfs;
return newShip;
}
void CPackages::getMergedFiles(Utils::CStringList &list, CCatFile *cat1, CCatFile *cat2)
{
// first add all files from the "primary" mod
for (auto itr = cat1->GetFiles()->cbegin(); itr != cat1->GetFiles()->cend(); itr++)
list.pushBack((*itr)->sFile.findReplace("\\", "/"), "1");
// now add the ones from the secondary
for (auto itr = cat2->GetFiles()->cbegin(); itr != cat2->GetFiles()->cend(); itr++)
{
Utils::String sFile = (*itr)->sFile.findReplace("\\", "/");
// if its found on the 2nd list, dont add, just adjust the type
if(!list.changeData(sFile, "-1"))
list.pushBack(sFile, "2");
}
}
bool CPackages::CanWeMerge(const Utils::String &file) const
{
return CModDiff::CanBeDiffed(file);
}
bool CPackages::NeedToMerge(CyString file)
{
CyString firstDir = file.GetToken("/", 1, 1);
if ( firstDir.Compare("t") )
return true;
if ( firstDir.Compare("types") )
return true;
if ( firstDir.Compare("maps") )
return true;
return false;
}
bool CPackages::MergeMods(CCatFile *mod1, CCatFile *mod2, CyString outFile, CyStringList *cantMerge)
{
CCatFile newCat;
if ( newCat.open(outFile.ToString(), this->getAddonDir()) != CATERR_CREATED )
return false;
Utils::CStringList list;
this->getMergedFiles(list, mod1, mod2);
if (list.empty())
return false;
// add all the files to the new mod first
Utils::CStringList 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(CyString((*itr)->str), "1");
}
}
else if ( status == 2 )
{
if ( !newCat.WriteFromCat(mod2, (*itr)->str) )
{
if ( cantMerge )
cantMerge->PushBack(CyString((*itr)->str), "2");
}
}
else if ( status == -1 )
{
if ( this->NeedToMerge((*itr)->str) )
{
if ( this->CanWeMerge((*itr)->str) )
conflicts.pushBack((*itr)->str);
else if ( cantMerge )
cantMerge->PushBack(CyString((*itr)->str), "-1");
}
else
{
if ( !newCat.WriteFromCat(mod1, (*itr)->str) )
{
if ( cantMerge )
cantMerge->PushBack(CyString((*itr)->str), "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
// CyStringList *text = this->MergeTextFiles(conflicts, mod1, mod2);
// delete text;
// write the cat file when we're done
if ( !newCat.WriteCatFile() )
return false;
return true;
}
/**
* Gets the file list from a mod that might have compatability problems
*
* This includes all types and text files
*
* Returns true if it finds any files
*/
bool CPackages::GetModCompatabilityList(C_File *file, CyStringList *list)
{
// not a valid file
if ( !file ) return false;
if ( file->GetFileType() != FILETYPE_MOD ) return false;
if ( !file->GetFileExt().Compare("cat") ) return false;
// we need to read the file list for the mod
CCatFile cat;
if ( cat.open(file->filePointer(), this->getAddonDir(), CATREAD_JUSTCONTENTS, false) == CATERR_NONE )
{
for (unsigned int i = 0; i < cat.GetNumFiles(); i++ )
{
SInCatFile *f = cat.GetFile(i);
CyString filename = f->sFile;
filename = filename.FindReplace("\\", "/");
bool found = false;
if ( filename.Left(2).Compare("t/") || filename.Left(6).Compare("types/") )
found = true;
else if ( filename.Left(8).Compare("addon/t/") || filename.Left(12).Compare("addon/types/") )
found = true;
if ( found ) {
if ( list )
list->PushBack(filename, CyString(f->lSize));
else
return true;
}
}
}
if ( list && !list->Empty() )
return true;
return false;
}
/**
* Gets the files that are not compatable with each other
*
* Returns true if theres any files that are not compatable
*
* If list is specified, fills up with all files that were found
*/
bool CPackages::CheckCompatabilityBetweenModFiles(C_File *from, C_File *to, CyStringList *list)
{
// not a valid file
if ( !from || !to ) return false;
if ( from->GetFileType() != FILETYPE_MOD ) return false;
if ( to->GetFileType() != FILETYPE_MOD ) return false;
if ( !from->GetFileExt().Compare("cat") ) return false;
if ( !to->GetFileExt().Compare("cat") ) return false;
// get file lists from each file
CyStringList fromList;
if ( GetModCompatabilityList(from, &fromList) )
{
CyStringList toList;
if ( GetModCompatabilityList(to, &toList) )
{
// both have files we need to check, compare them
for ( SStringList *str = fromList.Head(); str; str = str->next )
{
CyString fromFile = str->str;
fromFile = fromFile.FindReplace("\\", "/");
fromFile = fromFile.FindReplace("//", "/");
for ( SStringList *toStr = toList.Head(); toStr; toStr = toStr->next )
{
CyString toFile = toStr->str;
toFile = toFile.FindReplace("\\", "/");
toFile = toFile.FindReplace("//", "/");
if ( fromFile.Compare(toFile) )
{
if ( list )
list->PushBack(from->GetFilename() + "::" + str->str, to->GetFilename() + "::" + toStr->str);
else
return true;
}
}
}
}
}
if ( list && !list->Empty() )
return true;
return false;
}
bool CPackages::CheckCompatabilityBetweenMods(CBaseFile *from, CBaseFile *to, CyStringList *list)
{
if ( !from || !to ) return false;
if ( !from->IsEnabled() || !to->IsEnabled() ) return false;
if ( !from->AnyFileType(FILETYPE_MOD) ) return false;
if ( !to->AnyFileType(FILETYPE_MOD) ) return false;
if ( from == to ) return false; // cant have incompatabilities to itself
int count = 0;
for ( C_File *f = from->GetFirstFile(FILETYPE_MOD); f; f = from->GetNextFile(f) )
{
if ( !f->IsFakePatch() ) continue;
if ( f->GetFileExt().Compare("dat") ) continue;
for ( C_File *compareFile = to->GetFirstFile(FILETYPE_MOD); compareFile; compareFile = to->GetNextFile(compareFile) )
{
if ( compareFile == f ) continue; // same file we're checking against
if ( !compareFile->IsFakePatch() ) continue;
if ( compareFile->GetFileExt().Compare("dat") ) continue;
// now we have to files to compare
if ( CheckCompatabilityBetweenModFiles(f, compareFile, list) )
++count;
}
}
if ( count )
return true;
return false;
}
int CPackages::CheckCompatabilityAgainstPackages(CBaseFile *newFile, CyStringList *list, CLinkList<CBaseFile> *packages)
{
if ( !newFile->IsEnabled() ) return 0;
if ( !newFile->AnyFileType(FILETYPE_MOD) ) return 0;
// we need to extract all mod files
for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *f = fNode->Data();
if ( f->GetFileType() != FILETYPE_MOD ) continue;
if ( !f->IsFakePatch() ) continue;
if ( !f->CheckFileExt("cat") ) continue;
if ( newFile->ExtractFile(f, m_sTempDir) )
f->SetFullDir(m_sTempDir);
}
// compare mod files against all installed packages
int count = 0;
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( !node->Data() ) continue;
CBaseFile *p = node->Data();
if ( !p->IsEnabled() ) continue;
if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
if ( this->IsSamePackage(p, newFile) ) continue; // dont include self
if ( CheckCompatabilityBetweenMods(newFile, p, list) )
{
++count;
if ( packages && !packages->FindData(p) )
packages->push_back(p);
}
}
for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
{
C_File *f = fNode->Data();
CFileIO::Remove(f->filePointer());
f->SetFullDir("");
}
return count;
}
bool CPackages::IsSamePackage(CBaseFile *p1, CBaseFile *p2)
{
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(CyStringList *list)
{
if ( !list ) return;
m_lFakePatchOrder.Clear();
for ( SStringList *str = list->Head(); str; str = str->next )
m_lFakePatchOrder.PushBack(str->str, str->data);
}
SAvailablePackage *CPackages::CreateAvailablePackageData(CBaseFile *package)
{
if ( !package ) return NULL;
SAvailablePackage *p = new SAvailablePackage;
for ( CListNode<SGameCompat> *node = package->GetGameCompatabilityList()->Front(); node; node = node->next() ) {
SGameCompat *gc = new SGameCompat;
gc->iGame = node->Data()->iGame;
gc->iVersion = node->Data()->iVersion;
gc->sVersion = node->Data()->sVersion;
p->lGames.push_back(gc);
}
p->bSigned = package->IsSigned();
p->iChanging = package->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("\n", "::newline::");
p->sName = package->name();
p->sUpdated = package->creationDate();
p->sVersion = package->version();
p->sFilename = CFileIO(package->filename()).filename();
return p;
}
Utils::String CPackages::FormatAvailablePackageData(CBaseFile *package)
{
SAvailablePackage *p = CPackages::CreateAvailablePackageData(package);
Utils::String ret = CPackages::FormatAvailablePackageData(p);
delete p;
return ret;
}
Utils::String CPackages::FormatAvailablePackageData(SAvailablePackage *package)
{
Utils::String ret = (long)package->iType;
Utils::String gameCompat;
for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
if ( !gameCompat.empty() )
gameCompat += "!";
gameCompat += Utils::String::Number(node->Data()->iGame);
}
if ( gameCompat.empty() )
gameCompat = "0";
ret = ret.addToken("::", gameCompat);
ret = ret.addToken("::", package->sName);
ret = ret.addToken("::", package->sAuthor);
ret = ret.addToken("::", package->sVersion);
ret = ret.addToken("::", package->sUpdated);
ret = ret.addToken("::", package->sFilename);
ret = ret.addToken("::", Utils::String::Number(package->iEase));
ret = ret.addToken("::", Utils::String::Number(package->iChanging));
ret = ret.addToken("::", Utils::String::Number(package->iRec));
ret = ret.addToken("::", Utils::String::Number(package->iPluginType));
ret = ret.addToken("::", Utils::String::Number(package->iScriptType));
ret = ret.addToken("::", (package->bSigned) ? "1" : "0");
ret = ret.addToken("::", package->sDesc);
return ret;
}
void CPackages::ParseAvailablePackage(CyString sStr, CyString webaddress)
{
// first check game
Utils::String str = sStr.ToString();
int num = 0;
Utils::String *tok = str.tokenise("::", &num);
if ( !num || !tok ) return;
// invalid number of entries?
if ( num < 7 ) { CLEANSPLIT(tok, num); 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::String sGame = tok[1];
if ( sGame.isin("!") ) {
for(int i = 1; i <= sGame.countToken("!"); i++) {
SGameCompat *gc = new SGameCompat;
gc->iVersion = 0;
gc->iGame = sGame.token("!", 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.ToString() + "/" + p->sFilename;
p->iChanging = p->iEase = p->iPluginType = p->iRec = p->iScriptType = -1;
// check if we have the extra values
if ( num >= 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 ( num > 12 ) {
if ( num > 13 ) {
p->sDesc = tok[13];
p->bSigned = tok[12].toBool();
}
else
p->sDesc = tok[12];
}
}
else if ( num > 7 )
p->sDesc = tok[8];
if ( !p->sDesc.empty() )
p->sDesc = p->sDesc.findReplace("::newline::", "\\n");
AddAvailablePackage(p);
CLEANSPLIT(tok, num);
}
SAvailablePackage *CPackages::FindAvailablePackage(const Utils::String &filename)
{
for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
{
if ( node->Data()->sFilename.Compare(filename) )
return node->Data();
}
return NULL;
}
bool CPackages::AddAvailablePackage(SAvailablePackage *package)
{
if ( !package->lGames.empty() ) {
bool found = false;
for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
if ( !node->Data()->iGame || node->Data()->iGame == m_iGame ) {
found = true;
break;
}
}
if ( !found )
return false;
}
SAvailablePackage *p = FindAvailablePackage(package->sFilename);
if ( p )
m_lAvailablePackages.remove(p);
m_lAvailablePackages.push_back(package);
return true;
}
bool CPackages::AnyAvailablePackages(int type)
{
if ( type == -1 ) return !m_lAvailablePackages.empty();
for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
{
if ( node->Data()->iType == type )
return true;
}
return false;
}
int CPackages::FindAllServers(CyStringList *list)
{
for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
{
if ( !node->Data()->webAddress().empty() )
list->PushBack(CyString(node->Data()->webAddress()), true);
if ( node->Data()->AnyWebMirrors() )
{
for ( SStringList *str = node->Data()->GetWebMirrors()->Head(); str; str = str->next )
list->PushBack(str->str, true);
}
}
CFileIO File("data\\web");
if ( File.startRead() ) {
while(!File.atEnd()) {
Utils::String line = File.readEndOfLine();
line.removeChar("\n\r\t ");
if ( !line.empty() )
list->PushBack(CyString(line), true);
}
File.close();
}
list->PushBack("http://xpluginmanager.co.uk/tcscripts", true);
list->PushBack("http://xpluginmanager.co.uk/apscripts", true);
return list->Count();
}
void CPackages::ReadArchiveData(CyString filename, CBaseFile *archive)
{
size_t size;
char *data = CFileIO(filename).ReadToData(&size);
if ( size && data )
ReadArchiveData(data, size, archive);
}
void CPackages::ReadArchiveData(const char *buf, size_t len, CBaseFile *archive)
{
Utils::CStringList otherGames;
Utils::CStringList gameAddons;
for (unsigned int i = 0; i < m_gameExe.gameCount(); ++i)
{
SGameExe *exe = m_gameExe.GetGame(i);
if (!exe->sAddon.empty())
gameAddons.pushBack(Utils::String::Number(i + 1), exe->sAddon);
}
CyString data(buf);
int max;
CyString *str = data.SplitToken("\n", &max);
if ( str && max )
{
for ( int i = 0; i < max; i++ )
{
CyString line = str[i];
if ( line.Empty() )
continue;
// filter out any spaces, tabs in front
line.RemoveChar('\t');
line.RemoveChar('\r');
CyString linenospace = line;
linenospace.RemoveFirstSpace();
if ( linenospace.Empty() )
continue;
// check for any comments
if ( linenospace.Left(2) == "//" )
continue;
if ( linenospace[0] == '#' )
continue;
// all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
if ( !line.IsIn(':') )
continue;
CyString first = line.GetToken(":", 1, 1);
CyString rest = line.GetToken(":", 2).RemoveFirstSpace();
CyString checkType = first;
bool shared = false;
if ( checkType.Left(6).Compare("Shared") )
{
checkType = first.Right(-6);
shared = true;
}
// now check type name
int filetype = GetFileTypeFromString(checkType);
if ( filetype == -1 )
archive->LoadPackageData(first.ToString(), rest.ToString(), Utils::String::Null(), otherGames, gameAddons, NULL);
}
}
CLEANSPLIT(str, max)
}
CBaseFile *CPackages::_archive_fromRar(CyString filename, bool toInstall )
{
// make sure we can open the zip file
CBaseFile *archive = NULL;
#ifdef _RAR
HANDLE hArcData;
int RHCode,PFCode;
char CmtBuf[16384];
struct RARHeaderDataEx HeaderData;
struct RAROpenArchiveDataEx OpenArchiveData;
// find the pluginmanager text to covnert to spkfile
if ( toInstall ) {
memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
OpenArchiveData.ArcName=(char *)filename.c_str();
OpenArchiveData.CmtBuf=CmtBuf;
OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
OpenArchiveData.OpenMode=RAR_OM_LIST;
hArcData=RAROpenArchiveEx(&OpenArchiveData);
if (OpenArchiveData.OpenResult!=0) return NULL;
HeaderData.CmtBuf=NULL;
memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));
while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0) {
if ( CyString(HeaderData.FileName).Compare("pluginmanager.txt") ) {
toInstall = false;
break;
}
}
RARCloseArchive(hArcData);
}
if ( toInstall )
archive = new CArchiveFile(); // just installing an archive file
else
archive = new CSpkFile(); // converting to a spk file
memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
OpenArchiveData.ArcName=(char *)filename.c_str();
OpenArchiveData.CmtBuf=CmtBuf;
OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
OpenArchiveData.OpenMode=RAR_OM_EXTRACT;
OpenArchiveData.UserData=EXTRACT;
hArcData=RAROpenArchiveEx(&OpenArchiveData);
if (OpenArchiveData.OpenResult!=0) return NULL;
HeaderData.CmtBuf=NULL;
memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));
bool error = false;
CyString extractedFile = CDirIO(m_sTempDir).File("extracted.tst").findreplace("/", "\\").findreplace("\\\\", "\\");
while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
{
CyString fileName = HeaderData.FileName;
if ( HeaderData.FileAttr == 16 )
continue;
wchar_t wText[200];
::MultiByteToWideChar(CP_ACP, NULL, (char *)extractedFile.c_str(), -1, wText, extractedFile.Length() + 1);
PFCode=RARProcessFileW(hArcData, RAR_EXTRACT, NULL, NULL);
if (PFCode!=0)
{
error = true;
break;
}
CFileIO File(fileName);
if ( File.exists() )
{
if ( fileName.Compare("pluginmanager.txt") )
this->ReadArchiveData(File.fullFilename(), archive);
else
{
CyString extradir;
int type = SPK::GetAutomaticFiletype(fileName, &extradir, true);
// check for special file types
C_File *f = NULL;
if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
f = archive->AddFile(CFileIO(fileName).filename(), "", 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, type);
f->ReadFromFile(File.fullFilename());
}
File.remove();
}
}
RARCloseArchive(hArcData);
if ( error )
{
delete archive;
archive = NULL;
}
#endif
return archive;
}
CBaseFile *CPackages::_archive_fromZip(CyString filename, bool toInstall)
{
CBaseFile *archive = NULL;
TCHAR buf[5000];
wsprintf(buf, L"%hs", filename.c_str());
HZIP hz = OpenZip(buf, 0);
if ( !hz )
return NULL;
int index;
// move the files from the zip to the package
ZIPENTRY ze;
if ( FindZipItem(hz, L"pluginmanager.txt", true, &index, &ze) == Z_OK )
toInstall = false;
CloseZip(hz);
hz = OpenZip(buf, 0);
if ( !hz )
return NULL;
// create the correct package
if ( toInstall )
archive = new CArchiveFile(); // just installing an archive file
else
archive = new CSpkFile(); // converting to a spk file
GetZipItem(hz, -1, &ze);
int numitems = ze.index;
bool error = false;
for ( int zi = 0; zi < numitems; zi++ )
{
ZIPENTRY ze;
if ( GetZipItem(hz, zi, &ze) != Z_OK )
{
error = true;
break;
}
if ( ze.attr & FILE_ATTRIBUTE_DIRECTORY )
continue; // dont do directories
char *iBuf = new char[ze.unc_size];
UnzipItem(hz, zi, iBuf, ze.unc_size);
CyString Name(ze.name);
// if its the data file, dont add it, but extract to get settings from
if ( Name.Compare("pluginmanager.txt") )
{
this->ReadArchiveData(iBuf, ze.unc_size, archive);
delete[] iBuf;
}
else
{
CyString extradir;
int type = SPK::GetAutomaticFiletype(Name, &extradir, true);
C_File *f = NULL;
Utils::String filename = CFileIO(Name).filename();
Utils::String dir = CFileIO(Name).dir();
// check for special file types
if ( type == FILETYPE_SCRIPT_UNINSTALL ) {
f = archive->AddFile(filename, dir, FILETYPE_SCRIPT);
if ( f ) {
f->copyData((const unsigned char *)iBuf, ze.unc_size);
}
type = FILETYPE_UNINSTALL;
}
int game = 0;
// check for addons
if (dir.contains('/', true))
{
Utils::String first = dir.token("/", 1);
int g = m_gameExe.findAddonType(first);
if (g != -1)
game = g + 1;
}
if ( type == -1 )
f = archive->AddFile(filename, dir, FILETYPE_EXTRA, game);
else
f = archive->AddFile(filename, extradir, type, game);
if ( f )
f->SetData((const unsigned char *)iBuf, ze.unc_size);
else
delete[] iBuf;
}
}
CloseZip(hz);
if ( error )
{
delete archive;
archive = NULL;
}
return archive;
}
CBaseFile *CPackages::CreateFromArchive(CyString filename, bool toInstall )
{
// make sure we can open the zip file
CBaseFile *archive = NULL;
if ( CFileIO(filename).CheckFileExtension("rar") )
archive = this->_archive_fromRar(filename, toInstall);
else if ( CFileIO(filename).CheckFileExtension("zip") )
archive = this->_archive_fromZip(filename, toInstall);
if ( archive ) {
archive->setFilename(CFileIO(filename).ChangeFileExtension("spk").ToString());
if ( toInstall )
archive->setName(CFileIO(filename).filename());
else
archive->setName(CFileIO(filename).baseName());
}
return archive;
}
Utils::String CPackages::CreateFromPackagerScript(CPackages *packages, const Utils::String &filename)
{
Utils::String curDir = CFileIO(filename).dir();
Utils::CStringList variables;
variables.pushBack("$PATH", curDir);
CBaseFile *package = packages->LoadPackagerScript(filename, NULL, NULL, NULL, &variables);
if ( !package )
return Utils::String::Null();
Utils::String saveto = package->filename();
saveto = saveto.findReplace("$DEFAULTDIR", curDir + "/");
saveto = saveto.findReplace("$PATH", curDir);
saveto = saveto.findReplace("\\", "/");
saveto = saveto.findReplace("//", "/");
if ( !saveto.right(4).Compare(".spk") && package->GetType() != TYPE_XSP )
saveto += ".spk";
else if ( !saveto.right(4).Compare(".xsp") && package->GetType() == TYPE_XSP )
saveto += ".xsp";
// write script
if ( package->WriteFile(saveto) )
{
if ( package->AutoGenerateUpdateFile() )
package->createUpdateFile(CFileIO(saveto).dir());
return saveto;
}
return Utils::String::Null();
}
int CPackages::GeneratePackageUpdateData(CyString dir, bool includeSingle)
{
Utils::CStringList filedata;
CPackages packages;
CDirIO Dir(dir);
for ( int i = 0; i < 2; i++ )
{
Utils::String pattern;
if ( i == 0 ) pattern = "*.spk";
else if ( i == 1 ) pattern = ".xsp";
else break;
Utils::CStringList files;
if(Dir.dirList(files, "", 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.ToString());
filedata.pushBack(CPackages::FormatAvailablePackageData(p));
delete p;
}
}
}
if ( !filedata.empty() )
{
CFileIO File(dir + "/xpackagedata.dat");
if ( File.writeFile(&filedata) )
return filedata.size();
}
return 0;
}
int CPackages::VerifyInstalledFiles(CyStringList *missingFiles, bool getPackages)
{
int count = 0;
for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
{
C_File *f = fn->Data();
bool exists = false;
if ( f->GetFilePointer().IsIn("::") ) {
CyString modFile = f->GetFilePointer().GetToken("::", 1, 1);
CyString file = f->GetFilePointer().GetToken("::", 2, 2);
if ( CFileIO(modFile).ExistsOld() ) {
CCatFile catFile;
if ( catFile.open(modFile.ToString(), "", CATREAD_CATDECRYPT, false) == CATERR_NONE ) {
if ( catFile.FindData(file) )
exists = true;
}
}
}
else {
exists = CFileIO(f->GetFilePointer()).ExistsOld();
}
if ( !exists )
{
++count;
if ( missingFiles )
{
CyString packages;
if ( getPackages )
{
for ( CListNode<CBaseFile> *p = m_lPackages.Front(); p; p = p->next() )
{
CBaseFile *package = p->Data();
if ( package->IsFileAdded(f) )
{
if ( !packages.Empty() )
packages += "\n";
packages += package->GetFullPackageName(m_iLanguage);
}
}
}
CyString filename = f->GetFilePointer();
filename = filename.Remove(m_sCurrentDir);
missingFiles->PushBack(filename, packages, false);
}
}
}
return count;
}