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