Rev 91 | Rev 102 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
// SpkFile.cpp: implementation of the CSpkFile class.////////////////////////////////////////////////////////////////////////#include "BaseFile.h"//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////#include "spk.h"#include "DirIO.h"#include "File_IO.h"#include "CatFile.h"#include "archive/zip.h"#include "Packages.h"#include "TextDB.h"#include <Package/InstallText.h>// remove these eventuallyusing namespace SPK;using namespace Package;CArchiveFile::~CArchiveFile(){Delete ();}CArchiveFile::CArchiveFile() : CBaseFile (){SetDefaults ();}void CBaseFile::SetDefaults (){_setDefaults();m_pIconFile = NULL;m_bAutoGenerateUpdateFile = false;m_SHeader.iValueCompression = SPKCOMPRESS_ZLIB;m_SHeader2.iFileCompression = SPKCOMPRESS_ZLIB;m_SHeader2.iDataCompression = SPKCOMPRESS_LZMA;m_pParent = NULL;_changed();m_bUpdate = false;ClearError();m_iLoadError = 0;m_bFullyLoaded = false;m_bSigned = false;m_bEnable = m_bGlobal = m_bProfile = m_bModifiedEnabled = true;m_bOverrideFiles = false;}CBaseFile::CBaseFile() : _pTextDB(NULL){SetDefaults ();}CBaseFile::~CBaseFile(){Delete();}void CBaseFile::Delete (){m_lFiles.clear(true);if ( m_pIconFile ){delete m_pIconFile;m_pIconFile = NULL;}if ( _pTextDB ) {delete _pTextDB;_pTextDB = NULL;}m_lNames.clear(true);}CyString CBaseFile::GetLanguageName ( int lang ){for ( CListNode<SNames> *node = m_lNames.Front(); node; node = node->next() ){SNames *n = node->Data();if ( n->iLanguage == lang )return n->sName;}return this->name();}/*############################################################################################################ Base Class Functions ############################################################################################################*/CLinkList<C_File> *CBaseFile::fileList(int type) const{CLinkList<C_File> *list = new CLinkList<C_File>;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f->GetFileType() == type )list->push_back(f);}return list;}C_File *CBaseFile::GetNextFile(C_File *prev) const{int type = -1;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( type == -1 ){if ( f == prev )type = f->GetFileType();}else{if ( f->GetFileType() == type )return f;}}return NULL;}C_File *CBaseFile::GetPrevFile(C_File *next) const{if ( !next )return NULL;int type = -1;for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() ){C_File *f = node->Data();if ( type == -1 ){if ( f == next )type = f->GetFileType();}else{if ( f->GetFileType() == type )return f;}}return NULL;}C_File *CBaseFile::GetFirstFile(int type) const{for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f->GetFileType() == type )return f;}return NULL;}int CBaseFile::CheckFile ( CyString filename, float *version ){CFileIO File(filename);if ( !File.startRead() ) return 0;Utils::String line = File.readEndOfLine();Utils::String type = line.token(";", 1);File.close();// check for old versionif ( line.left(3) == "HiP" ) return SPKFILE_OLD;// check for formatif ( version ) *version = line.token(";", 2);if ( type == "BaseCycrow" ) return SPKFILE_BASE;if ( type == "SPKCycrow" ) return SPKFILE_SINGLE;if ( type == "XSPCycrow" ) return SPKFILE_SINGLESHIP;if ( type == "MSPKCycrow" ) return SPKFILE_MULTI;return SPKFILE_INVALID;}void CBaseFile::ClearFileData(){for ( CListNode<C_File> *f = m_lFiles.Front(); f; f = f->next() ){f->Data()->DeleteData();}}CyString CBaseFile::GetNameValidFile (){CyString name = this->name();name.RemoveChar ( ':' );name.RemoveChar ( '/' );name.RemoveChar ( '\\' );name.RemoveChar ( '*' );name.RemoveChar ( '?' );name.RemoveChar ( '"' );name.RemoveChar ( '<' );name.RemoveChar ( '>' );name.RemoveChar ( '|' );return name;}void CBaseFile::SwitchFilePointer(C_File *oldFile, C_File *newFile){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f == oldFile ){node->ChangeData(newFile);break;}}}bool CBaseFile::AnyFileType ( int type ){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f->GetFileType() == type )return true;}return false;}void CBaseFile::AddFile ( C_File *file ){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f->GetFileType() != file->GetFileType() )continue;if ( f->GetName() != file->GetName () )continue;if ( f->GetDir() != file->GetDir() )continue;if ( f->GetGame() != file->GetGame() )continue;m_lFiles.remove(node, true);break;}_addFile(file);}C_File *CBaseFile::AddFile ( CyString file, CyString dir, int type, int game ){C_File *newfile = new C_File ( file );newfile->SetDir ( dir );newfile->SetFileType ( type );newfile->SetGame(game);// first check if the file already existsfor ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f->GetFileType() != newfile->GetFileType() )continue;if ( f->GetName() != newfile->GetName () )continue;if ( f->GetDir() != newfile->GetDir() )continue;if ( f->GetGame() != newfile->GetGame() )continue;// must already exist, delete this onem_lFiles.remove(node, true);break;}_addFile(newfile);return newfile;}bool CBaseFile::AddFileNow ( CyString file, CyString dir, int type, CProgressInfo *progress ){C_File *f = AddFile ( file, dir, type );if ( !f->ReadFromFile () )return false;// compress the filereturn f->CompressData ( m_SHeader2.iDataCompression, progress );}C_File *CBaseFile::AppendFile ( CyString file, int type, int game, CyString dir, CProgressInfo *progress ){C_File *newfile = AddFile ( file, dir, type, game );if ( !newfile )return NULL;// read the file into memoryif ( newfile->ReadFromFile () ){// now compress the fileif ( newfile->CompressData ( m_SHeader2.iDataCompression, progress ) )return newfile;}else if ( newfile->GetLastError() == SPKERR_MALLOC ){if ( newfile->CompressFile ( progress ) )return newfile;}m_lFiles.pop_back ();delete newfile;return NULL;}C_File *CBaseFile::FindFileAt ( int filetype, int pos ){int count = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( file->GetFileType() != filetype )continue;if ( count == pos )return file;++count;}return NULL;}C_File *CBaseFile::FindFile ( CyString filename, int type, CyString dir, int game ){CyString lfile = filename.ToLower();lfile = lfile.FindReplace ( "\\", "/" );lfile = lfile.GetToken ( lfile.NumToken('/'), '/' );CListNode<C_File> *node = m_lFiles.Front();while ( node ){C_File *f = node->Data();node = node->next();if ( type != f->GetFileType() )continue;if ( dir != f->GetDir() )continue;if ( game != f->GetGame() )continue;if ( f->GetName().ToLower() == lfile )return f;}return NULL;}bool CBaseFile::RemoveFile ( CyString file, int type, CyString dir, int game ){C_File *f = FindFile (file, type, dir, game);if ( !f )return false;return RemoveFile ( f );}bool CBaseFile::RemoveFile ( C_File *file ){int count = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( f == file )return RemoveFile ( count );++count;}return false;}bool CBaseFile::RemoveFile ( int pos ){if ( (pos < 0) || (pos >= m_lFiles.size()) )return false;C_File *file = m_lFiles.Get ( pos );m_lFiles.erase ( pos + 1 );if ( file )delete file;_changed();return true;}void CBaseFile::RemoveAllFiles ( int type, int game ){if ( m_lFiles.empty() )return;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){if ( game > -1 ) {if ( node->Data()->GetGame() != game )continue;}if ( type == -1 || node->Data()->GetFileType() == type )node->DeleteData();}m_lFiles.RemoveEmpty();_changed();}void CBaseFile::RecompressAllFiles ( int type, CProgressInfo *progress ){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( progress )progress->UpdateFile(file);if ( file->GetCompressionType() == type )continue;if ( !file->GetData() )file->ReadFromFile();file->ChangeCompression ( type, progress );}}void CBaseFile::CompressAllFiles ( int type, CProgressInfo *progress, CProgressInfo *overallProgress, int level ){if ( overallProgress ) overallProgress->SetMax(m_lFiles.size());int iCount = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( progress )progress->UpdateFile(file);if ( !file->GetData() )file->ReadFromFile();file->CompressData ( type, progress, level );if ( overallProgress ) overallProgress->SetDone(++iCount);}}bool CBaseFile::UncompressAllFiles ( CProgressInfo *progress ){int countFile = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *fit = node->Data();if ( progress ){progress->UpdateFile ( fit );progress->UpdateProgress(countFile++, m_lFiles.size());}bool uncomprToFile = false;if ( progress )progress->SwitchSecond();if ( !fit->UncompressData ( progress ) ){if ( fit->GetCompressionType() == SPKCOMPRESS_7ZIP ){if ( !fit->UncompressToFile ( "temp", this, false, progress ) )return false;else{uncomprToFile = true;fit->SetFullDir ( "temp" );}}if ( !uncomprToFile )return false;}if ( progress )progress->SwitchSecond();}return true;}long CBaseFile::GetFullFileSize(){long fullsize = 1000;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )fullsize += node->Data()->GetUncompressedDataSize();if ( m_pIconFile )fullsize += m_pIconFile->GetUncompressedDataSize();return fullsize;}/*Func: GetEndOfLineInput: id - The file id for the current file to read fromline - Pointed to hold the line number thats readupper - true if it converts to uppercaseReturn: String - the string it has readDesc: Reads a string from a file, simlar to readLine() classes, reads to the end of the line in a file*/CyString CBaseFile::GetEndOfLine ( FILE *id, int *line, bool upper ){CyString word;char c = fgetc ( id );if ( c == -1 )return "";while ( (c != 13) && (!feof(id)) && (c != '\n') ){word += c;c = fgetc ( id );}if ( line )++(*line);if ( upper )return word.ToUpper();return word;}int CBaseFile::CountFiles ( int filetype ){int i = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( file->GetFileType() != filetype )continue;++i;}return i;}void CBaseFile::ClearNames (){m_lNames.clear(true);}void CBaseFile::RemoveLanguageName ( int lang ){SNames *n;for ( n = m_lNames.First(); n; n = m_lNames.Next() ){if ( n->iLanguage == lang ){m_lNames.RemoveCurrent();delete n;_changed();}}}void CBaseFile::AddLanguageName ( int lang, const Utils::String &name ){// first check for an existing languageSNames *n;for ( n = m_lNames.First(); n; n = m_lNames.Next() ){if ( n->iLanguage == lang ){n->sName = name;return;}}// not found, add a new entryn = new SNames;n->iLanguage = lang;n->sName = name;m_lNames.push_back ( n );_changed();}CyString CBaseFile::GetFullPackageName(CyString format, int lang){if ( format.Empty() )return GetFullPackageName(lang);CyString args[3] = { this->GetLanguageName(lang), this->version(), this->author() };return format.Args(args, 3);}/*Func: CreateFilesLineReturn: String - returns the full string for files listDesc: Creates a signle line list of all the files*/CyString CBaseFile::CreateFilesLine ( bool updateheader, CProgressInfo *progress ){CyString line;if ( progress ){progress->SetDone(0);progress->UpdateStatus(STATUS_COMPRESS);}if ( updateheader ){m_SHeader2.iNumFiles = 0;m_SHeader2.lFullSize = 0;}if ( m_pIconFile ){// no data, read it from fileif ( !m_pIconFile->GetData() )m_pIconFile->ReadFromFile ();// compress the fileif ( !m_pIconFile->CompressData ( m_SHeader2.iDataCompression, progress ) )m_pIconFile->SetDataCompression(SPKCOMPRESS_NONE);line += CyString("Icon:") + (m_pIconFile->GetDataSize() + (long)4) + ":" + m_pIconFile->GetUncompressedDataSize() + ":" + (long)m_pIconFile->GetCompressionType() + ":" + m_sIconExt + "\n";if ( updateheader ){++m_SHeader2.iNumFiles;m_SHeader2.lFullSize += (m_pIconFile->GetDataSize() + 4);}}for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( progress )progress->UpdateFile ( file );// no data, read it from fileif ( !file->GetData() ){if ( !file->ReadFromFile () ){if ( file->GetLastError() == SPKERR_MALLOC ){if ( !file->CompressFile ( progress ) )continue;}}}if ( !file->GetData() )continue;// compress the fileif ( !file->CompressData ( m_SHeader2.iDataCompression, progress ) ){file->SetDataCompression(SPKCOMPRESS_NONE);file->SetUncompressedDataSize(file->GetDataSize());}CyString command = GetFileTypeString ( file->GetFileType() );if ( command.Empty() )continue;if ( file->IsShared() )command = CyString("$") + command;if ( file->GetDir().Empty() )line += (command + ":" + (file->GetDataSize() + (long)4) + ":" + file->GetUncompressedDataSize() + ":" + (long)file->GetCompressionType() + ":" + (long)file->GetCreationTime() + ":" + ((file->IsCompressedToFile()) ? "1" : "0") + ":" + file->GetFilename() + ":GAME_" + (long)file->GetGame() + "\n");elseline += (command + ":" + (file->GetDataSize() + (long)4) + ":" + file->GetUncompressedDataSize() + ":" + (long)file->GetCompressionType() + ":" + (long)file->GetCreationTime() + ":" + ((file->IsCompressedToFile()) ? "1" : "0") + ":" + file->GetFilename() + ":" + file->GetDir() + ":GAME_" + (long)file->GetGame() + "\n");if ( updateheader ){++m_SHeader2.iNumFiles;m_SHeader2.lFullSize += (file->GetDataSize() + 4);}}return line;}/*################################################################################################ Reading Functions ################################################################################################*/void CBaseFile::ReadAllFilesToMemory (){// no file to read fromif ( this->filename().empty() ) return;// now open the fileCFileIO File(this->filename());if ( !File.startRead() ) return;// read the headerFile.readEndOfLine();// skip past valuesFile.seek(4 + m_SHeader.lValueCompressSize);// read the next headerFile.readEndOfLine();// skip past filesFile.seek(4 + m_SHeader2.lSize);if ( m_pIconFile ){if ( (!m_pIconFile->GetData()) && (!m_pIconFile->Skip()) )m_pIconFile->readFromFile(File, m_pIconFile->GetDataSize());elseFile.seek(4 + m_pIconFile->GetDataSize());}for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *fit = node->Data();if ( (!fit->GetData()) && (!fit->Skip()) )fit->readFromFile(File, fit->GetDataSize());elseFile.seek(4 + fit->GetDataSize());}File.close();}bool CBaseFile::ReadFileToMemory(C_File *f){if ( this->filename().empty() || !f ) return false; // no filename to load fromif ( f->GetData() && f->GetDataSize() ) return true; // already loaded the dataif ( !m_lFiles.FindData(f) ) return false; // unable to find file entry// now open the fileCFileIO *File = _startRead();if ( !File ) return false;if ( m_pIconFile ) File->seek(4 + m_pIconFile->GetDataSize());for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {C_File *fit = node->Data();if (fit == f ) {fit->readFromFile(*File, fit->GetDataSize());break;}else File->seek(4 + fit->GetDataSize());}delete File;return true;}CFileIO *CBaseFile::_startRead(){// no file to read fromif ( this->filename().empty() ) return NULL;// now open the fileCFileIO *File = new CFileIO(this->filename());if ( !File->startRead() ) return NULL;// read the headerFile->readEndOfLine();// skip past valuesFile->seek(4 + m_SHeader.lValueCompressSize);// read the next headerFile->readEndOfLine();// skip past filesFile->seek(4 + m_SHeader2.lSize);return File;}void CBaseFile::_addFile(C_File *file, bool dontChange){if ( !dontChange ) {file->UpdateSigned();_changed();}m_lFiles.push_back(file);_updateTextDB(file);}void CBaseFile::_updateTextDB(C_File *file){if ( !_pTextDB ) _pTextDB = new CTextDB();if ( file->GetFileType() == FILETYPE_TEXT ) {Utils::String baseFile = CFileIO(file->filePointer()).baseName();int lang = (baseFile.isin("-L")) ? baseFile.right(3) : baseFile.truncate(-4);// read in the text file to the database_pTextDB->parseTextFile(0, 0, file->filePointer(), lang);}}void CBaseFile::_resetTextDB(){if ( _pTextDB ) delete _pTextDB;_pTextDB = new CTextDB();for(CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next()) {_updateTextDB(node->Data());}}void CBaseFile::ReadIconFileToMemory (){if ( !m_pIconFile ) return;CFileIO *File = _startRead();if ( !File ) return;if ( (!m_pIconFile->GetData()) && (!m_pIconFile->Skip()) )m_pIconFile->readFromFile(*File, m_pIconFile->GetDataSize());elseFile->seek(4 + m_pIconFile->GetDataSize());delete File;}void CBaseFile::_install_adjustFakePatches(CPackages *pPackages){CyStringList lPatches;int startfake = pPackages->FindNextFakePatch();for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *fit = node->Data();// only do fake patchsif ( !fit->IsFakePatch() )continue;// we should only have cat and dat files, but lets check just incase they have been added incorrectlyif ( !fit->CheckFileExt ("cat") && !fit->CheckFileExt("dat") )continue;// search for the name on the listSStringList *opposite = lPatches.FindString(fit->GetBaseName());CyString newname;if ( opposite )newname = opposite->data;else{newname = CyString::Number((long)startfake).PadNumber(2);lPatches.PushBack(fit->GetBaseName(), newname);}// rename the filefit->FixOriginalName();CLog::logf(CLog::Log_Install, 2, "Adjusting fake patch number, %s => %s", fit->GetNameDirectory(this).c_str(), (newname + "." + fit->GetFileExt()).c_str());fit->SetName ( newname + "." + fit->GetFileExt() );// find the next gapif ( !opposite ) {startfake = pPackages->FindNextFakePatch(startfake + 1);}}}void CBaseFile::_install_renameText(CPackages *pPackages){int starttext = pPackages->FindNextTextFile();for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {C_File *fit = node->Data();if ( !fit->IsAutoTextFile() )continue;CyString newname = SPK::FormatTextName(starttext, pPackages->GetLanguage(), (pPackages->GetCurrentGameFlags() & EXEFLAG_TCTEXT));fit->FixOriginalName();CLog::logf(CLog::Log_Install, 2, "Adjusting text file, %s => %s", fit->GetNameDirectory(this).c_str(), (newname + "." + fit->GetFileExt()).c_str());fit->SetName ( newname + "." + fit->GetFileExt() );++starttext;}}bool CBaseFile::_install_setEnabled(bool bEnable, C_File *fit){if ( !bEnable ){if ( (fit->GetFileType() == FILETYPE_UNINSTALL) || (fit->GetFileType() == FILETYPE_README) || (fit->GetFileType() == FILETYPE_ADVERT) )bEnable = true;else if ( (fit->GetFileType() == FILETYPE_EXTRA) && (fit->GetDir().Left(7).ToLower() == "Extras/") )bEnable = true;else if ( (IsPatch()) && (fit->GetFileType() == FILETYPE_MOD) && (!fit->IsFakePatch()) )bEnable = true;if ( bEnable ) CLog::logf(CLog::Log_Install, 3, "Filetype(%d) is always enabled, setting enabled flag", fit->GetFileType());}return bEnable;}bool CBaseFile::_install_uncompress(C_File *fit, CProgressInfo *progress, CyStringList *errorStr, bool *uncomprToFile){*uncomprToFile = false;m_sLastError = fit->GetNameDirectory(this);m_iLastError = SPKERR_UNCOMPRESS;if ( !fit->UncompressData ( progress ) ){CLog::log(CLog::Log_Install, 2, "Failed to uncompress data, attempting file decompression");if ( fit->GetCompressionType() == SPKCOMPRESS_7ZIP ){if ( fit->UncompressToFile ( NullString, this, false, progress ) )*uncomprToFile = true;}if ( !uncomprToFile ){if ( errorStr )errorStr->PushBack(m_sLastError, ERRORLOG(SPKINSTALL_UNCOMPRESS_FAIL));CLog::log(CLog::Log_Install, 1, "Unable to decompress file, skipping");return false;}}ClearError ();return true;}bool CBaseFile::_install_checkVersion(C_File *pFile, const Utils::String &sDestination){// new check if we should install the file// first get the versionif ( !m_bOverrideFiles && pFile->ReadScriptVersion() ){CLog::log(CLog::Log_Install, 2, "Checking for existing file version");C_File checkfile;CyString checkfilename = sDestination;if ( !checkfilename.Empty() ) checkfilename += "/";checkfilename += pFile->GetNameDirectory(this);checkfile.SetFilename ( checkfilename );checkfile.SetFileType ( pFile->GetFileType() );if ( checkfile.CheckValidFilePointer() ) {if ( checkfile.ReadScriptVersion() > pFile->GetVersion() ) {CLog::log(CLog::Log_Install, 1, "Newer version of the file found in directory, skipping");return false;}}}return true;}Utils::String CBaseFile::_install_adjustFilepointer(C_File *pFile, bool bEnabled, const Utils::String &sDestination){CyString filename = sDestination;if ( !filename.Empty() ) filename += "/";if ( (IsPatch()) && (pFile->GetFileType() == FILETYPE_MOD) )pFile->SetDir ( CyString("Patch") );if ( pFile->IsInMod() ){if ( bEnabled )pFile->SetFilename(filename + pFile->GetInMod() + "::" + pFile->GetNameDirectory(this));elsepFile->SetFilename(filename + "PluginManager/DisabledFiles.cat::" + pFile->GetNameDirectory(this));}elsepFile->SetFilename ( filename + pFile->GetNameDirectory(this) );if ( !bEnabled ){if ( !pFile->IsInMod() ){if ( pFile->IsFakePatch() )pFile->SetFilename ( filename + "PluginManager/Disabled/FakePatches/FakePatch_" + this->GetNameValidFile() + "_" + this->author() + "_" + pFile->GetName() );else if ( pFile->IsAutoTextFile() )pFile->SetFilename ( filename + "PluginManager/Disabled/TextFiles/Text_" + this->GetNameValidFile() + "_" + this->author() + "_" + pFile->GetName() );elsepFile->SetFullDir ( filename + "PluginManager/Disabled/" + pFile->GetDirectory(this) );}pFile->SetDisabled(true);}CLog::logf(CLog::Log_Install, 2, "Adjusting the file pointer to correct install destintation, %s", pFile->GetFilePointer().c_str());return filename.ToString();}C_File *CBaseFile::_install_checkFile(C_File *pFile, CyStringList *errorStr, bool *bDoFile, CLinkList<C_File> *pFileList){if ( !pFile->IsFakePatch() && pFile->GetFileType() != FILETYPE_README ){C_File *cFile;for ( cFile = pFileList->First(); cFile; cFile = pFileList->Next() ){if ( !cFile->MatchFile(pFile) ) continue;if ( !m_bOverrideFiles && !cFile->CompareNew(pFile) ) {if ( errorStr ) errorStr->PushBack(pFile->GetNameDirectory(this), ERRORLOG(SPKINSTALL_SKIPFILE));CLog::log(CLog::Log_Install, 1, "Newer version of the file already installed, skipping");*bDoFile = false;}break;}return cFile;}return NULL;}bool CBaseFile::_install_checkFileEnable(C_File *pCheckFile, C_File *fit, const Utils::String &sDestination, bool bEnabled, CyStringList *errorStr){// found a file, check if its in the disabled directoryCyString dir = pCheckFile->GetFilePointer();dir = dir.GetToken ( 1, dir.NumToken ('/') - 1, '/' );CyString lastDir = dir.GetToken ( dir.NumToken('/'), '/' ).ToLower();// if its disabled, rename it so its enabledif ( ((pCheckFile->IsDisabled()) || (lastDir == "disabled") || (dir.ToLower().IsIn ("/disabled/"))) && (bEnabled) ){CLog::logf(CLog::Log_Install, 2, "Existing file, %s, is disabled, re-enabling it", pCheckFile->GetFilePointer().c_str());// first check if the directory existsif ( pCheckFile->IsInMod() ) {CyString tofile = pCheckFile->GetFilePointer().GetToken("::", 2, 2);CCatFile tocat;int err = tocat.Open ( fit->GetFilePointer().GetToken("::", 1, 1), "", CATREAD_CATDECRYPT, true );if ( (err == CATERR_NONE) || (err == CATERR_CREATED) ) {tocat.AppendFile(pCheckFile->GetFilePointer().ToString(), tofile.ToString());CLog::logf(CLog::Log_Install, 2, "Adding existing file into new mod File, %s => %s", fit->GetFilePointer().GetToken("::", 1, 1).c_str(), tofile.c_str());}CCatFile fromcat;err = fromcat.Open ( pCheckFile->GetFilePointer().GetToken("::", 1, 1), "", CATREAD_CATDECRYPT, false );if ( err == CATERR_NONE ) {fromcat.RemoveFile(tofile);CLog::logf(CLog::Log_Install, 2, "Removing file from existing mod, %s::%s", pCheckFile->GetFilePointer().GetToken("::", 1, 1).c_str(), tofile.c_str());}CLog::logf(CLog::Log_Install, 1, "Adjusting existing file name %s => %s", pCheckFile->GetFilePointer().c_str(), fit->GetFilePointer().c_str());pCheckFile->SetFilename ( fit->GetFilePointer() );CLog::logf(CLog::Log_Install, 2, "Adjusting In Mod setting, %s => %s", pCheckFile->GetInMod().c_str(), fit->GetInMod().c_str());pCheckFile->SetInMod(fit->GetInMod());}else {CyString to = pCheckFile->GetDirectory(this);CDirIO Dir(sDestination);if ( !Dir.Exists(to) ) {if ( !Dir.Create ( to ) ) {if ( errorStr ) errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));return false;}if ( errorStr ) errorStr->PushBack(to, ERRORLOG(SPKINSTALL_CREATEDIRECTORY));}CyString destfile = CyString(sDestination) + "/" + pCheckFile->GetNameDirectory(this);if ( CFileIO(destfile).ExistsOld() ) CFileIO::Remove(destfile.ToString());CLog::logf(CLog::Log_Install, 1, "Adjusting existing filename, %s => %s", pCheckFile->GetFilePointer().c_str(), destfile.c_str());rename ( pCheckFile->GetFilePointer().c_str(), destfile.c_str() );pCheckFile->SetFilename ( CyString(sDestination) + "/" + pCheckFile->GetNameDirectory(this) );}pCheckFile->SetDisabled(false);if ( errorStr ) errorStr->PushBack(pCheckFile->GetNameDirectory(this), ERRORLOG(SPKINSTALL_ENABLEFILE));}return true;}bool CBaseFile::_install_createDirectory(CDirIO &Dir, const Utils::String &sTo, C_File *pFile, CyStringList *errorStr){m_sLastError = sTo;if ( !sTo.isin ( "::" ) ){if ( !Dir.Exists(sTo) ){CLog::logf(CLog::Log_Install, 2, "Creating directory to install file into, %s", sTo.c_str());if ( !Dir.Create(sTo) ){if ( errorStr )errorStr->PushBack(CyString(sTo), ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));return false;}if ( errorStr )errorStr->PushBack(CyString(sTo), ERRORLOG(SPKINSTALL_CREATEDIRECTORY));}}else {CLog::logf(CLog::Log_Install, 2, "Adjusting file extension for file in mod, %s => %s", pFile->GetFilePointer().c_str(), CCatFile::PckChangeExtension(pFile->GetFilePointer()).c_str());pFile->SetFilename(CCatFile::PckChangeExtension(pFile->GetFilePointer()));}return true;}void CBaseFile::_install_writeFile(C_File *pFile, const Utils::String &sDestination, CyStringList *errorStr){m_iLastError = SPKERR_WRITEFILE;m_sLastError = pFile->GetFilePointer();CyString sInstalledFile = pFile->GetNameDirectory(this);if ( pFile->IsDisabled() ){sInstalledFile = pFile->GetFilePointer().Remove(sDestination);if ( sInstalledFile[0] == '/' || sInstalledFile[0] == '\\' )sInstalledFile.Erase(0, 1);}if ( !pFile->WriteFilePointer() ){CLog::log(CLog::Log_Install, 1, "Failed to write the file");if ( errorStr )errorStr->PushBack(sInstalledFile, ERRORLOG(SPKINSTALL_WRITEFILE_FAIL));}else{CLog::log(CLog::Log_Install, 1, "File written successfully");CLog::log(CLog::Log_Install, 2, "Checking signed status of the file");pFile->UpdateSigned();if ( errorStr )errorStr->PushBack(sInstalledFile, ERRORLOG(SPKINSTALL_WRITEFILE));switch(pFile->GetFileType()){case FILETYPE_SCRIPT:case FILETYPE_UNINSTALL:CLog::log(CLog::Log_Install, 2, "Updating file signature");pFile->UpdateSignature();break;}}}bool CBaseFile::InstallFiles ( CyString destdir, CProgressInfo *progress, CLinkList<C_File> *filelist, CyStringList *errorStr, bool enabled, CPackages *packages ){//TODO: add errorStr and progress as member variablesif ( enabled ) {this->_install_adjustFakePatches(packages);if ( packages ) this->_install_renameText(packages);}bool bFailed = false;CDirIO Dir(destdir);int fileCount = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *fit = node->Data();// start the install process, check if we need to the file enabled or disabledCLog::logf(CLog::Log_Install, 1, "Preparing to install file: %s", fit->GetNameDirectory(this).c_str());bool fileEnabled = _install_setEnabled(enabled, fit);// check if the file is for the correct game versionif ( fit->GetGame() && packages->GetGame() ) {if ( fit->GetGame() != packages->GetGame() ) {CLog::logf(CLog::Log_Install, 1, "File didn't match game version, skipping, %d != %d", fit->GetGame(), packages->GetGame());continue;}}// update the progress display to show we are processing this fileif ( progress ) {if ( progress->IsSecond() ) progress->SwitchSecond();progress->UpdateFile ( fit );progress->UpdateProgress(fileCount++, m_lFiles.size());progress->SwitchSecond();}// first uncompress the file//TODO: add this flag to C_Filebool uncomprToFile;if ( !this->_install_uncompress(fit, progress, errorStr, &uncomprToFile) ) {bFailed = true;continue;}bool dofile = _install_checkVersion(fit, destdir.ToString());// change file pointerUtils::String sFilename = _install_adjustFilepointer(fit, fileEnabled, destdir.ToString());C_File *adjustPointer = NULL;bool checkFile = dofile;if ( filelist ) {C_File *cFile = (checkFile) ? _install_checkFile(fit, errorStr, &dofile, filelist) : NULL;// no matching file found, adding to main listif ( !cFile ) filelist->push_back ( fit );else{// if the file is not enabled, we need to check for any that might be enabledif ( !fileEnabled ) //_install_checkDisabled(cFile, destdir, errorStr);{//TODO: check what this is actually doingif ( !cFile->GetUsed() ){CFileIO rFile(cFile->GetFilePointer());if ( rFile.exists() ){if ( errorStr ){if ( rFile.remove() )errorStr->PushBack(cFile->GetFilePointer().Remove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE));elseerrorStr->PushBack(cFile->GetFilePointer().Remove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE_FAIL));}}cFile->SetFilename(fit->GetFilePointer());cFile->SetDisabled(true);}else{fit->SetFullDir ( CyString(sFilename) + fit->GetDirectory(this) );fit->SetDisabled(false);}}// move it to enabledelse {if ( !this->_install_checkFileEnable(cFile, fit, destdir.ToString(), fileEnabled, errorStr) ) {bFailed = true;continue;}}adjustPointer = cFile;if ( dofile ) adjustPointer->SetCreationTime(fit->GetCreationTime());}}if ( dofile ){// uncompressed to file, rename and moveif ( uncomprToFile ){m_iLastError = SPKERR_WRITEFILE;CyString to = fit->GetDirectory(this);if ( !fileEnabled ) to = CyString("PluginManager/Disabled/") + to;if ( !_install_createDirectory(Dir, to.ToString(), fit, errorStr) ) {bFailed = true;continue;}int err = 1;m_sLastError = to;if ( !fit->GetTempFile ().Empty() ) err = rename ( fit->GetTempFile().c_str(), to.c_str() );if ( err ) {bFailed = true;continue;}}//otherwise, just extract the fileelse{// old file is found in list, switch to using new oneif ( (filelist) && (adjustPointer) ) {adjustPointer->CopyData(fit, false);CLog::log(CLog::Log_Install, 2, "Copying data into existing file");}CyString fpointer = fit->GetFilePointer();m_iLastError = SPKERR_CREATEDIRECTORY;CyString dir = fit->GetFilePointer().GetToken ( "/", 1, fit->GetFilePointer().NumToken("/") - 1 );dir = dir.Remove(destdir);if ( dir[0] == '/' || dir[0] == '\\' ) dir.Erase(0, 1);if ( !_install_createDirectory(Dir, dir.ToString(), fit, errorStr) ) {bFailed = true;continue;}_install_writeFile(fit, destdir.ToString(), errorStr);}ClearError ();}if ( adjustPointer ){CLog::log(CLog::Log_Install, 2, "Adjusting pointers to existing file and deleting new file pointer");node->ChangeData(adjustPointer);delete fit;}CLog::log(CLog::Log_Install, 1, "File installation completed");}// now clear or data memoryCLog::log(CLog::Log_Install, 2, "Delting temporary file data from memory");for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) node->Data()->DeleteData();return !bFailed;}/*######################################################################################################*//*Func: ParseHeaderInput: Header String - string formated directly from the fileReturn: Boolean - If string is a valid headerDesc: Splits up the main header string to get all required settings*/bool CBaseFile::ParseHeader ( CyString header ){if ( !this->CheckHeader(header.GetToken ( 1, ';' ).ToString()) )return false;m_SHeader.fVersion = header.GetToken ( 2, ';' ).ToFloat();if ( m_SHeader.fVersion > FILEVERSION )return false;m_SHeader.iValueCompression = header.GetToken ( 3, ';' ).ToInt();m_SHeader.lValueCompressSize = header.GetToken ( 4, ';' ).ToLong();return true;}bool CBaseFile::CheckHeader(const Utils::String header) const{if ( header.Compare("BaseCycrow") )return true;return false;}/*Func: ParseFileHeaderInput: Header String - string formated directly from the fileReturn: Boolean - If string is a valid headerDesc: Splits up the file header string to get all required settings*/bool CBaseFile::ParseFileHeader ( CyString header ){if ( header.GetToken ( 1, ';' ) != "FileHeader" )return false;m_SHeader2.iNumFiles = header.GetToken ( 2, ';' ).ToInt();m_SHeader2.lSize = header.GetToken ( 3, ';' ).ToInt();m_SHeader2.lFullSize = header.GetToken ( 4, ';' ).ToInt();m_SHeader2.iFileCompression = header.GetToken ( 5, ';' ).ToInt();m_SHeader2.iDataCompression = header.GetToken ( 6, ';' ).ToInt();return true;}/*Func: ParseValueLineInput: String - single line from a file to setReturn: Boolean - returns true if value existsDesc: Reads the line and assigns the parameters for the file*/bool CBaseFile::ParseValueLine(const Utils::String &sLine){Utils::String first = sLine.token(" ", 1);Utils::String rest = sLine.tokens(" ", 2);if ( first.Compare("Name:") ) this->setName(rest);else if ( first.Compare("Author:") ) this->setAuthor(rest);else if ( first.Compare("Version:") ) this->setVersion(rest);else if ( first.Compare("fGameVersion:") ) {if ( m_lGames.Back() ) {m_lGames.Back()->Data()->sVersion = rest;}}else if ( first.Compare("GameVersion:") ) {if ( m_lGames.Back() ) {m_lGames.Back()->Data()->iVersion = rest;}}else if ( first.Compare("Game:") )this->AddGameCompatability(rest, "");else if ( first.Compare("GameCompat:") )this->AddGameCompatability(rest.token(" ", 1), rest.tokens(" ", 2));else if ( first.Compare("GameCompatExact:") )this->AddGameCompatability(rest.token(" ", 1), rest.tokens(" ", 2));else if ( first.Compare("Date:") ) this->setCreationDate(rest);else if ( first.Compare("WebAddress:") ) this->setWebAddress(rest);else if ( first.Compare("WebSite:") ) this->setWebSite(rest);else if ( first.Compare("Email:") ) this->setEmail(rest);else if ( first.Compare("WebMirror1:") || first.Compare("Mirror1:") || first.Compare("WebMirror:") )this->AddWebMirror(rest);else if ( first.Compare("WebMirror2:") || first.Compare("Mirror2:") )this->AddWebMirror(rest);else if ( first.Compare("PluginType:") ) this->setPluginType(rest);else if ( first.Compare("Desc:") ) this->setDescription(rest);else if ( first.Compare("UninstallAfter:") ) this->addUninstallText(ParseLanguage(rest.token("|", 1)), false, rest.tokens("|", 2));else if ( first.Compare("UninstallBefore:") ) this->addUninstallText(ParseLanguage(rest.token("|", 1)), true, rest.tokens("|", 2));else if ( first.Compare("InstallAfter:") ) this->addInstallText(ParseLanguage(rest.token("|", 1)), false, rest.tokens("|", 2));else if ( first.Compare("InstallBefore:") ) this->addInstallText(ParseLanguage(rest.token("|", 1)), true, rest.tokens("|", 2));else if ( first.Compare("ScriptName:") )AddLanguageName(ParseLanguage(rest.token(":", 1)), rest.token(":", 2));else if ( first.Compare("GameChanging:") ) this->setGameChanging(rest);else if ( first.Compare("EaseOfUse:") ) this->setEaseOfUse(rest);else if ( first.Compare("Recommended:") ) this->setRecommended(rest);else if ( first.Compare("NeededLibrary:") )this->AddNeededLibrary(rest.token("||", 1), rest.token("||", 2), rest.token("||", 3));else if ( first.Compare("FakePatchBefore:") )this->AddFakePatchOrder(false, rest.token("||", 1), rest.token("||", 2));else if ( first.Compare("FakePatchAfter:") )this->AddFakePatchOrder(true, rest.token("||", 1), rest.token("||", 2));else if ( first.Compare("ForumLink:") ) this->setForumLink(rest);elsereturn false;return true;}int CBaseFile::ParseLanguage(const Utils::String &lang) const{int langID = lang;if ( !langID ) {if ( lang.Compare("english") ) return 44;else if ( lang.Compare("default") ) return 0;else if ( lang.Compare("german") ) return 49;else if ( lang.Compare("russian") ) return 7;else if ( lang.Compare("spanish") ) return 34;else if ( lang.Compare("french") ) return 33;}return langID;}/*Func: ReadValuesInput: String - values in one long lineDesc: splits the values data into each line to read the data*/void CBaseFile::ReadValues ( CyString values ){int num = 0;CyString *lines = values.SplitToken ( '\n', &num );for ( int i = 0; i < num; i++ )ParseValueLine ( lines[i].ToString() );CLEANSPLIT(lines, num)}/*Func: ParseFilesLineInput: String - single line from a file to setReturn: Boolean - returns true if value existsDesc: Reads the line and assigns the parameters for the file*/bool CBaseFile::ParseFilesLine ( CyString line ){if ( !line.IsIn(":") )return false;CyString command = line.GetToken ( 1, ':' );long size = line.GetToken ( 2, ':').ToInt ();long usize = line.GetToken ( 3, ':').ToInt ();long compression = line.GetToken ( 4, ':').ToInt ();if ( command == "Icon" ){m_sIconExt = line.GetToken ( 5, ':' );m_pIconFile = new C_File ();m_pIconFile->SetDataSize ( size - 4 );m_pIconFile->SetDataCompression ( compression );m_pIconFile->SetUncompressedDataSize ( usize );return true;}time_t time = line.GetToken ( 5,':' ).ToLong();bool compressToFile = (line.GetToken ( 6, ':').ToInt() == 1) ? true : false;CyString name = line.GetToken ( 7, ':' );CyString dir = line.GetToken ( 8, ':' );if ( name.Empty() )return true;bool shared = false;if ( command.Left(1) == "$" ){shared = true;command.Erase ( 0, 1 );}int type = -1;if ( command == "Script" )type = FILETYPE_SCRIPT;else if ( command == "Text" )type = FILETYPE_TEXT;else if ( command == "Readme" )type = FILETYPE_README;else if ( command == "Map" )type = FILETYPE_MAP;else if ( command == "Mod" )type = FILETYPE_MOD;else if ( command == "Uninstall" )type = FILETYPE_UNINSTALL;else if ( command == "Sound" )type = FILETYPE_SOUND;else if ( command == "Mission" )type = FILETYPE_MISSION;else if ( command == "Extra" )type = FILETYPE_EXTRA;else if ( command == "Screen" )type = FILETYPE_SCREEN;else if ( command == "Backup" )type = FILETYPE_BACKUP;else if ( command == "Advert" )type = FILETYPE_ADVERT;else if ( command == "ShipScene" )type = FILETYPE_SHIPSCENE;else if ( command == "CockpitScene" )type = FILETYPE_COCKPITSCENE;else if ( command == "ShipOther" )type = FILETYPE_SHIPOTHER;else if ( command == "ShipModel" )type = FILETYPE_SHIPMODEL;if ( type == -1 )return false;C_File *file = new C_File ();if ( dir.Left(5).Compare("GAME_") ) {file->SetGame(dir.GetToken("_", 2, 2).ToInt());dir = NullString;}else if ( line.NumToken(":") >= 9 ) {file->SetGame(line.GetToken(":", 9, 9).GetToken("_", 2, 2).ToInt());}file->SetFileType ( type );file->SetCreationTime ( time );file->SetName ( name );file->SetDir ( dir );file->SetDataSize ( size - 4 );file->SetDataCompression ( compression );file->SetUncompressedDataSize ( usize );file->SetShared ( shared );file->SetCompressedToFile ( compressToFile );_addFile(file, true);return true;}/*Func: ParseFilesInput: String - values in one long lineDesc: splits the files data into each line to read the data*/void CBaseFile::ReadFiles ( CyString values ){int num = 0;CyString *lines = values.SplitToken ( '\n', &num );for ( int i = 0; i < num; i++ )ParseFilesLine ( lines[i] );CLEANSPLIT(lines, num)}/*Func: ReadFileInput: filename - the name of the file to open and readreaddata - If falses, dont read the files to memory, just read the headers and valuesReturn: boolean - return ture if acceptable formatDesc: Opens and reads the spk file and loads all data into class*/bool CBaseFile::ReadFile ( CyString filename, int readtype, CProgressInfo *progress ){CFileIO File(filename.ToString());if ( !File.startRead() ) return false;bool ret = this->readFile(File, readtype, progress);if ( ret ) this->setFilename(filename.ToString());File.close();return ret;}int CBaseFile::_read_Header(std::fstream &stream, int iReadType, int iMaxProgress, CProgressInfo *pProgress){int doneLen = 0;// read data to memoryunsigned char *readData;try {readData = new unsigned char[m_SHeader.lValueCompressSize];}catch (std::exception &e) {CLog::logf(CLog::Log_IO, 2, "CBaseFile::_read_Header() unable to malloc [header], %d (%s)", m_SHeader.lValueCompressSize, e.what());return -1;}unsigned char size[4];stream.read((char *)size, 4);stream.read((char *)readData, m_SHeader.lValueCompressSize);unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];// check for zlib compressionif ( m_SHeader.iValueCompression == SPKCOMPRESS_ZLIB ) {// uncomress the datatry {unsigned char *uncompr = new unsigned char[uncomprLen];int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader.lValueCompressSize );// update the progress for each sectionif ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(2, iMaxProgress);if ( err == Z_OK ) this->ReadValues ( CyString ((char *)uncompr) );doneLen = uncomprLen;delete uncompr;}catch (std::exception &e) {CLog::logf(CLog::Log_IO, 2, "CBaseFile::_read_Header() unable to malloc [uncompr], %d (%s)", uncomprLen, e.what());delete []readData;return -1;}}else if ( m_SHeader.iValueCompression == SPKCOMPRESS_7ZIP ) {long len = uncomprLen;unsigned char *compr = LZMADecode_C ( readData, m_SHeader.lValueCompressSize, (size_t*)&len, NULL );// update the progress for each sectionif ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(2, iMaxProgress);if ( compr ) ReadValues ( CyString ((char *)compr) );}// no compressionelseReadValues ( CyString ((char *)readData) );delete []readData;return doneLen;}int CBaseFile::_read_FileHeader(std::fstream &stream, int iReadType, int iMaxProgress, int iDoneLen, CProgressInfo *pProgress){unsigned char *readData;try {readData = new unsigned char[m_SHeader2.lSize];}catch (std::exception &e) {CLog::logf(CLog::Log_IO, 2, "CBaseFile::_read_FileHeader() unable to malloc [header], %d (%s)", m_SHeader2.lSize, e.what());return -1;}unsigned char size[4];stream.read((char *)size, 4);stream.read((char *)readData, m_SHeader2.lSize);unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];// check for zlib compressionif ( m_SHeader.iValueCompression == SPKCOMPRESS_ZLIB ) {if ( uncomprLen < (unsigned long)iDoneLen ) uncomprLen = iDoneLen;try {unsigned char *uncompr = new unsigned char[uncomprLen];int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader2.lSize );// update the progress for each sectionif ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(5, iMaxProgress);if ( err == Z_OK ) ReadFiles ( CyString ((char *)uncompr) );delete uncompr;}catch (std::exception &e) {CLog::logf(CLog::Log_IO, 2, "CBaseFile::_read_FileHeader() unable to malloc [uncompr], %d (%s)", uncomprLen, e.what());delete []readData;return -1;}}else if ( m_SHeader.iValueCompression == SPKCOMPRESS_7ZIP ){long len = uncomprLen;unsigned char *compr = LZMADecode_C ( readData, m_SHeader2.lSize, (size_t*)&len, NULL );// update the progress for each sectionif ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(5, iMaxProgress);if ( compr ) ReadFiles ( CyString ((char *)compr) );}elseReadFiles ( CyString ((char *)readData) );delete []readData;return true;}bool CBaseFile::readFile(CFileIO &File, int readtype, CProgressInfo *progress){ClearError ();// first read the headerif ( !ParseHeader(File.readEndOfLine()) ) return false;if ( readtype == SPKREAD_HEADER ) return true;// update the progress for each sectionint maxProgress = (readtype == SPKREAD_VALUES) ? 3 : 6;if ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(1, maxProgress);long doneLen = 0;// next read the data values for the spk filesif ( m_SHeader.lValueCompressSize ) doneLen = this->_read_Header(File.stream(), readtype, maxProgress, progress);// update the progress for each sectionif ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(3, maxProgress);if ( readtype == SPKREAD_VALUES ) return true;// next should be the next headerif ( !ParseFileHeader(File.readEndOfLine()) ) return false;// clear the current file listm_lFiles.clear(true);if ( _pTextDB ) {delete _pTextDB;_pTextDB = NULL;}// update the progress for each sectionif ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(4, maxProgress);if ( m_SHeader2.lSize ) this->_read_FileHeader(File.stream(), readtype, maxProgress, doneLen, progress);// file mismatchlong numfiles = m_lFiles.size();if ( m_pIconFile ) ++numfiles;if ( m_SHeader2.iNumFiles != numfiles ) {m_iLastError = SPKERR_FILEMISMATCH;return false;}// update the progress for each sectionif ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(6, maxProgress);if ( readtype == SPKREAD_ALL ) {int fileCount = 2;if ( m_pIconFile ) m_pIconFile->readFromFile(File, m_pIconFile->GetDataSize());// ok finally we need to read all the filefor ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {node->Data()->readFromFile(File, node->Data()->GetDataSize());if ( progress ) progress->UpdateProgress(fileCount++, m_lFiles.size() + 2);}m_bFullyLoaded = true;}return true;}bool CBaseFile::IsMod(){// check for any mod files that are not fake patchsfor ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() ){C_File *f = fNode->Data();if ( f->GetFileType() != FILETYPE_MOD )continue;if ( !f->IsFakePatch() )return true;}return false;}bool CBaseFile::IsFakePatch() const{// check for any mod files that are not fake patchsfor ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() ){C_File *f = fNode->Data();if ( f->GetFileType() != FILETYPE_MOD )continue;if ( f->IsFakePatch() )return true;}return false;}Utils::String CBaseFile::CreateValuesLine () const{Utils::String values("Name: ");values += this->name() + "\n";values += "Author: " + this->author() + "\n";values += "Version: " + this->version() + "\n";if ( !this->creationDate().empty() )values += "Date: " + this->creationDate() + "\n";if ( !this->webAddress().empty() ) values += "WebAddress: " + this->webAddress() + "\n";if ( !this->webSite().empty() ) values += "WebSite: " + this->webSite() + "\n";if ( !this->email().empty() ) values += "Email: " + this->email() + "\n";if ( !this->forumLink().empty() ) values += "ForumLink: " + this->forumLink() + "\n";for ( SStringList *str = m_lMirrors.Head(); str; str = str->next )values += Utils::String("WebMirror: ") + str->str.ToString() + "\n";if ( !this->description().empty() ) {Utils::String desc = this->description();desc = desc.findReplace("<newline>", "<br>");desc = desc.findReplace("\n", "<br>");desc.remove('\r');values += "Desc: " + desc + "\n";}for ( CListNode<SGameCompat> *gc = m_lGames.Front(); gc; gc = gc->next() ) {if ( !gc->Data()->sVersion.empty() )values += Utils::String("GameCompatExact: ") + (long)gc->Data()->iGame + " " + gc->Data()->sVersion + "\n";elsevalues += Utils::String("GameCompat: ") + (long)gc->Data()->iGame + " " + (long)gc->Data()->iVersion + "\n";}if ( m_bSigned )values += "Signed\n";for ( int j = 0; j < 2; j++ ) {const CInstallText *text;Utils::String textStart;switch(j) {case 0: textStart = "Uninstall"; text = this->uninstallText(); break;case 1: textStart = "Install"; text = this->installText(); break;}for ( unsigned int i = 0; i < text->count(); i++ ) {int iLang = text->language(i);if ( !text->getBefore(iLang).empty() ) values += textStart + "Before: " + (long)iLang + "|" + text->getBefore(iLang) + "\n";if ( !text->getAfter(iLang).empty() ) values += textStart + "After: " + (long)iLang + "|" + text->getAfter(iLang) + "\n";}}values += Utils::String("GameChanging: ") + (long)gameChanging() + "\n";values += Utils::String("EaseOfUse: ") + (long)easeOfUse() + "\n";values += Utils::String("Recommended: ") + (long)recommended() + "\n";for ( CListNode<SNames> *nNode = m_lNames.Front(); nNode; nNode = nNode->next() )values += Utils::String("ScriptName: ") + (long)nNode->Data()->iLanguage + ":" + nNode->Data()->sName + "\n";for ( CListNode<SNeededLibrary> *libNode = m_lNeededLibrarys.Front(); libNode; libNode = libNode->next() ) {SNeededLibrary *l = libNode->Data();values += (CyString("NeededLibrary: ") + l->sName + "||" + l->sAuthor + "||" + l->sMinVersion + "\n").ToString();}for ( SStringList *fpbNode = m_lFakePatchBefore.Head(); fpbNode; fpbNode = fpbNode->next )values += (CyString("FakePatchBefore: ") + fpbNode->str + "||" + fpbNode->data + "\n").ToString();for ( SStringList *fpaNode = m_lFakePatchAfter.Head(); fpaNode; fpaNode = fpaNode->next )values += (CyString("FakePatchAfter: ") + fpaNode->str + "||" + fpaNode->data + "\n").ToString();values += Utils::String("PluginType: ") + (long)this->pluginType() + "\n";return values;}/*Func: WriteFileInput: filename - The filename of the spk file to write toDesc: Writes the data to an spk file*/bool CBaseFile::WriteFile ( CyString filename, CProgressInfo *progress ){CFileIO File(filename);if ( File.startWrite() ) return WriteData(File, progress);return false;}bool CBaseFile::WriteHeader(CFileIO &file, int valueheader, int valueComprLen){return file.write("BaseCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen);}bool CBaseFile::WriteData(CFileIO &file, CProgressInfo *progress ){int valueheader = m_SHeader.iValueCompression, fileheader = m_SHeader.iValueCompression;if ( valueheader == SPKCOMPRESS_7ZIP )valueheader = SPKCOMPRESS_ZLIB;if ( fileheader == SPKCOMPRESS_7ZIP )fileheader = SPKCOMPRESS_ZLIB;// get the script valuesthis->UpdateSigned(true);CyString values = this->CreateValuesLine();// compress the valuesint valueUncomprLen = (int)values.Length();unsigned long valueComprLen = 0;unsigned char *valueCompr = NULL;bool compressed = false;if ( valueheader == SPKCOMPRESS_ZLIB ){valueComprLen = valueUncomprLen;if ( valueComprLen < 100 )valueComprLen = 200;else if ( valueComprLen < 1000 )valueComprLen *= 2;valueCompr = (unsigned char *)calloc((unsigned int)valueComprLen, 1);int err = compress ( (unsigned char *)valueCompr, &valueComprLen, (const unsigned char *)values.c_str(), (unsigned long)values.Length(), 0 );if ( err == Z_OK )compressed = true;}if ( !compressed ){valueComprLen = valueUncomprLen;valueCompr = (unsigned char *)calloc((unsigned int)valueComprLen, 1);memcpy ( valueCompr, values.c_str(), valueComprLen );valueheader = SPKCOMPRESS_NONE;}// write the main header to the fileif ( !this->WriteHeader(file, valueheader, valueComprLen) ) return false;// write the compressed data to filefile.put(static_cast<unsigned char>(valueUncomprLen >> 24));file.put(static_cast<unsigned char>(valueUncomprLen >> 16));file.put(static_cast<unsigned char>(valueUncomprLen >> 8));file.put(static_cast<unsigned char>(valueUncomprLen));file.write(valueCompr, valueComprLen);free ( valueCompr );// now compress the files header// create the files valuesCyString files = CreateFilesLine ( true, progress );// compress the files valueslong fileUncomprLen = (long)files.Length(), fileComprLen = fileUncomprLen;unsigned char *fileCompr = NULL;compressed = false;if ( fileUncomprLen ){if ( fileheader == SPKCOMPRESS_ZLIB ){if ( fileComprLen < 100 )fileComprLen = 200;else if ( fileComprLen < 1000 )fileComprLen *= 2;fileCompr = (unsigned char *)calloc((unsigned int)fileComprLen, 1);int err = compress ( (unsigned char *)fileCompr, (unsigned long *)&fileComprLen, (const unsigned char *)files.c_str(), (unsigned long)files.Length(), 0 );if ( err == Z_OK )compressed = true;}}// if unable to compress, store it as plain textif ( !compressed ){fileComprLen = fileUncomprLen;fileCompr = (unsigned char *)calloc((unsigned int)fileComprLen, 1);memcpy ( fileCompr, files.c_str(), fileComprLen );fileheader = SPKCOMPRESS_NONE;}// now write the file headerm_SHeader2.lSize = fileComprLen;file.write("FileHeader;%d;%ld;%ld;%d;%d\n", m_SHeader2.iNumFiles, m_SHeader2.lSize, m_SHeader2.lFullSize, fileheader, m_SHeader2.iDataCompression);file.put(static_cast<unsigned char>(fileUncomprLen >> 24));file.put(static_cast<unsigned char>(fileUncomprLen >> 16));file.put(static_cast<unsigned char>(fileUncomprLen >> 8));file.put(static_cast<unsigned char>(fileUncomprLen));file.write(fileCompr, fileComprLen);free ( fileCompr );if ( progress ){progress->UpdateStatus(STATUS_WRITE);progress->SetDone(0);long max = 0;for ( CListNode<C_File> *file = m_lFiles.Front(); file; file = file->next() )max += file->Data()->GetDataSize();if ( m_pIconFile )max += m_pIconFile->GetDataSize();progress->SetMax(max);}// now finally, write all the file dataif ( m_pIconFile ) {if ( progress ) progress->UpdateFile(m_pIconFile);file.writeSize(m_pIconFile->GetUncompressedDataSize());file.write(m_pIconFile->GetData(), m_pIconFile->GetDataSize());if ( progress ) progress->IncDone(m_pIconFile->GetDataSize());}for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *f = node->Data();if ( progress ) progress->UpdateFile(f);file.writeSize(f->GetUncompressedDataSize());unsigned char *data = f->GetData();size_t remaining = f->GetDataSize();while ( remaining ) {size_t writeSize = WRITECHUNK;if ( writeSize > remaining ) writeSize = remaining;if ( file.write(data, writeSize) ) {data += writeSize;remaining -= writeSize;}if ( progress ) progress->IncDone((int)writeSize);}}_changed();return true;}bool CBaseFile::ExtractFile ( C_File *file, CyString dir, bool includedir, CProgressInfo *progress ){if ( ReadFileToMemory ( file ) ){// now finally, uncompress the filelong len = 0;unsigned char *data = file->UncompressData ( &len, progress );if ( !data ){// attempt a file decompressif ( file->GetCompressionType() == SPKCOMPRESS_7ZIP ){if ( file->UncompressToFile ( dir, this, includedir, progress ) )return true;}return false;}if ( !file->WriteToDir ( dir, this, includedir, NullString, data, len ) )return false;return true;}elsereturn false;}bool CBaseFile::ExtractFile ( int filenum, CyString dir, bool includedir, CProgressInfo *progress ){// invalid valusif ( filenum < 0 )return false;// out of rangeif ( filenum > m_lFiles.size() )return false;// get the file pointerC_File *file = m_lFiles.Get ( filenum );return ExtractFile ( file, dir, includedir, progress );}bool CBaseFile::ExtractAll ( CyString dir, int game, bool includedir, CProgressInfo *progress ){// no file to read fromif ( this->filename().empty() )return false;// now open the fileCFileIO *File = _startRead();if ( !File ) return false;// now were in the file section// skip past each oneif ( m_pIconFile ) File->seek(4 + m_pIconFile->GetDataSize ());CDirIO Dir(dir);for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {C_File *fit = node->Data();if ( progress ) progress->UpdateFile ( fit );if ( (!fit->GetDataSize ()) || (!fit->GetData()) ) {if ( !fit->readFromFile(*File, fit->GetDataSize()) ) return false;}elseFile->seek(fit->GetDataSize());if ( game ) {if ( fit->GetGame() && fit->GetGame() != game )continue;}// create directory firstDir.Create(fit->GetDirectory(this));long size = 0;unsigned char *data = fit->UncompressData (&size, progress);if ( (!data) && (fit->GetCompressionType() == SPKCOMPRESS_7ZIP) ) {if ( !fit->UncompressToFile ( dir, this, includedir, progress ) ) return false;}else if ( (!data) || (!fit->WriteToDir ( dir, this, includedir, NullString, data, size )) ) return false;}delete File;return true;}bool CBaseFile::UpdateSigned (bool updateFiles){m_bSigned = true;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){C_File *file = node->Data();if ( updateFiles )file->UpdateSigned();// extra, text, soundtrack, readmes and screen files do not require a modified game directlyif ( (file->GetFileType() == FILETYPE_EXTRA) || (file->GetFileType() == FILETYPE_TEXT) || (file->GetFileType() == FILETYPE_SOUND) || (file->GetFileType() == FILETYPE_SCREEN) || (file->GetFileType() == FILETYPE_README) )continue;// mods and maps always need modified gameelse if ( (file->GetFileType() == FILETYPE_MOD) || (file->GetFileType() == FILETYPE_MAP) ){m_bSigned = false;break;}// else should be a script file, script or uninstall type// all scripts must be signed, if any are not, then no signed statusif ( !file->IsSigned () ){m_bSigned = false;break;}}return m_bSigned;}bool CBaseFile::IsPackageNeeded(const Utils::String &scriptName, const Utils::String &author){for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() ){SNeededLibrary *l = node->Data();if ( l->sName.Compare(scriptName) && l->sAuthor.Compare(author) )return true;}return false;}SNeededLibrary *CBaseFile::FindPackageNeeded(const Utils::String &scriptName, const Utils::String &author){for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() ){SNeededLibrary *l = node->Data();if ( l->sName.Compare(scriptName) && l->sAuthor.Compare(author) )return l;}return NULL;}void CBaseFile::RemoveFakePatchOrder(CyString scriptName, CyString author){RemoveFakePatchOrder(true, scriptName, author);RemoveFakePatchOrder(false, scriptName, author);}void CBaseFile::RemoveFakePatchOrder(bool after, CyString scriptName, CyString author){CyStringList *list;if ( after )list = &m_lFakePatchAfter;elselist = &m_lFakePatchBefore;for ( SStringList *str = list->Head(); str; str = str->next ){// already addedif ( str->str.Compare(scriptName) && str->data.Compare(author) )str->remove = true;}list->RemoveMarked();}void CBaseFile::AddFakePatchOrder(bool after, CyString scriptName, CyString author){CyStringList *list;if ( after )list = &m_lFakePatchAfter;elselist = &m_lFakePatchBefore;// check if the package already existsfor ( SStringList *str = list->Head(); str; str = str->next ){// already addedif ( str->str.Compare(scriptName) && str->data.Compare(author) )return;}// cant have it on both listRemoveFakePatchOrder(!after, scriptName, author);list->PushBack(scriptName, author);}void CBaseFile::AddNeededLibrary(const Utils::String &scriptName, const Utils::String &author, const Utils::String &minVersion){SNeededLibrary *l = this->FindPackageNeeded(scriptName, author);if ( !l ){l = new SNeededLibrary;l->sName = scriptName;l->sAuthor = author;m_lNeededLibrarys.push_back(l);}l->sMinVersion = minVersion;}void CBaseFile::RemovePackageNeeded(const Utils::String &scriptName, const Utils::String &author){SNeededLibrary *l = this->FindPackageNeeded(scriptName, author);if ( l ){m_lNeededLibrarys.remove(l);delete l;}}void CBaseFile::ClearNeededPackages(){SNeededLibrary *ns = this->FindPackageNeeded("<package>", "<author>");Utils::String version;if ( ns ) version = ns->sMinVersion;for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )node->DeleteData();m_lNeededLibrarys.clear();if ( !version.empty() ) this->AddNeededLibrary("<package>", "<author>", version);}bool CBaseFile::GeneratePackagerScript(bool wildcard, CyStringList *list, bool datafile){list->PushBack("#");list->PushBack(CyString("# Packager Script"));list->PushBack(CyString("# -- Generated by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2) + " --");list->PushBack("#");list->PushBack("");if ( !datafile ){list->PushBack("# Variable for your game directory, where to get files from");list->PushBack("# $PATH variable is used to get the current path");list->PushBack("Variable: $GAMEDIR $PATH");list->PushBack("");}list->PushBack("# The name of the script");list->PushBack(CyString("Name: ") + this->name());list->PushBack("");list->PushBack("# The author of the script, ie, you");list->PushBack(CyString("Author: ") + this->author());list->PushBack("");list->PushBack("# The creation data, when it was created");if ( datafile )list->PushBack(CyString("Date: ") + this->creationDate());else {list->PushBack("# $DATE variable is used to get the current date");list->PushBack("Date: $DATE");}list->PushBack("");list->PushBack("# The version of script");if ( datafile )list->PushBack(CyString("Version: ") + this->version());else{list->PushBack("# $ASK variable is used to get an input when creating");list->PushBack("Version: $ASK");}list->PushBack("");if ( !m_lGames.empty() ) {list->PushBack("# The game version the script is for <game> <version> (can have multiple games)");for ( SGameCompat *g = m_lGames.First(); g; g = m_lGames.Next() ) {Utils::String game = CBaseFile::ConvertGameToString(g->iGame);if ( !g->sVersion.empty() ){game += " ";game += g->sVersion;}else{game += " ";game += (long)g->iVersion;}list->PushBack(CyString("Game: ") + game);}list->PushBack("");}if ( !this->description().empty() ) {list->PushBack("# The description of the script, displays when installing");list->PushBack(CyString("Description: ") + this->description());list->PushBack("");}if ( !this->webSite().empty() ) {list->PushBack("# A link to the website for the script, ie for an online help page");list->PushBack(CyString("WebSite: ") + this->webSite());list->PushBack("");}if ( !this->forumLink().empty() ) {list->PushBack("# A direct link to the thread in the egosoft forum");list->PushBack(CyString("ForumLink: ") + this->forumLink());list->PushBack("");}if ( !this->webAddress().empty() ) {list->PushBack("# A link to the address for the update file");list->PushBack(CyString("WebAddress: ") + this->webAddress());list->PushBack("");}if ( !this->email().empty() ) {list->PushBack("# The email address of the author, to allow users to contract if needed");list->PushBack(CyString("Email: ") + this->email());list->PushBack("");}if ( m_lMirrors.Count() ){list->PushBack("# A link to the mirror address for the update file, can have many of these");for ( SStringList *node = m_lMirrors.Head(); node; node = node->next )list->PushBack(CyString("WebMirror: ") + node->str);list->PushBack("");}if ( m_bAutoGenerateUpdateFile ){list->PushBack("# Auto generate the package update file when created");list->PushBack("GenerateUpdateFile");}if ( m_lNeededLibrarys.size() ){list->PushBack("# Needed Library dependacies, require these to be installed");for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )list->PushBack(CyString("Depend: ") + node->Data()->sName + "|" + node->Data()->sMinVersion + "|" + node->Data()->sAuthor);list->PushBack("");}if ( !_noRatings() ){list->PushBack("# Ratings Values, 0 to 5, <ease> <changing> <recommended>");list->PushBack(CyString("Ratings: ") + (long)easeOfUse() + " " + (long)gameChanging() + " " + (long)recommended());list->PushBack("");}if ( m_lNames.size() ){list->PushBack("# Package names, uses different names for different languages");for ( CListNode<SNames> *node = m_lNames.Front(); node; node = node->next() )list->PushBack(CyString("ScriptName: ") + (long)node->Data()->iLanguage + " " + node->Data()->sName);list->PushBack("");}for ( int j = 0; j < 2; j++ ) {Utils::String installText = (j == 0) ? "Install" : "Uninstall";const CInstallText *pText = (j == 0) ? this->installText() : this->uninstallText();if ( pText->any() ){list->PushBack(CyString("# " + installText + " Texts, display text before and/or after " + installText + "ing to inform the use of special conditions"));for ( unsigned int i = 0; i < pText->count(); i++ ) {long iLang = pText->language(i);if ( !pText->getBefore(iLang).empty() ) list->PushBack(CyString(installText + "Before: ") + iLang + " " + pText->getBefore(iLang));if ( !pText->getAfter(iLang).empty() ) list->PushBack(CyString(installText + "After: ") + iLang + " " + pText->getAfter(iLang));}list->PushBack("");}}list->PushBack("# Plugin Type, the type the plugin is, mainly used to show users the type, types include: Normal, Stable, Experimental, Cheat, Mod");switch ( this->pluginType() ){case PLUGIN_NORMAL:list->PushBack("PluginType: Normal");break;case PLUGIN_STABLE:list->PushBack("PluginType: Stable");break;case PLUGIN_EXPERIMENTAL:list->PushBack("PluginType: Experimental");break;case PLUGIN_CHEAT:list->PushBack("PluginType: Cheat");break;case PLUGIN_MOD:list->PushBack("PluginType: Mod");break;}list->PushBack("");return true;}bool CBaseFile::GeneratePackagerScriptFile(bool wildcard, CyStringList *list){// now do files and wildcardsCyStringList files;for ( CListNode<C_File> *f = m_lFiles.Front(); f; f = f->next() ){CyString name = "$GAMEDIR/";bool done = false;if ( wildcard ){CyString base = f->Data()->GetBaseName();if ( f->Data()->GetFileType() == FILETYPE_SCRIPT ){if ( base.GetToken(".", 1, 1).Compare("plugin") || base.GetToken(".", 1, 1).Compare("lib") ){name += f->Data()->GetDirectory(this) + "/" + base.GetToken(".", 1, 2) + ".*";done = true;}else if ( base.GetToken(".", 1, 1).Compare("al") && !base.GetToken(".", 2, 2).Compare("plugin") ){name += f->Data()->GetDirectory(this) + "/" + base.GetToken(".", 1, 2) + ".*";done = true;}}else if ( f->Data()->GetFileType() == FILETYPE_TEXT ){if ( base.IsIn("-L") ){name += f->Data()->GetDirectory(this) + "/" + base.GetToken("-L", 1, 1) + "-L*";done = true;}else{name += f->Data()->GetDirectory(this) + "/*" + base.Right(4) + ".*";done = true;}}}if ( !done )name += f->Data()->GetNameDirectory(this);if ( !f->Data()->GetDir().Empty() ){name += "|";name += f->Data()->GetDir();}files.PushBack(CyString("GAME ") + CBaseFile::ConvertGameToString(f->Data()->GetGame()) + " " + name, f->Data()->GetFileTypeString(), true);}if ( !files.Empty() ){list->PushBack("# Files List, all the files to add, can include wild cards");for ( SStringList *node = files.Head(); node; node = node->next )list->PushBack(node->data + ": " + node->str);list->PushBack("");}return true;}Utils::String CBaseFile::GetAutosaveName(){return this->name() + "-V" + this->version() + "-" + this->creationDate().findReplace("/", ".");}bool CBaseFile::CheckGameCompatability(int game){if ( m_lGames.empty() )return true; // no game compatability added, assume its ok for allfor ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {if ( node->Data()->iGame == 0 || node->Data()->iGame == game )return true;}return false;}bool CBaseFile::CheckGameVersionCompatability(int game, CyString sVersion, int iVersion){if ( m_lGames.empty() )return true; // no game compatability added, assume its ok for allbool foundAll = false;for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {if ( node->Data()->iGame == 0 )foundAll = true;else if ( node->Data()->iGame == game ) { // now check the versionif ( node->Data()->sVersion.empty() ) {if ( CyString(node->Data()->sVersion).CompareVersion(sVersion) == COMPARE_OLDER )return false;return true;}else {if ( node->Data()->iVersion > iVersion )return false;return true;}}}return foundAll;}bool CBaseFile::RemoveGameCompatability(int game){for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {if ( node->Data()->iGame == game ) {m_lGames.remove(node);_changed();return true;}}return false;}SGameCompat *CBaseFile::GetGameCompatability(int game){for ( CListNode<SGameCompat> *node = m_lGames.Front(); node; node = node->next() ) {if ( node->Data()->iGame == game ) {return node->Data();}}return NULL;}void CBaseFile::AddGameCompatability(int game, const Utils::String &version){// first check if we already have it on the listSGameCompat *Found = this->GetGameCompatability(game);if ( !Found ) {Found = new SGameCompat;m_lGames.push_back(Found);}Found->iGame = game;Found->iVersion = -1;Found->sVersion = "";if ( version.isin(".") || !version.isNumber() )Found->sVersion = version;elseFound->iVersion = version;_changed();}bool CBaseFile::LoadPackageData(const Utils::String &sFirst, const Utils::String &sRest){if ( sFirst.Compare("Name") ) this->setName(sRest);else if ( sFirst.Compare("Author") ) this->setAuthor(sRest);else if ( sFirst.Compare("ScriptName") )AddLanguageName(ParseLanguage(sRest.token(" ", 1)), sRest.tokens(" ", 2));else if ( sFirst.Compare("UninstallBefore") ) this->addUninstallText(ParseLanguage(sRest.token(" ", 1)), true, sRest.tokens(" ", 2));else if ( sFirst.Compare("UninstallAfter") ) this->addUninstallText(ParseLanguage(sRest.token(" ", 1)), false, sRest.tokens(" ", 2));else if ( sFirst.Compare("InstallBefore") ) this->addInstallText(ParseLanguage(sRest.token(" ", 1)), true, sRest.tokens(" ", 2));else if ( sFirst.Compare("InstallAfter") ) this->addInstallText(ParseLanguage(sRest.token(" ", 1)), false, sRest.tokens(" ", 2));else if ( sFirst.Compare("Date") ) this->setCreationDate(sRest);else if ( sFirst.Compare("Version") ) this->setVersion(sRest);else if ( sFirst.Compare("GameVersion") )this->AddGameCompatability(-1, sRest);else if ( sFirst.Compare("PluginType") ) {if ( sRest.isNumber() ) this->setPluginType(sRest);else if ( sRest.Compare("Normal") ) this->setPluginType(PLUGIN_NORMAL);else if ( sRest.Compare("Stable") ) this->setPluginType(PLUGIN_STABLE);else if ( sRest.Compare("Experimental") ) this->setPluginType(PLUGIN_EXPERIMENTAL);else if ( sRest.Compare("Cheat") ) this->setPluginType(PLUGIN_CHEAT);else if ( sRest.Compare("Mod") ) this->setPluginType(PLUGIN_MOD);}// new versionelse if ( sFirst.Compare("GenerateUpdateFile") )m_bAutoGenerateUpdateFile = true;else if ( sFirst.Compare("Game") ){Utils::String sGame = sRest.token(" ", 1);this->AddGameCompatability(CBaseFile::GetGameFromString(sGame), sRest.token(" ", 2));}else if ( sFirst.Compare("Description") ) this->setDescription(sRest);else if ( sFirst.Compare("AutoSave") || sFirst.Compare("AutoExport") || sFirst.Compare("AutoRarExport") || sFirst.Compare("AutoZipExport") ){Utils::String filename = sRest;Utils::String cdate = this->creationDate().findReplace("/", ".").remove(' ');if ( filename.isin("$AUTOSAVE") ){if ( this->GetType() == TYPE_XSP )filename = filename.findReplace("$AUTOSAVE", "$NAME-V$VERSION-$CDATE.xsp");elsefilename = filename.findReplace("$AUTOSAVE", "$NAME-V$VERSION-$CDATE.spk");}filename = filename.findReplace("$NAME", this->name().remove(' '));filename = filename.findReplace("$AUTHOR", this->author().remove(' '));filename = filename.findReplace("$DATE", cdate);filename = filename.findReplace("$CDATE", cdate);filename = filename.findReplace("$VERSION", this->version());if ( sFirst.Compare("AutoZipExport") || sFirst.Compare("AutoExport") )this->setExportFilename(CFileIO(filename).ChangeFileExtension("zip").ToString());else if ( sFirst.Compare("AutoRarExport") )this->setExportFilename(CFileIO(filename).ChangeFileExtension("rar").ToString());elsethis->setFilename(filename);}else if ( sFirst.Compare("WebSite") ) this->setWebSite(sRest);else if ( sFirst.Compare("ForumLink") || sFirst.Compare("Forum") ) this->setForumLink(sRest);else if ( sFirst.Compare("Email") ) this->setEmail(sRest);else if ( sFirst.Compare("WebAddress") ) this->setWebAddress(sRest);else if ( sFirst.Compare("WebMirror") )this->AddWebMirror(sRest);else if ( sFirst.Compare("WebMirror1") )this->AddWebMirror(sRest);else if ( sFirst.Compare("WebMirror2") )this->AddWebMirror(sRest);else if ( sFirst.Compare("Ftp") )m_sFtpAddr = sRest;else if ( sFirst.Compare("Ratings") ) _setRatings(sRest.token(" ", 1), sRest.token(" ", 2), sRest.token(" ", 3));else if ( sFirst.Compare("EaseOfUse") ) setEaseOfUse(sRest);else if ( sFirst.Compare("GameChanging")) setGameChanging(sRest);else if ( sFirst.Compare("Recommended") ) setRecommended(sRest);else if ( sFirst.Compare("Depend") ){Utils::String version = sRest.token("|", 2);Utils::String name = sRest.token("|", 1);Utils::String author = sRest.tokens("|", 3);this->AddNeededLibrary(name, author, version);}else if ( sFirst.Compare("DependPackage") ){CPackages p;CBaseFile *spk = p.OpenPackage(sRest, 0, 0, SPKREAD_VALUES);if ( spk ){this->AddNeededLibrary(spk->name(), spk->author(), spk->version());delete spk;}}else if ( sFirst.Compare("Icon") ){C_File *icon = new C_File(sRest.c_str());if ( icon->ReadFromFile() )this->SetIcon(icon, CFileIO(sRest).GetFileExtension());}else{Utils::String checkType = sFirst;bool shared = false;if ( checkType.left(6).Compare("Shared") ){checkType = sFirst.right(-6);shared = true;}// now check type nameint filetype = GetFileTypeFromString(checkType);if ( filetype != -1 )this->AddFileScript(filetype, shared, sRest);else if ( !checkType.Compare("changelog") )return false;}return true;}void CBaseFile::AddFileScript(int filetype, bool shared, CyString rest){CyString dir;if ( rest.IsIn("|") ){dir = rest.GetToken("|", 2);rest = rest.GetToken("|", 1, 1);}int game = 0;if ( rest.GetToken(" ", 1, 1).Left(4).Compare("GAME") ) {game = CBaseFile::GetGameFromString(rest.GetToken(" ", 2, 2).ToString());rest = rest.GetToken(" ", 3);}rest = rest.FindReplace("\\", "/");// wild cardsif ( rest.IsAnyIn("*?") ){CDirIO Dir(CFileIO(rest).GetDir());CyStringList *dirList = Dir.DirList();if ( dirList ){for ( SStringList *strNode = dirList->Head(); strNode; strNode = strNode->next ){CyString file = Dir.File(strNode->str);if ( file.WildMatch(rest) ){C_File *newfile = this->AppendFile(file, filetype, game, dir);if ( newfile )newfile->SetShared(shared);}}delete dirList;}}else{C_File *file = this->AppendFile(rest, filetype, game, dir);if ( file )file->SetShared(shared);}}CyString CBaseFile::GetFullFileSizeString() { return SPK::GetSizeString ( this->GetFullFileSize() ); }// used for a multiple spk fileunsigned char *CBaseFile::CreateData(size_t *size, CProgressInfo *progress){if ( this->WriteFile("temp.dat", progress) ) {CFileIO File("temp.dat");File.setAutoDelete(true);return File.readAll(size);}return NULL;}void CBaseFile::ConvertNormalMod(C_File *f, CyString to){C_File *match = this->FindMatchingMod(f);if ( match ){// file linkif ( !match->GetData() )match->ReadFromFile();match->ChangeBaseName(to);}// file linkif ( !f->GetData() )f->ReadFromFile();f->ChangeBaseName(to);}void CBaseFile::ConvertAutoText(C_File *f){CyString to;if ( f->GetBaseName().IsIn("-L") )to = CyString("0000-L") + f->GetBaseName().GetToken("-L", 2, 2);else if ( f->GetBaseName().IsIn("-l") )to = CyString("0000-L") + f->GetBaseName().GetToken("-l", 2, 2);elseto = f->GetBaseName().Left(-4) + "0000";// file linkif ( !f->GetData() )f->ReadFromFile();f->ChangeBaseName(to);}void CBaseFile::ConvertFakePatch(C_File *f){// find next available fake patchint num = 0;bool found = true;while ( found ){++num;found = false;CyString find = CyString::Number(num).PadNumber(2);for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){if ( node->Data()->GetFileType() != FILETYPE_MOD )continue;if ( !node->Data()->IsFakePatch() )continue;if ( node->Data()->GetBaseName().Compare(find) ){found = true;break;}}}CyString to = CyString::Number(num).PadNumber(2);C_File *match = this->FindMatchingMod(f);// file linkif ( !f->GetData() )f->ReadFromFile();f->ChangeBaseName(to);if ( match ){// file linkif ( !match->GetData() )match->ReadFromFile();match->ChangeBaseName(to);}}C_File *CBaseFile::FindMatchingMod(C_File *f){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){if ( node->Data()->GetFileType() != FILETYPE_MOD )continue;if ( f->GetFileExt().Compare(node->Data()->GetFileExt()) )continue;if ( f->GetBaseName().Compare(node->Data()->GetBaseName()) )return node->Data();}return NULL;}void CBaseFile::RenameFile(C_File *f, CyString baseName){if ( f->GetFileType() == FILETYPE_MOD ){C_File *match = this->FindMatchingMod(f);if ( match )match->ChangeBaseName(baseName);}// need to edit the fileif ( f->GetFileType() == FILETYPE_SCRIPT || f->GetFileType() == FILETYPE_UNINSTALL )f->RenameScript(baseName);f->ChangeBaseName(baseName);}CyString CBaseFile::CreateUpdateFile(CyString dir){CyString file = this->GetNameValidFile() + "_" + this->author() + ".dat";file.RemoveChar(' ');CyStringList write;write.PushBack(CyString("Package: ") + this->name());write.PushBack(CyString("Author: ") + this->author());write.PushBack(CyString("Version: ") + this->version());write.PushBack(CyString("File: ") + CFileIO(this->filename()).GetFilename());CFileIO File(dir + "/" + file);if ( File.WriteFile(&write) )return File.GetFullFilename();return NullString;}CyString CBaseFile::ErrorString(int error, CyString errorStr){if ( error == SPKERR_NONE ) return NullString;CyString err;switch(error){case SPKERR_MALLOC:err = "Memory Failed";break;case SPKERR_FILEOPEN:err = "Failed to open file";break;case SPKERR_FILEREAD:err = "Failed to read file";break;case SPKERR_UNCOMPRESS:err = "Failed to Uncompress";break;case SPKERR_WRITEFILE:err = "Failed to write file";break;case SPKERR_CREATEDIRECTORY:err = "Failed to create directory";break;case SPKERR_FILEMISMATCH:err = "File count mismatch";break;}if ( !err.Empty() ){if ( !errorStr.Empty() ){err += " (";err += errorStr + ")";}return err;}return CyString((long)error);}bool CBaseFile::SaveToArchive(CyString filename, int game, CProgressInfo *progress){TCHAR buf[5000];wsprintf(buf, L"%hs", filename.c_str());HZIP hz = CreateZip(buf, 0);if ( !hz ) return false;// read files and compressReadAllFilesToMemory();if ( !UncompressAllFiles(progress) ){CloseZip(hz);return false;}for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ){if ( game != -1 ) {if ( game && node->Data()->GetGame() && node->Data()->GetGame() != game )continue;if ( !game && node->Data()->GetGame() )continue;}CyString fname = node->Data()->GetNameDirectory(this);// create the directorywsprintf(buf, L"%hs", fname.c_str());ZipAdd(hz, buf, node->Data()->GetData(), node->Data()->GetDataSize());}// add the data fileCyStringList list;if ( this->GeneratePackagerScript(false, &list, true) ){if ( CFileIO("test.tmp").WriteFile(&list) ){ZipAdd(hz, L"pluginmanager.txt", L"test.tmp");CFileIO::Remove("test.tmp");}}CloseZip(hz);return true;}int CBaseFile::GetGameFromString(const Utils::String &sGame){int iGame = GAME_ALL;if ( sGame.Compare("ALL") )iGame = GAME_ALL;else if ( sGame.Compare("X3") )iGame = GAME_X3;else if ( sGame.Compare("X2") )iGame = GAME_X2;else if ( sGame.Compare("X3TC") )iGame = GAME_X3TC;else if ( sGame.Compare("X3AP") )iGame = GAME_X3AP;else if ( sGame.Compare("XREBIRTH") )iGame = GAME_XREBIRTH;else if ( sGame.isNumber() )iGame = sGame;return iGame;}Utils::String CBaseFile::ConvertGameToString(int iGame){Utils::String game = "ALL";switch(iGame) {case GAME_ALL:game = "ALL";break;case GAME_X2:game = "X2";break;case GAME_X3:game = "X3";break;case GAME_X3TC:game = "X3TC";break;case GAME_X3AP:game = "X3AP";break;case GAME_XREBIRTH:game = "XREBIRTH";break;default:game = (long)iGame;}return game;}bool CBaseFile::IsGameInPackage(int game){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {if ( node->Data()->GetGame() == game )return true;}return false;}bool CBaseFile::IsAnyGameInPackage(){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {if ( node->Data()->GetGame() )return true;}return false;}Utils::String builtInWares(){Utils::String str;str += "28;0;0;0;0;59;5753;0;35714;1;1;0;35714;-100000;0;0;SS_WARE_SW_NEW1;\n";str += "28;0;0;0;0;60;5763;0;33232;1;1;0;33232;0;1043;0;SS_WARE_SW_NEW2;\n";str += "28;0;0;0;0;61;5773;0;21428;1;1;0;21428;0;1043;0;SS_WARE_SW_NEW3;\n";str += "28;0;0;0;0;62;5783;0;56;1;1;0;56;-100000;0;0;SS_WARE_SW_NEW4;\n";str += "28;0;0;0;0;63;5793;0;88;1;1;0;88;-100000;0;0;SS_WARE_SW_NEW5;\n";str += "28;0;0;0;0;64;5803;0;283;1;1;0;283;-100000;0;0;SS_WARE_SW_NEW6;\n";str += "28;0;0;0;0;65;5813;0;383;1;1;0;383;-100000;0;0;SS_WARE_SW_NEW7;\n";str += "28;0;0;0;0;66;5823;0;1389;1;1;0;1389;-100000;1043;0;SS_WARE_SW_NEW8;\n";str += "28;0;0;0;0;67;5833;0;3396;1;1;0;3396;-100000;0;0;SS_WARE_SW_NEW9;\n";str += "28;0;0;0;0;68;5843;0;4215;1;1;0;4215;-100000;0;0;SS_WARE_SW_NEW10;\n";str += "28;0;0;0;0;69;5853;0;5635;1;1;0;5635;-100000;0;0;SS_WARE_SW_NEW11;\n";str += "28;0;0;0;0;70;5863;0;65735;1;1;0;65735;-100000;0;0;SS_WARE_SW_NEW12;\n";str += "28;0;0;0;0;71;5873;0;17857;1;1;0;17857;333;1043;0;SS_WARE_SW_NEW13;\n";str += "28;0;0;0;0;72;5883;0;21428;1;1;0;21428;0;1043;0;SS_WARE_SW_NEW14;\n";str += "28;0;0;0;0;73;5893;0;324515;1;1;0;324515;-100000;0;0;SS_WARE_SW_NEW15;\n";str += "28;0;0;0;0;74;5903;0;638508;1;1;0;638508;-100000;0;0;SS_WARE_SW_NEW16;\n";str += "28;0;0;0;0;75;5913;0;225755;1;1;0;225755;-100000;0;0;SS_WARE_SW_NEW17;\n";str += "28;0;0;0;0;76;5923;0;1931535;1;1;0;1931535;1000;0;0;SS_WARE_SW_NEW18;\n";str += "28;0;0;0;0;77;5933;0;2209150;1;1;0;2209150;-100000;0;0;SS_WARE_SW_NEW19;\n";str += "28;0;0;0;0;78;5943;0;6727565;1;1;0;6727565;-100000;0;0;SS_WARE_SW_NEW20;\n";str += "28;0;0;0;0;85;9999;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_1;\n";str += "28;0;0;0;0;86;15053;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_2;\n";str += "28;0;0;0;0;87;15063;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_3;\n";str += "28;0;0;0;0;88;15073;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_4;\n";str += "28;0;0;0;0;89;15083;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_5;\n";str += "28;0;0;0;0;90;15093;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_6;\n";return str;}void CBaseFile::_addWaresToList(int iLang, CLinkList<SWareEntry> &list, const Utils::String &wares, enum WareTypes eType){int totalWares = 0;Utils::String *w = wares.tokenise("\n", &totalWares);for(int i = 0; i < totalWares; i++) {int textId = w[i].token(";", 7).toLong();int useLang = iLang;if ( !_pTextDB->exists(useLang, 17, textId) )useLang = 44;if ( !_pTextDB->exists(useLang, 17, textId) )useLang = 49;if ( _pTextDB->exists(useLang, 17, textId) ) {SWareEntry *ware = new SWareEntry;ware->name = _pTextDB->get(useLang, 17, textId);ware->description = _pTextDB->get(useLang, 17, textId + 1);ware->id = w[i].token(";", -2);ware->relval = w[i].token(";", 9).toLong();ware->notority = w[i].token(";", 14).toLong();ware->type = eType;ware->position = i;ware->package = this;list.push_back(ware);}}CLEANSPLIT(w, totalWares);}bool CBaseFile::readWares(int iLang, CLinkList<SWareEntry> &list, const Utils::String &empWares){_pTextDB->setLanguage(iLang);// now go through all emp wares and get the ones we have text for_addWaresToList(iLang, list, empWares, Ware_EMP);_addWaresToList(iLang, list, builtInWares(), Ware_BuiltIn);return true;}bool CBaseFile::_readCommands(int iLang, int iStartID, CLinkList<SCommandSlot> &list){_pTextDB->setLanguage(iLang);for(int i = 2; i <= 13; i++) {for(int j = 0; j < 64; j++) {int id = (i * 100) + j;if ( _pTextDB->exists(iLang, iStartID + 2, id) ) {SCommandSlot *slot = new SCommandSlot;list.push_back(slot);slot->id = _pTextDB->get(iLang, iStartID, id);slot->name = _pTextDB->get(iLang, iStartID + 2, id);slot->shortName = _pTextDB->get(iLang, iStartID + 3, id);slot->info = _pTextDB->get(iLang, iStartID + 14, id);slot->slot = id;slot->package = this;}}}for(int i = 1400; i <= 2000; i++) {if ( _pTextDB->exists(iLang, iStartID + 2, i) ) {SCommandSlot *slot = new SCommandSlot;list.push_back(slot);slot->id = _pTextDB->get(iLang, iStartID, i);slot->name = _pTextDB->get(iLang, iStartID + 2, i);slot->shortName = _pTextDB->get(iLang, iStartID + 3, i);slot->info = _pTextDB->get(iLang, iStartID + 14, i);slot->slot = i;slot->package = this;}}for(int i = 6000; i <= 9999; i++) {if ( _pTextDB->exists(iLang, iStartID + 2, i) ) {SCommandSlot *slot = new SCommandSlot;list.push_back(slot);slot->id = _pTextDB->get(iLang, iStartID, i);slot->name = _pTextDB->get(iLang, iStartID + 2, i);slot->shortName = _pTextDB->get(iLang, iStartID + 3, i);slot->info = _pTextDB->get(iLang, iStartID + 14, i);slot->slot = i;slot->package = this;}}return true;}bool CBaseFile::readWingCommands(int iLang, CLinkList<SCommandSlot> &list){return _readCommands(iLang, 2028, list);}bool CBaseFile::readCommands(int iLang, CLinkList<SCommandSlot> &list){return _readCommands(iLang, 2008, list);}int CBaseFile::FindFirstGameInPackage(){for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {if ( node->Data()->GetGame() )return node->Data()->GetGame();}return 0;}bool CBaseFile::IsMultipleGamesInPackage(){int game = 0;for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {if ( node->Data()->GetGame() ) {if ( game != node->Data()->GetGame() )return true;game = node->Data()->GetGame();}}return false;}