Rev 254 | 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 "Packages.h"
#include "TextDB.h"
#include <Package/InstallText.h>
// remove these eventually
using 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;
_bCombineFiles = 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();
}
Utils::WString CBaseFile::getFullPackageName(int language, const Utils::WString &byString) const
{
return getFullPackageName(language, true, byString);
}
Utils::WString CBaseFile::getFullPackageName(int language, bool includeVersion, const Utils::WString &byString) const
{
Utils::WString p = this->name(language);
if (includeVersion)
{
p += L" V";
p += this->version();
}
p += L" ";
p += byString + L" " + this->author();
return p;
}
Utils::WString CBaseFile::getFullPackageName(const Utils::WString &format, int lang) const
{
if (format.empty())
return getFullPackageName(lang);
Utils::WString args[3] = { this->name(lang), this->version(), this->author() };
return format.args(args, 3);
}
void CBaseFile::Delete ()
{
m_lTempFiles.clear();
m_lFiles.clear(true);
if ( m_pIconFile )
{
delete m_pIconFile;
m_pIconFile = NULL;
}
if ( _pTextDB ) {
delete _pTextDB;
_pTextDB = NULL;
}
}
/*
##########################################################################################
################## Base Class Functions ##################
##########################################################################################
*/
const CLinkList<C_File> &CBaseFile::fileList() const
{
return m_lFiles;
}
CLinkList<C_File> &CBaseFile::fileList(FileType type, CLinkList<C_File>& list) const
{
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;
}
CLinkList<C_File> &CBaseFile::fileList(FileType type)
{
m_lTempFiles.clear();
return fileList(type, m_lTempFiles);
}
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(const Utils::WString &filename, float *version )
{
CFileIO File(filename);
if ( !File.startRead() ) return 0;
Utils::WString line = File.readEndOfLine();
Utils::WString type = line.token(L";", 1);
File.close();
// check for old version
if ( line.left(3) == L"HiP" ) return SPKFILE_OLD;
// check for format
if ( version ) *version = line.token(L";", 2);
if ( type == L"BaseCycrow" ) return SPKFILE_BASE;
if ( type == L"SPKCycrow" ) return SPKFILE_SINGLE;
if ( type == L"XSPCycrow" ) return SPKFILE_SINGLESHIP;
if ( type == L"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();
}
}
Utils::WString CBaseFile::getNameValidFile() const
{
Utils::WString 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->fileType() != file->fileType() )
continue;
if ( f->name() != file->name () )
continue;
if ( f->dir() != file->dir() )
continue;
if ( f->game() != file->game() )
continue;
m_lFiles.remove(node, true);
break;
}
_addFile(file);
}
C_File *CBaseFile::addFile(const Utils::WString &file, const Utils::WString &dir, FileType type, int game, bool packed)
{
C_File *newfile = new C_File(file);
newfile->setDir(dir);
newfile->setFileType(type);
newfile->setGame(game);
if (packed)
{
if (newfile->PCKFile())
newfile->setFilename(CFileIO(newfile->filePointer()).changeFileExtension(L"pck"));
}
// first check if the file already exists
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *f = node->Data();
if ( f->fileType() != newfile->fileType() )
continue;
if ( f->name() != newfile->name () )
continue;
if ( f->dir() != newfile->dir() )
continue;
if (f->game() != newfile->game())
{
//same file, for different game, check if they are the same and combine them
if (_bCombineFiles)
{
char *data = NULL;
char *newData = NULL;
size_t size = 0;
size_t newSize = 0;
if (f->GetData() && f->GetDataSize())
data = (char *)f->UncompressData((long *)&size, NULL);
else if(f->isExternalFile())
data = CFileIO(f->filePointer()).ReadToData(&size);
if (newfile->GetData() && newfile->GetDataSize())
newData = (char *)f->UncompressData((long *)&newSize, NULL);
else
newData = CFileIO(newfile->filePointer()).ReadToData(&newSize);
// compare bytes
bool matched = false;
if (size == newSize)
{
matched = true;
for (unsigned long i = 0; i < size; ++i)
{
if (data[i] != newData[i])
{
matched = false;
break;
}
}
}
if (data)
delete data;
if (newData)
delete newData;
if (matched)
{
if(!f->game() || f->game() == GAME_ALLNEW)
newfile->setGame(0);
else
newfile->setGame(newfile->game() | f->game());
m_lFiles.remove(node, true);
break;
}
}
continue;
}
// must already exist, delete this one
m_lFiles.remove(node, true);
break;
}
_addFile(newfile);
return newfile;
}
bool CBaseFile::addFileNow(const Utils::WString &file, const Utils::WString &dir, FileType type, CProgressInfo* progress)
{
C_File* f = addFile(file, dir, type);
if (!f->ReadFromFile())
return false;
// compress the file
return f->CompressData(m_SHeader2.iDataCompression, progress);
}
C_File *CBaseFile::appendFile(const Utils::WString &file, int type, int game, bool packed, const Utils::WString &dir, CProgressInfo *progress )
{
C_File *newfile = addFile(file, dir, static_cast<FileType>(type), game, packed);
if ( !newfile )
return NULL;
// read the file into memory
if (newfile->GetData() && newfile->GetDataSize())
{
// now compress the file
if (newfile->CompressData(m_SHeader2.iDataCompression, progress))
return newfile;
}
if ( newfile->ReadFromFile () )
{
// now compress the file
if ( 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(FileType filetype, size_t pos) const
{
size_t 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(const Utils::WString &filename, FileType type, const Utils::WString &dir, int game) const
{
Utils::WString lfile = CFileIO(filename.lower()).filename();
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->dir())
continue;
if (game && (game != f->game()))
continue;
if (f->name().lower() == lfile)
return f;
}
return NULL;
}
bool CBaseFile::removeFile(const Utils::WString& file, FileType type, const Utils::WString& dir, int game)
{
C_File* f = findFile(file, type, dir, game);
if (!f)
return false;
return removeFile(f);
}
bool CBaseFile::removeFile(C_File *file)
{
size_t 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(size_t pos)
{
if (pos >= static_cast<size_t>(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(FileType type, int game)
{
if ( m_lFiles.empty() )
return;
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
if (game == 0 && node->Data()->game() && node->Data()->game() != GAME_ALLNEW)
continue;
if ( game > -1 )
{
unsigned int fileGame = node->Data()->game() & ~GAME_ALLNEW;
if (fileGame != (1 << game))
{
// just remove the game from file
if (fileGame & (1 << game))
node->Data()->setGame(node->Data()->game() & ~(1 << game));
continue;
}
}
if ( type == FILETYPE_UNKNOWN || 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->isExternalFile())
{
if (!fit->UncompressData(progress))
{
if (fit->GetCompressionType() == SPKCOMPRESS_7ZIP)
{
if (!fit->uncompressToFile(L"temp", this, false, progress))
return false;
else
{
uncomprToFile = true;
fit->setFullDir(L"temp");
}
}
if (!uncomprToFile)
return false;
}
}
if ( progress )
progress->SwitchSecond();
}
return true;
}
size_t CBaseFile::fileSize() const
{
size_t fullsize = 1000;
for (CListNode<C_File>* node = m_lFiles.Front(); node; node = node->next())
fullsize += node->Data()->uncompressedDataSize();
if (m_pIconFile)
fullsize += m_pIconFile->uncompressedDataSize();
return fullsize;
}
/*
Func: GetEndOfLine
Input: id - The file id for the current file to read from
line - Pointed to hold the line number thats read
upper - true if it converts to uppercase
Return: String - the string it has read
Desc: Reads a string from a file, simlar to readLine() classes, reads to the end of the line in a file
*/
Utils::String CBaseFile::GetEndOfLine ( FILE *id, int *line, bool upper )
{
Utils::String 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.upper();
return word;
}
size_t CBaseFile::countFiles(FileType filetype) const
{
size_t 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;
}
/*
Func: CreateFilesLine
Return: String - returns the full string for files list
Desc: Creates a signle line list of all the files
*/
Utils::WString CBaseFile::createFilesLine(SSPKHeader2 *header, CProgressInfo* progress) const
{
Utils::WString line;
if (progress)
{
progress->SetDone(0);
progress->UpdateStatus(STATUS_COMPRESS);
}
if (header)
{
header->iDataCompression = m_SHeader2.iDataCompression;
header->iFileCompression = m_SHeader2.iFileCompression;
header->lSize = m_SHeader2.lSize;
header->iNumFiles = 0;
header->lFullSize = 0;
}
if (m_pIconFile)
{
// no data, read it from file
if (!m_pIconFile->GetData())
m_pIconFile->ReadFromFile();
// compress the file
if (!m_pIconFile->CompressData(m_SHeader2.iDataCompression, progress))
m_pIconFile->SetDataCompression(SPKCOMPRESS_NONE);
line += L"Icon:" + Utils::WString::Number(m_pIconFile->GetDataSize() + (long)4) + L":" + m_pIconFile->uncompressedDataSize() + L":" + (long)m_pIconFile->GetCompressionType() + L":" + _sIconExt + L"\n";
if (header)
{
++header->iNumFiles;
header->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 file
if (!file->GetData())
{
if (!file->ReadFromFile())
{
if (file->GetLastError() == SPKERR_MALLOC)
{
if (!file->CompressFile(progress))
continue;
}
}
}
if (!file->GetData())
continue;
// compress the file
if (!file->CompressData(m_SHeader2.iDataCompression, progress))
{
file->SetDataCompression(SPKCOMPRESS_NONE);
file->SetUncompressedDataSize(file->GetDataSize());
}
Utils::WString command = GetFileTypeString(file->GetFileType());
if (command.empty())
continue;
if (file->IsShared())
command = L"$" + command;
if (file->dir().empty())
line += command + L":" + (file->GetDataSize() + (long)4) + L":" + file->uncompressedDataSize() + L":" + (long)file->GetCompressionType() + L":" + (long)file->GetCreationTime() + L":" + ((file->IsCompressedToFile()) ? L"1" : L"0") + L":" + file->filename() + L":NULL:" + (long)file->game() + L"\n";
else
line += command + L":" + (file->GetDataSize() + (long)4) + L":" + file->uncompressedDataSize() + L":" + (long)file->GetCompressionType() + L":" + (long)file->GetCreationTime() + L":" + ((file->IsCompressedToFile()) ? L"1" : L"0") + L":" + file->filename() + L":" + file->dir() + L":" + (long)file->game() + L"\n";
if (header)
{
++header->iNumFiles;
header->lFullSize += (file->GetDataSize() + 4);
}
}
return line;
}
/*
######################################################################################
########## Reading Functions ##########
######################################################################################
*/
void CBaseFile::ReadAllFilesToMemory ()
{
// no file to read from
if ( this->filename().empty() ) return;
// now open the file
CFileIO File(this->filename());
if ( !File.startRead() ) return;
// read the header
File.readEndOfLineStr();
// skip past values
File.seek(4 + m_SHeader.lValueCompressSize);
// read the next header
File.readEndOfLineStr();
// skip past files
File.seek(4 + m_SHeader2.lSize);
if ( m_pIconFile )
{
if ( (!m_pIconFile->GetData()) && (!m_pIconFile->Skip()) )
m_pIconFile->readFromFile(File, m_pIconFile->GetDataSize());
else
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->GetData()) && (!fit->Skip()) )
fit->readFromFile(File, fit->GetDataSize());
else
File.seek(4 + fit->GetDataSize());
}
File.close();
}
bool CBaseFile::ReadFileToMemory(C_File *f)
{
if ( this->filename().empty() || !f ) return false; // no filename to load from
if ( f->GetData() && f->GetDataSize() ) return true; // already loaded the data
if ( !m_lFiles.FindData(f) ) return false; // unable to find file entry
// now open the file
CFileIO *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 from
if ( this->filename().empty() ) return NULL;
// now open the file
CFileIO *File = new CFileIO(this->filename());
if ( !File->startRead() ) return NULL;
// read the header
File->readEndOfLineStr();
// skip past values
File->seek(4 + m_SHeader.lValueCompressSize);
// read the next header
File->readEndOfLineStr();
// skip past files
File->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::WString baseFile = CFileIO(file->filePointer()).baseName();
int lang = (baseFile.contains(L"-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());
else
File->seek(4 + m_pIconFile->GetDataSize());
delete File;
}
void CBaseFile::_install_adjustFakePatches(CPackages *pPackages)
{
Utils::WStringList lPatches;
int startfake = pPackages->findNextFakePatch();
std::vector<C_File*> fakepatches;
for (CListNode<C_File>* node = m_lFiles.Front(); node; node = node->next())
{
C_File* fit = node->Data();
// only do fake patchs
if (!fit->IsFakePatch())
continue;
// we should only have cat and dat files, but lets check just incase they have been added incorrectly
if (!fit->checkFileExt(L"cat"))
continue;
if (fakepatches.empty())
fakepatches.push_back(fit);
else
{
bool added = false;
int num = fit->baseName().toInt();
for (auto itr = fakepatches.begin(); itr != fakepatches.end(); itr++)
{
int checkNum = (*itr)->baseName().toInt();
if (num < checkNum)
{
fakepatches.insert(itr, fit);
added = true;
break;
}
}
if(!added)
fakepatches.push_back(fit);
}
}
// rename all the cat files
for (auto itr = fakepatches.begin(); itr != fakepatches.end(); itr++)
{
Utils::WString newname = Utils::WString::PadNumber((long)startfake, 2);
lPatches.pushBack((*itr)->baseName(), newname);
// rename the file
(*itr)->FixOriginalName();
CLog::logf(CLog::Log_Install, 2, L"Adjusting fake patch number, %s => %s", (*itr)->getNameDirectory(this).c_str(), (newname + L"." + (*itr)->fileExt()).c_str());
(*itr)->setName(newname + L"." + (*itr)->fileExt());
// find the next gap
startfake = pPackages->findNextFakePatch(startfake);
}
// now rename all the matching dat files
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
C_File *fit = node->Data();
// only do fake patchs
if ( !fit->IsFakePatch() )
continue;
// we should only have cat and dat files, but lets check just incase they have been added incorrectly
if ( !fit->checkFileExt(L"dat") )
continue;
// search for the name on the list
if (lPatches.contains(fit->baseName()))
{
Utils::WString newname = lPatches.findString(fit->baseName());
// rename the file
fit->FixOriginalName();
CLog::logf(CLog::Log_Install, 2, L"Adjusting fake patch number, %s => %s", fit->getNameDirectory(this).c_str(), (newname + L"." + fit->fileExt()).c_str());
fit->setName(newname + L"." + fit->fileExt());
}
}
}
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;
Utils::WString newname = SPK::FormatTextName(starttext, pPackages->GetLanguage(), (pPackages->GetCurrentGameFlags() & EXEFLAG_TCTEXT));
fit->FixOriginalName();
CLog::logf(CLog::Log_Install, 2, L"Adjusting text file, %s => %s", fit->getNameDirectory(this).c_str(), (newname + "." + fit->fileExt()).c_str());
fit->setName(newname + "." + fit->fileExt());
++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->dir().left(7).lower() == L"Extras/") )
bEnable = true;
else if ( (IsPatch()) && (fit->GetFileType() == FILETYPE_MOD) && (!fit->IsFakePatch()) )
bEnable = true;
if ( bEnable ) CLog::logf(CLog::Log_Install, 3, L"Filetype(%d) is always enabled, setting enabled flag", fit->GetFileType());
}
return bEnable;
}
bool CBaseFile::_install_uncompress(C_File *fit, CProgressInfo *progress, Utils::WStringList *errorStr, bool *uncomprToFile)
{
*uncomprToFile = false;
_sLastError = fit->getNameDirectory(this);
_iLastError = SPKERR_UNCOMPRESS;
if ( !fit->UncompressData ( progress ) )
{
CLog::log(CLog::Log_Install, 2, L"Failed to uncompress data, attempting file decompression");
if ( fit->GetCompressionType() == SPKCOMPRESS_7ZIP )
{
if ( fit->uncompressToFile(Utils::WString::Null(), this, false, progress ) )
*uncomprToFile = true;
}
if ( !uncomprToFile )
{
if ( errorStr )
errorStr->pushBack(_sLastError, ERRORLOG(SPKINSTALL_UNCOMPRESS_FAIL));
CLog::log(CLog::Log_Install, 1, L"Unable to decompress file, skipping");
return false;
}
}
ClearError ();
return true;
}
bool CBaseFile::_install_checkVersion(C_File *pFile, const Utils::WString &sDestination)
{
// new check if we should install the file
// first get the version
if ( !m_bOverrideFiles && pFile->ReadScriptVersion() )
{
CLog::log(CLog::Log_Install, 2, L"Checking for existing file version");
C_File checkfile;
Utils::WString checkfilename = sDestination;
if ( !checkfilename.empty() ) checkfilename += L"/";
checkfilename += pFile->getNameDirectory(this);
checkfile.setFilename(checkfilename);
checkfile.setFileType(pFile->fileType());
if ( checkfile.CheckValidFilePointer() ) {
if ( checkfile.ReadScriptVersion() > pFile->GetVersion() ) {
CLog::log(CLog::Log_Install, 1, L"Newer version of the file found in directory, skipping");
return false;
}
}
}
return true;
}
Utils::WString CBaseFile::_install_adjustFilepointer(C_File *pFile, bool bEnabled, const Utils::WString &sDestination)
{
Utils::WString filename = sDestination;
if ( !filename.empty() ) filename += L"/";
if ( (IsPatch()) && (pFile->fileType() == FILETYPE_MOD) )
pFile->setDir(L"Patch");
if ( pFile->isInMod() )
{
if ( bEnabled )
pFile->setFilename(filename + pFile->getInMod() + L"::" + pFile->getNameDirectory(this));
else
pFile->setFilename(filename + L"PluginManager/DisabledFiles.cat::" + pFile->getNameDirectory(this));
}
else
pFile->setFilename ( filename + pFile->getNameDirectory(this) );
if ( !bEnabled )
{
if ( !pFile->isInMod() )
{
if ( pFile->IsFakePatch() )
pFile->setFilename ( filename + L"PluginManager/Disabled/FakePatches/FakePatch_" + this->getNameValidFile() + L"_" + this->author() + L"_" + pFile->name());
else if ( pFile->isAutoTextFile() )
pFile->setFilename ( filename + L"PluginManager/Disabled/TextFiles/Text_" + this->getNameValidFile() + L"_" + this->author() + L"_" + pFile->name());
else
pFile->setFullDir ( filename + L"PluginManager/Disabled/" + pFile->getDirectory(this) );
}
pFile->SetDisabled(true);
}
CLog::logf(CLog::Log_Install, 2, L"Adjusting the file pointer to correct install destintation, %s", pFile->filePointer().c_str());
return filename;
}
C_File *CBaseFile::_install_checkFile(C_File *pFile, Utils::WStringList *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, L"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::WString &sDestination, bool bEnabled, Utils::WStringList *errorStr)
{
// found a file, check if its in the disabled directory
Utils::WString dir = CFileIO(pCheckFile->filePointer()).dir();
Utils::WString lastDir = CDirIO(dir).topDir().lower();
// if its disabled, rename it so its enabled
if ( ((pCheckFile->IsDisabled()) || (lastDir == L"disabled") || (dir.lower().contains(L"/disabled/"))) && (bEnabled) )
{
CLog::logf(CLog::Log_Install, 2, L"Existing file, %s, is disabled, re-enabling it", pCheckFile->filePointer().c_str());
// first check if the directory exists
if ( pCheckFile->isInMod() ) {
Utils::WString tofile = pCheckFile->filePointer().token(L"::", 2);
CCatFile tocat;
int err = tocat.open(fit->filePointer().token(L"::", 1), L"", CATREAD_CATDECRYPT, true);
if ( (err == CATERR_NONE) || (err == CATERR_CREATED) ) {
tocat.appendFile(pCheckFile->filePointer(), tofile);
CLog::logf(CLog::Log_Install, 2, L"Adding existing file into new mod File, %s => %s", fit->filePointer().token(L"::", 1).c_str(), tofile.c_str());
}
CCatFile fromcat;
err = fromcat.open(pCheckFile->filePointer().token(L"::", 1), L"", CATREAD_CATDECRYPT, false);
if ( err == CATERR_NONE ) {
fromcat.removeFile(tofile);
CLog::logf(CLog::Log_Install, 2, L"Removing file from existing mod, %s::%s", pCheckFile->filePointer().token(L"::", 1).c_str(), tofile.c_str());
}
CLog::logf(CLog::Log_Install, 1, L"Adjusting existing file name %s => %s", pCheckFile->filePointer().c_str(), fit->filePointer().c_str());
pCheckFile->setFilename(fit->filePointer());
CLog::logf(CLog::Log_Install, 2, L"Adjusting In Mod setting, %s => %s", pCheckFile->getInMod().c_str(), fit->getInMod().c_str());
pCheckFile->setInMod(fit->getInMod());
}
else {
Utils::WString 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));
}
Utils::WString destfile = sDestination + L"/" + pCheckFile->getNameDirectory(this);
if ( CFileIO::Exists(destfile) ) CFileIO::Remove(destfile);
CLog::logf(CLog::Log_Install, 1, L"Adjusting existing filename, %s => %s", pCheckFile->filePointer().c_str(), destfile.c_str());
CFileIO::Rename(pCheckFile->filePointer(), destfile);
pCheckFile->setFilename (sDestination + L"/" + 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::WString &sTo, C_File *pFile, Utils::WStringList *errorStr)
{
_sLastError = sTo;
if ( !sTo.contains(L"::"))
{
if ( !Dir.exists(sTo) )
{
CLog::logf(CLog::Log_Install, 2, L"Creating directory to install file into, %s", sTo.c_str());
if ( !Dir.create(sTo) )
{
if ( errorStr )
errorStr->pushBack(sTo, ERRORLOG(SPKINSTALL_CREATEDIRECTORY_FAIL));
return false;
}
if ( errorStr )
errorStr->pushBack(sTo, ERRORLOG(SPKINSTALL_CREATEDIRECTORY));
}
}
else {
CLog::logf(CLog::Log_Install, 2, L"Adjusting file extension for file in mod, %s => %s", pFile->filePointer().c_str(), CCatFile::PckChangeExtension(pFile->filePointer()).c_str());
pFile->setFilename(CCatFile::PckChangeExtension(pFile->filePointer()));
}
return true;
}
void CBaseFile::_install_writeFile(C_File *pFile, const Utils::WString &sDestination, Utils::WStringList *errorStr)
{
_iLastError = SPKERR_WRITEFILE;
_sLastError = pFile->filePointer();
Utils::WString sInstalledFile = pFile->getNameDirectory(this);
if ( pFile->IsDisabled() )
{
sInstalledFile = pFile->filePointer().findRemove(sDestination);
if ( sInstalledFile[0] == '/' || sInstalledFile[0] == '\\' )
sInstalledFile.erase(0, 1);
}
if ( !pFile->writeFilePointer() )
{
CLog::log(CLog::Log_Install, 1, L"Failed to write the file");
if ( errorStr )
errorStr->pushBack(sInstalledFile, ERRORLOG(SPKINSTALL_WRITEFILE_FAIL));
}
else
{
CLog::log(CLog::Log_Install, 1, L"File written successfully");
CLog::log(CLog::Log_Install, 2, L"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, L"Updating file signature");
pFile->updateSignature();
break;
}
}
}
bool CBaseFile::installFiles(const Utils::WString &destdir, CProgressInfo *progress, CLinkList<C_File> *filelist, Utils::WStringList *errorStr, bool enabled, CPackages *packages )
{
//TODO: add errorStr and progress as member variables
if ( 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 disabled
CLog::logf(CLog::Log_Install, 1, L"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 version
if (!fit->isForGame(packages->GetGame()))
{
CLog::logf(CLog::Log_Install, 1, L"File didn't match game version, skipping, %d != %d", fit->game(), packages->GetGame());
continue;
}
// update the progress display to show we are processing this file
if ( 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_File
bool uncomprToFile;
if ( !this->_install_uncompress(fit, progress, errorStr, &uncomprToFile) ) {
bFailed = true;
continue;
}
bool dofile = _install_checkVersion(fit, destdir);
// change file pointer
Utils::WString sInstallDir = _install_adjustFilepointer(fit, fileEnabled, destdir);
C_File *adjustPointer = NULL;
if ( filelist ) {
C_File *cFile = _install_checkFile(fit, errorStr, &dofile, filelist);
// no matching file found, adding to main list
if ( !cFile ) filelist->push_back ( fit );
else
{
// if the file is not enabled, we need to check for any that might be enabled
if ( !fileEnabled ) //_install_checkDisabled(cFile, destdir, errorStr);
{
//TODO: check what this is actually doing
if ( !cFile->getUsed() )
{
CFileIO rFile(cFile->filePointer());
if ( rFile.exists() )
{
if ( errorStr )
{
if ( rFile.remove() )
errorStr->pushBack(cFile->filePointer().findRemove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE));
else
errorStr->pushBack(cFile->filePointer().findRemove(destdir), ERRORLOG(SPKINSTALL_DELETEFILE_FAIL));
}
}
cFile->setFilename(fit->filePointer());
cFile->SetDisabled(true);
}
else
{
fit->setFullDir(sInstallDir + fit->getDirectory(this));
fit->SetDisabled(false);
}
}
// move it to enabled
else {
if ( !this->_install_checkFileEnable(cFile, fit, destdir, fileEnabled, errorStr) ) {
bFailed = true;
continue;
}
}
adjustPointer = cFile;
if ( dofile ) adjustPointer->SetCreationTime(fit->GetCreationTime());
}
}
if ( dofile )
{
// uncompressed to file, rename and move
if ( uncomprToFile )
{
_iLastError = SPKERR_WRITEFILE;
Utils::WString to = fit->getDirectory(this);
if ( !fileEnabled ) to = L"PluginManager/Disabled/" + to;
if ( !_install_createDirectory(Dir, to, fit, errorStr) ) {
bFailed = true;
continue;
}
bool err = true;
_sLastError = to;
if ( !fit->getTempFile().empty())
err = CFileIO::Rename(fit->getTempFile(), to);
if ( err ) {
bFailed = true;
continue;
}
}
//otherwise, just extract the file
else
{
// old file is found in list, switch to using new one
if ( (filelist) && (adjustPointer) ) {
adjustPointer->CopyData(fit, false);
CLog::log(CLog::Log_Install, 2, L"Copying data into existing file");
}
Utils::WString fpointer = fit->filePointer();
_iLastError = SPKERR_CREATEDIRECTORY;
Utils::WString dir = CFileIO(fit->filePointer()).dir();
dir = dir.findRemove(destdir);
if (!dir.empty() && (dir[0] == '/' || dir[0] == '\\'))
dir.erase(0, 1);
if ( !_install_createDirectory(Dir, dir, fit, errorStr) ) {
bFailed = true;
continue;
}
_install_writeFile(fit, destdir, errorStr);
}
ClearError ();
}
if ( adjustPointer )
{
CLog::log(CLog::Log_Install, 2, L"Adjusting pointers to existing file and deleting new file pointer");
node->ChangeData(adjustPointer);
delete fit;
}
CLog::log(CLog::Log_Install, 1, L"File installation completed");
}
// now clear or data memory
CLog::log(CLog::Log_Install, 2, L"Delting temporary file data from memory");
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) node->Data()->DeleteData();
return !bFailed;
}
/*######################################################################################################*/
/*
Func: ParseHeader
Input: Header String - string formated directly from the file
Return: Boolean - If string is a valid header
Desc: Splits up the main header string to get all required settings
*/
bool CBaseFile::_parseHeader(const Utils::WString &header)
{
if ( !this->_checkHeader(header.token(L";", 1)))
return false;
m_SHeader.fVersion = header.token(L";", 2).toFloat();
if ( m_SHeader.fVersion > FILEVERSION )
return false;
m_SHeader.iValueCompression = header.token(L";", 3).toInt();
m_SHeader.lValueCompressSize = header.token(L";", 4).toLong();
return true;
}
bool CBaseFile::_checkHeader(const Utils::WString &header) const
{
if ( header.Compare(L"BaseCycrow") )
return true;
return false;
}
/*
Func: ParseFileHeader
Input: Header String - string formated directly from the file
Return: Boolean - If string is a valid header
Desc: Splits up the file header string to get all required settings
*/
bool CBaseFile::_parseFileHeader(const Utils::WString &header)
{
if (header.token(L";", 1) != L"FileHeader")
return false;
m_SHeader2.iNumFiles = header.token(L";", 2).toInt();
m_SHeader2.lSize = header.token(L";", 3).toInt();
m_SHeader2.lFullSize = header.token(L";", 4).toInt();
m_SHeader2.iFileCompression = header.token(L";", 5).toInt();
m_SHeader2.iDataCompression = header.token(L";", 6).toInt();
return true;
}
/*
Func: ParseValueLine
Input: String - single line from a file to set
Return: Boolean - returns true if value exists
Desc: Reads the line and assigns the parameters for the file
*/
bool CBaseFile::parseValueLine(const Utils::WString &sLine)
{
Utils::WString first = sLine.token(L" ", 1);
Utils::WString rest = sLine.tokens(L" ", 2);
if ( first.Compare(L"Name:") ) this->setName(rest);
else if ( first.Compare(L"Author:") ) this->setAuthor(rest);
else if ( first.Compare(L"Version:") ) this->setVersion(rest);
else if ( first.Compare(L"fGameVersion:") ) {
if ( m_lGames.Back() ) {
m_lGames.Back()->Data()->sVersion = rest;
}
}
else if ( first.Compare(L"GameVersion:") ) {
if ( m_lGames.Back() ) {
m_lGames.Back()->Data()->iVersion = rest.toInt();
}
}
else if ( first.Compare(L"Game:") )
this->AddGameCompatability(rest, L"");
else if ( first.Compare(L"GameCompat:") )
this->AddGameCompatability(rest.token(L" ", 1), rest.tokens(L" ", 2));
else if ( first.Compare(L"GameCompatExact:") )
this->AddGameCompatability(rest.token(L" ", 1), rest.tokens(L" ", 2));
else if ( first.Compare(L"Date:") ) this->setCreationDate(rest);
else if ( first.Compare(L"WebAddress:") ) this->setWebAddress(rest);
else if ( first.Compare(L"WebSite:") ) this->setWebSite(rest);
else if ( first.Compare(L"Email:") ) this->setEmail(rest);
else if ( first.Compare(L"WebMirror1:") || first.Compare(L"Mirror1:") || first.Compare(L"WebMirror:") )
this->addWebMirror(rest);
else if ( first.Compare(L"WebMirror2:") || first.Compare(L"Mirror2:") )
this->addWebMirror(rest);
else if ( first.Compare(L"PluginType:") ) this->setPluginType(rest);
else if ( first.Compare(L"Desc:") ) this->setDescription(rest);
else if ( first.Compare(L"UninstallAfter:") ) this->addUninstallText(parseLanguage(rest.token(L"|", 1)), false, rest.tokens(L"|", 2));
else if ( first.Compare(L"UninstallBefore:") ) this->addUninstallText(parseLanguage(rest.token(L"|", 1)), true, rest.tokens(L"|", 2));
else if ( first.Compare(L"InstallAfter:") ) this->addInstallText(parseLanguage(rest.token(L"|", 1)), false, rest.tokens(L"|", 2));
else if ( first.Compare(L"InstallBefore:") ) this->addInstallText(parseLanguage(rest.token(L"|", 1)), true, rest.tokens(L"|", 2));
else if ( first.Compare(L"ScriptName:") ) addName(parseLanguage(rest.token(L":", 1)), rest.token(L":", 2));
else if ( first.Compare(L"GameChanging:") ) this->setGameChanging(rest);
else if ( first.Compare(L"EaseOfUse:") ) this->setEaseOfUse(rest);
else if ( first.Compare(L"Recommended:") ) this->setRecommended(rest);
else if ( first.Compare(L"NeededLibrary:") )
this->addNeededLibrary(rest.token(L"||", 1), rest.token(L"||", 2), rest.token(L"||", 3));
else if ( first.Compare(L"FakePatchBefore:") )
this->addFakePatchOrder(false, rest.token(L"||", 1), rest.token(L"||", 2));
else if (first.Compare(L"FakePatchAfter:"))
this->addFakePatchOrder(true, rest.token(L"||", 1), rest.token(L"||", 2));
else if (first.Compare(L"Globals:"))
this->addGlobal(rest.token(L";", 1), rest.tokens(L";", 2));
else if ( first.Compare(L"ForumLink:") ) this->setForumLink(rest);
else
return false;
return true;
}
int CBaseFile::parseLanguage(const Utils::WString &lang) const
{
int langID = lang;
if ( !langID ) {
if (lang.Compare(L"english")) return 44;
else if (lang.Compare(L"default")) return 0;
else if (lang.Compare(L"german")) return 49;
else if (lang.Compare(L"russian")) return 7;
else if (lang.Compare(L"spanish")) return 34;
else if (lang.Compare(L"french")) return 33;
}
return langID;
}
/*
Func: ReadValues
Input: String - values in one long line
Desc: splits the values data into each line to read the data
*/
void CBaseFile::_readValues(const Utils::WString &values)
{
std::vector<Utils::WString> lines;
values.tokenise(L"\n", lines);
for (size_t i = 0; i < lines.size(); i++)
parseValueLine(lines[i]);
}
/*
Func: ParseFilesLine
Input: String - single line from a file to set
Return: Boolean - returns true if value exists
Desc: Reads the line and assigns the parameters for the file
*/
bool CBaseFile::_parseFilesLine(const Utils::WString &line)
{
if ( !line.contains(L":") )
return false;
Utils::WString command = line.token(L":", 1);
long size = line.token(L":", 2).toInt();
long usize = line.token(L":", 3).toInt ();
long compression = line.token(L":", 4).toInt ();
if ( command == L"Icon" )
{
_sIconExt = line.token(L":", 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.token(L":", 5).toLong();
bool compressToFile = (line.token(L":", 6).toInt() == 1) ? true : false;
Utils::WString name = line.token(L":", 7);
Utils::WString dir = line.token(L":", 8);
if ( name.empty() )
return true;
bool shared = false;
if ( command.left(1) == L"$" )
{
shared = true;
command.erase(0, 1);
}
FileType type = FILETYPE_UNKNOWN;
if ( command == L"Script" )
type = FILETYPE_SCRIPT;
else if ( command == L"Text" )
type = FILETYPE_TEXT;
else if ( command == L"Readme" )
type = FILETYPE_README;
else if ( command == L"Map" )
type = FILETYPE_MAP;
else if ( command == L"Mod" )
type = FILETYPE_MOD;
else if ( command == L"Uninstall" )
type = FILETYPE_UNINSTALL;
else if ( command == L"Sound" )
type = FILETYPE_SOUND;
else if ( command == L"Mission" )
type = FILETYPE_MISSION;
else if ( command == L"Extra" )
type = FILETYPE_EXTRA;
else if ( command == L"Screen" )
type = FILETYPE_SCREEN;
else if ( command == L"Backup" )
type = FILETYPE_BACKUP;
else if ( command == L"Advert" )
type = FILETYPE_ADVERT;
else if ( command == L"ShipScene" )
type = FILETYPE_SHIPSCENE;
else if ( command == L"CockpitScene" )
type = FILETYPE_COCKPITSCENE;
else if ( command == L"ShipOther" )
type = FILETYPE_SHIPOTHER;
else if ( command == L"ShipModel" )
type = FILETYPE_SHIPMODEL;
if (type == FILETYPE_UNKNOWN)
return false;
C_File *file = new C_File();
if (dir.left(5).Compare(L"GAME_")) {
unsigned int iGame = dir.token(L"_", 2).toInt();
if (!iGame)
file->setGame(0);
else
file->setGame(1 << 31 | 1 << iGame);
dir = Utils::WString::Null();
}
else if ( line.countToken(L":") >= 9 )
{
Utils::WString game = line.token(L":", 9);
if (game.contains(L"_"))
{
unsigned int iGame = game.token(L"_", 2).toInt();
if (iGame)
file->setGame(1 << 31 | 1 << iGame);
else
file->setGame(0);
}
else
{
int iGame = game.toInt();
if (iGame & (1 << 31))
file->setGame(iGame);
else if (!iGame)
file->setGame(0);
else
file->setGame(1 << 31 | 1 << iGame);
}
}
if (dir.Compare(L"NULL")) {
dir = Utils::WString::Null();
}
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: ParseFiles
Input: String - values in one long line
Desc: splits the files data into each line to read the data
*/
void CBaseFile::_readFiles(const Utils::WString &values)
{
std::vector<Utils::WString> lines;
values.tokenise(L"\n", lines);
for (size_t i = 0; i < lines.size(); i++)
_parseFilesLine(lines[i]);
}
/*
Func: ReadFile
Input: filename - the name of the file to open and read
readdata - If falses, dont read the files to memory, just read the headers and values
Return: boolean - return ture if acceptable format
Desc: Opens and reads the spk file and loads all data into class
*/
bool CBaseFile::readFile(const Utils::WString &filename, int readtype, CProgressInfo *progress)
{
CFileIO File(filename);
if ( !File.startRead() ) return false;
bool ret = this->readFile(File, readtype, progress);
if ( ret ) this->setFilename(filename);
File.close();
return ret;
}
int CBaseFile::_read_Header(CFileIO &File, int iReadType, int iMaxProgress, CProgressInfo *pProgress)
{
int doneLen = 0;
// read data to memory
unsigned char *readData = 0;
try {
readData = new unsigned char[m_SHeader.lValueCompressSize];
}
catch (std::exception &e) {
CLog::logf(CLog::Log_IO, 2, L"CBaseFile::_read_Header() unable to malloc [header], %d (%hs)", m_SHeader.lValueCompressSize, e.what());
return -1;
}
if (!readData)
return -1;
unsigned char size[4];
File.read(size, 4);
File.read(readData, m_SHeader.lValueCompressSize);
unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];
// check for zlib compression
if ( m_SHeader.iValueCompression == SPKCOMPRESS_ZLIB ) {
// uncomress the data
try {
unsigned char *uncompr = new unsigned char[uncomprLen];
int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader.lValueCompressSize );
// update the progress for each section
if ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(2, iMaxProgress);
if (err == Z_OK)
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((const char*)uncompr);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)uncompr;
_readValues(Utils::WString(data));
}
}
doneLen = uncomprLen;
delete []uncompr;
}
catch (std::exception &e) {
CLog::logf(CLog::Log_IO, 2, L"CBaseFile::_read_Header() unable to malloc [uncompr], %d (%hs)", 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 section
if ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(2, iMaxProgress);
if (compr)
{
if (m_SHeader.fVersion <= 4.4f)
{
std::string data((const char*)compr);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)compr;
_readValues(Utils::WString(data));
}
}
}
// no compression
else
{
if (m_SHeader.fVersion <= 4.4f)
{
std::string data((const char*)readData);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)readData;
_readValues(Utils::WString(data));
}
}
delete []readData;
return doneLen;
}
int CBaseFile::_read_FileHeader(CFileIO &File, 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, L"CBaseFile::_read_FileHeader() unable to malloc [header], %d (%hs)", m_SHeader2.lSize, e.what());
return -1;
}
unsigned char size[4];
File.read(size, 4);
File.read(readData, m_SHeader2.lSize);
unsigned long uncomprLen = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];
// check for zlib compression
if ( 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 section
if ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(5, iMaxProgress);
if (err == Z_OK)
{
//#pragma warning(disable:4244)
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)uncompr);
_readFiles(std::wstring(data.begin(), data.end()));
}
else
{
std::wstring data((wchar_t*)uncompr);
_readFiles(data);
}
//#pragma warning(enable:4244)
}
delete[] uncompr;
}
catch (std::exception &e) {
CLog::logf(CLog::Log_IO, 2, L"CBaseFile::_read_FileHeader() unable to malloc [uncompr], %d (%hs)", 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 section
if ( iReadType != SPKREAD_ALL && pProgress ) pProgress->UpdateProgress(5, iMaxProgress);
if (compr)
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)compr);
_readFiles(std::wstring(data.begin(), data.end()));
}
else
{
std::wstring data((wchar_t*)compr);
_readFiles(data);
}
}
}
else
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)readData);
_readFiles(std::wstring(data.begin(), data.end()));
}
else
{
std::wstring data((wchar_t*)readData);
_readFiles(data);
}
}
delete []readData;
return true;
}
bool CBaseFile::readFile(CFileIO &File, int readtype, CProgressInfo *progress)
{
ClearError ();
// first read the header
if ( !_parseHeader(File.readEndOfLineStr()) ) return false;
if ( readtype == SPKREAD_HEADER ) return true;
// update the progress for each section
int 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 files
if ( m_SHeader.lValueCompressSize ) doneLen = this->_read_Header(File, readtype, maxProgress, progress);
// update the progress for each section
if ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(3, maxProgress);
if ( readtype == SPKREAD_VALUES ) return true;
// next should be the next header
if ( !_parseFileHeader(File.readEndOfLineStr()) ) return false;
// clear the current file list
m_lFiles.clear(true);
if ( _pTextDB ) {
delete _pTextDB;
_pTextDB = NULL;
}
// update the progress for each section
if ( readtype != SPKREAD_ALL && progress ) progress->UpdateProgress(4, maxProgress);
if ( m_SHeader2.lSize ) this->_read_FileHeader(File, readtype, maxProgress, doneLen, progress);
// file mismatch
long numfiles = m_lFiles.size();
if ( m_pIconFile ) ++numfiles;
if ( m_SHeader2.iNumFiles != numfiles ) {
_iLastError = SPKERR_FILEMISMATCH;
return false;
}
// update the progress for each section
if ( 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 file
for ( 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 patchs
for ( 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 patchs
for ( 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::WString CBaseFile::createValuesLine () const
{
Utils::WString values(L"Name: ");
values += this->name() + L"\n";
values += L"Author: " + this->author() + L"\n";
values += L"Version: " + this->version() + L"\n";
if ( !this->creationDate().empty() )values += L"Date: " + this->creationDate() + L"\n";
if ( !this->webAddress().empty() ) values += L"WebAddress: " + this->webAddress() + L"\n";
if ( !this->webSite().empty() ) values += L"WebSite: " + this->webSite() + L"\n";
if ( !this->email().empty() ) values += L"Email: " + this->email() + L"\n";
if ( !this->forumLink().empty() ) values += L"ForumLink: " + this->forumLink() + L"\n";
for(auto itr = webMirrors().begin(); itr != webMirrors().end(); itr++)
values += Utils::WString(L"WebMirror: ") + (*itr)->str + L"\n";
if ( !this->description().empty() ) {
Utils::WString desc = this->description();
desc = desc.findReplace(L"<newline>", L"<br>");
desc = desc.findReplace(L"\n", L"<br>");
desc.remove(L'\r');
values += L"Desc: " + desc + L"\n";
}
for ( CListNode<SGameCompat> *gc = m_lGames.Front(); gc; gc = gc->next() ) {
if ( !gc->Data()->sVersion.empty() )
values += Utils::WString(L"GameCompatExact: ") + (long)gc->Data()->iGame + L" " + gc->Data()->sVersion + L"\n";
else
values += Utils::WString(L"GameCompat: ") + (long)gc->Data()->iGame + L" " + (long)gc->Data()->iVersion + L"\n";
}
if (computeSigned(true))
values += L"Signed\n";
for ( int j = 0; j < 2; j++ ) {
const CInstallText *text;
Utils::WString textStart;
switch(j) {
case 0: textStart = L"Uninstall"; text = this->uninstallText(); break;
case 1: textStart = L"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 + L"Before: " + (long)iLang + L"|" + text->getBefore(iLang) + L"\n";
if ( !text->getAfter(iLang).empty() ) values += textStart + L"After: " + (long)iLang + L"|" + text->getAfter(iLang) + L"\n";
}
}
values += Utils::WString(L"GameChanging: ") + (long)gameChanging() + L"\n";
values += Utils::WString(L"EaseOfUse: ") + (long)easeOfUse() + L"\n";
values += Utils::WString(L"Recommended: ") + (long)recommended() + L"\n";
for(auto itr = namesList()->begin(); itr != namesList()->end(); itr++)
values += Utils::WString(L"ScriptName: ") + (long)(*itr)->iLanguage + L":" + (*itr)->sName + L"\n";
for ( CListNode<SNeededLibrary> *libNode = m_lNeededLibrarys.Front(); libNode; libNode = libNode->next() ) {
SNeededLibrary *l = libNode->Data();
values += L"NeededLibrary: " + l->sName + L"||" + l->sAuthor + L"||" + l->sMinVersion + L"\n";
}
for (auto itr = _lFakePatchBefore.begin(); itr != _lFakePatchBefore.end(); itr++)
values += L"FakePatchBefore: " + (*itr)->str + L"||" + (*itr)->data + L"\n";
for (auto itr = _lFakePatchAfter.begin(); itr != _lFakePatchAfter.end(); itr++)
values += L"FakePatchAfter: " + (*itr)->str + L"||" + (*itr)->data + L"\n";
values += Utils::WString(L"PluginType: ") + (long)this->pluginType() + L"\n";
for (auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
values += L"Globals: " + (*itr)->str + L";" + (*itr)->data + L"\n";
return values;
}
/*
Func: WriteFile
Input: filename - The filename of the spk file to write to
Desc: Writes the data to an spk file
*/
bool CBaseFile::writeFile(const Utils::WString &filename, CProgressInfo *progress) const
{
CFileIO File(filename);
if ( File.startWrite() ) return writeData(File, progress);
return false;
}
bool CBaseFile::writeHeader(CFileIO &file, int valueheader, int valueComprLen) const
{
return file.write("BaseCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen);
}
bool CBaseFile::writeData(CFileIO &file, CProgressInfo *progress ) const
{
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 values
Utils::WString values = this->createValuesLine();
const char* data = (const char*)values.c_str();
// compress the values
int valueUncomprLen = (int)(wcslen(values.c_str()) * sizeof(wchar_t)) / sizeof(char);
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 *)data, (unsigned long)valueUncomprLen, 0 );
if ( err == Z_OK )
compressed = true;
}
if ( !compressed )
{
valueComprLen = valueUncomprLen;
valueCompr = (unsigned char *)calloc((unsigned int)valueComprLen, 1);
memcpy ( valueCompr, (const unsigned char *)data, valueComprLen );
valueheader = SPKCOMPRESS_NONE;
}
// write the main header to the file
if ( !this->writeHeader(file, valueheader, valueComprLen) ) return false;
// write the compressed data to file
file.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 values
SSPKHeader2 header;
Utils::WString files = createFilesLine(&header, progress);
// compress the files values
long fileUncomprLen = (long)((wcslen(files.c_str()) * sizeof(wchar_t)) / sizeof(char)), 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)fileUncomprLen, 0 );
if ( err == Z_OK )
compressed = true;
}
}
// if unable to compress, store it as plain text
if ( !compressed )
{
fileComprLen = fileUncomprLen;
fileCompr = (unsigned char *)calloc((unsigned int)fileComprLen, 1);
memcpy ( fileCompr, (const unsigned char *)files.c_str(), fileComprLen );
fileheader = SPKCOMPRESS_NONE;
}
// now write the file header
header.lSize = fileComprLen;
file.write("FileHeader;%d;%ld;%ld;%d;%d\n", header.iNumFiles, header.lSize, header.lFullSize, fileheader, header.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 data
if ( m_pIconFile ) {
if ( progress ) progress->UpdateFile(m_pIconFile);
file.writeSize(m_pIconFile->uncompressedDataSize());
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->uncompressedDataSize());
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);
}
}
return true;
}
bool CBaseFile::extractFile(C_File *file, const Utils::WString &dir, bool includedir, CProgressInfo *progress)
{
if (ReadFileToMemory(file))
{
// now finally, uncompress the file
long len = 0;
unsigned char *data = file->UncompressData(&len, progress);
if (!data)
{
// attempt a file decompress
if (file->GetCompressionType() == SPKCOMPRESS_7ZIP)
{
if (file->uncompressToFile(dir, this, includedir, progress))
return true;
}
return false;
}
if (!file->writeToDir(dir, this, includedir, Utils::WString::Null(), data, len))
return false;
return true;
}
else
return false;
}
bool CBaseFile::extractFile(C_File *file, const Utils::WString &dir, unsigned int game, const Utils::WStringList &gameAddons, bool includedir, CProgressInfo *progress)
{
if (ReadFileToMemory(file))
{
CDirIO Dir(dir);
Utils::WString addonDir;
if (file->isFileInAddon())
{
int addonGame = file->getForSingleGame();
if (!addonGame) addonGame = game;
if (addonGame > 0)
addonDir = gameAddons.findString(Utils::WString::Number(addonGame));
}
if (!addonDir.empty())
Dir.cd(addonDir);
// create directory first
Dir.create(file->getDirectory(this));
// now finally, uncompress the file
long len = 0;
unsigned char *data = file->UncompressData(&len, progress);
if (!data)
{
// attempt a file decompress
if (file->GetCompressionType() == SPKCOMPRESS_7ZIP)
{
if (file->uncompressToFile(Dir.dir(), this, includedir, progress))
return true;
}
return false;
}
if (!file->writeToDir(Dir.dir(), this, includedir, Utils::WString::Null(), data, len))
return false;
return true;
}
else
return false;
}
bool CBaseFile::extractFile(int filenum, const Utils::WString &dir, unsigned int game, const Utils::WStringList &gameAddons, bool includedir, CProgressInfo *progress)
{
// invalid valus
if (filenum < 0)
return false;
// out of range
if (filenum > m_lFiles.size())
return false;
// get the file pointer
C_File *file = m_lFiles.Get(filenum);
return extractFile(file, dir, game, gameAddons, includedir, progress);
}
bool CBaseFile::extractFile(int filenum, const Utils::WString &dir, bool includedir, CProgressInfo *progress)
{
// invalid valus
if (filenum < 0)
return false;
// out of range
if (filenum > m_lFiles.size())
return false;
// get the file pointer
C_File *file = m_lFiles.Get(filenum);
return extractFile(file, dir, includedir, progress);
}
bool CBaseFile::extractAll(const Utils::WString &dir, int game, const Utils::WStringList &gameAddons, bool includedir, CProgressInfo *progress)
{
// no file to read from
if (this->filename().empty())
return false;
// now open the file
CFileIO *File = _startRead();
if (!File) return false;
// now were in the file section
// skip past each one
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->GetDataSize()) || (!fit->GetData()))
{
if (!fit->readFromFile(*File, fit->GetDataSize()))
return false;
}
else
File->seek(fit->GetDataSize());
if (game && !fit->isForGame(game))
continue;
if (progress)
progress->UpdateFile(fit);
CDirIO Dir(dir);
Utils::WString addonDir;
if (fit->isFileInAddon())
{
int addonGame = fit->getForSingleGame();
if (!addonGame) addonGame = game;
if (addonGame > 0)
addonDir = gameAddons.findString(Utils::WString::Number(addonGame));
}
if (!addonDir.empty())
Dir.cd(addonDir);
// create directory first
Dir.create(fit->getDirectory(this));
long size = 0;
unsigned char *data = fit->UncompressData(&size, progress);
if ((!data) && (fit->GetCompressionType() == SPKCOMPRESS_7ZIP)) {
if (!fit->uncompressToFile(Dir.dir(), this, includedir, progress)) return false;
}
else if ((!data) || (!fit->writeToDir(Dir.dir(), this, includedir, Utils::WString::Null(), data, size))) return false;
}
delete File;
return true;
}
bool CBaseFile::computeSigned(bool updateFiles) const
{
// any globals?
if (!_lGlobals.empty())
return false;
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 directly
if ((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 game
else if ((file->GetFileType() == FILETYPE_MOD) || (file->GetFileType() == FILETYPE_MAP))
return false;
// else should be a script file, script or uninstall type
// all scripts must be signed, if any are not, then no signed status
if (!file->IsSigned())
return false;
}
return true;
}
bool CBaseFile::updateSigned(bool updateFiles)
{
m_bSigned = computeSigned(updateFiles);
return m_bSigned;
}
bool CBaseFile::isPackageNeeded(const Utils::WString &scriptName, const Utils::WString &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::WString &scriptName, const Utils::WString &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(const Utils::WString &scriptName, const Utils::WString &author)
{
removeFakePatchOrder(true, scriptName, author);
removeFakePatchOrder(false, scriptName, author);
}
void CBaseFile::removeFakePatchOrder(bool after, const Utils::WString &scriptName, const Utils::WString &author)
{
Utils::WStringList *list;
if ( after )
list = &_lFakePatchAfter;
else
list = &_lFakePatchBefore;
int found = list->findStringAndData(scriptName, author);
while (found != -1)
{
list->removeAt(found);
found = list->findStringAndData(scriptName, author);
}
}
void CBaseFile::addFakePatchOrder(bool after, const Utils::WString &scriptName, const Utils::WString &author)
{
Utils::WStringList *list;
if ( after )
list = &_lFakePatchAfter;
else
list = &_lFakePatchBefore;
// check if the package already exists
if (!list->containsStringAndData(scriptName, author))
{
// cant have it on both list
removeFakePatchOrder(!after, scriptName, author);
list->pushBack(scriptName, author);
}
}
void CBaseFile::addNeededLibrary(const Utils::WString &scriptName, const Utils::WString &author, const Utils::WString &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::WString &scriptName, const Utils::WString &author)
{
SNeededLibrary *l = this->findPackageNeeded(scriptName, author);
if ( l )
{
m_lNeededLibrarys.remove(l);
delete l;
}
}
void CBaseFile::ClearNeededPackages()
{
SNeededLibrary *ns = this->findPackageNeeded(L"<package>", L"<author>");
Utils::WString 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(L"<package>", L"<author>", version);
}
bool CBaseFile::GeneratePackagerScript(bool wildcard, Utils::WStringList *list, int game, const Utils::WStringList &gameAddons, bool datafile)
{
list->pushBack(L"#");
list->pushBack(L"# Packager Script");
list->pushBack(L"# -- Generated by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2) + L" --");
list->pushBack(L"#");
list->pushBack(L"");
if ( !datafile )
{
list->pushBack(L"# Variable for your game directory, where to get files from");
list->pushBack(L"# $PATH variable is used to get the current path");
list->pushBack(L"Variable: $GAMEDIR $PATH");
list->pushBack(L"");
}
list->pushBack(L"# The name of the script");
list->pushBack(L"Name: " + this->name());
list->pushBack(L"");
list->pushBack(L"# The author of the script, ie, you");
list->pushBack(L"Author: " + this->author());
list->pushBack(L"");
list->pushBack(L"# The creation data, when it was created");
if ( datafile )
list->pushBack(L"Date: " + this->creationDate());
else {
list->pushBack(L"# $DATE variable is used to get the current date");
list->pushBack(L"Date: $DATE");
}
list->pushBack(L"");
list->pushBack(L"# The version of script");
if ( datafile )
list->pushBack(L"Version: " + this->version());
else
{
list->pushBack(L"# $ASK variable is used to get an input when creating");
list->pushBack(L"Version: $ASK");
}
list->pushBack(L"");
if ( !m_lGames.empty() ) {
list->pushBack(L"# The game version the script is for <game> <version> (can have multiple games)");
for ( SGameCompat *g = m_lGames.First(); g; g = m_lGames.Next() ) {
if (game > 0 && g->iGame != game)
continue;
Utils::WString game = CBaseFile::ConvertGameToString(g->iGame);
if ( !g->sVersion.empty() )
{
game += L" ";
game += g->sVersion;
}
else
{
game += L" ";
game += (long)g->iVersion;
}
list->pushBack(L"Game: " + game);
}
list->pushBack(L"");
}
if ( !this->description().empty() ) {
list->pushBack(L"# The description of the script, displays when installing");
list->pushBack(L"Description: " + this->description());
list->pushBack(L"");
}
if ( !this->webSite().empty() ) {
list->pushBack(L"# A link to the website for the script, ie for an online help page");
list->pushBack(L"WebSite: " + this->webSite());
list->pushBack(L"");
}
if ( !this->forumLink().empty() ) {
list->pushBack(L"# A direct link to the thread in the egosoft forum");
list->pushBack(L"ForumLink: " + this->forumLink());
list->pushBack(L"");
}
if ( !this->webAddress().empty() ) {
list->pushBack(L"# A link to the address for the update file");
list->pushBack(L"WebAddress: " + this->webAddress());
list->pushBack(L"");
}
if ( !this->email().empty() ) {
list->pushBack(L"# The email address of the author, to allow users to contract if needed");
list->pushBack(L"Email: " + this->email());
list->pushBack(L"");
}
if (webMirrors().size())
{
list->pushBack(L"# A link to the mirror address for the update file, can have many of these");
for(auto itr = webMirrors().begin(); itr != webMirrors().end(); itr++)
list->pushBack(Utils::WString(L"WebMirror: ") + (*itr)->str);
list->pushBack(L"");
}
if ( m_bAutoGenerateUpdateFile )
{
list->pushBack(L"# Auto generate the package update file when created");
list->pushBack(L"GenerateUpdateFile");
}
if ( m_lNeededLibrarys.size() )
{
list->pushBack(L"# Needed Library dependacies, require these to be installed");
for ( CListNode<SNeededLibrary> *node = m_lNeededLibrarys.Front(); node; node = node->next() )
list->pushBack(L"Depend: " + node->Data()->sName + L"|" + node->Data()->sMinVersion + L"|" + node->Data()->sAuthor);
list->pushBack(L"");
}
if ( !_noRatings() )
{
list->pushBack(L"# Ratings Values, 0 to 5, <ease> <changing> <recommended>");
list->pushBack(Utils::WString(L"Ratings: ") + (long)easeOfUse() + L" " + (long)gameChanging() + L" " + (long)recommended());
list->pushBack(L"");
}
if (namesList()->size())
{
list->pushBack(L"# Package names, uses different names for different languages");
for(auto itr = namesList()->begin(); itr != namesList()->end(); itr++)
list->pushBack(Utils::WString("ScriptName: ") + (long)(*itr)->iLanguage + L" " + (*itr)->sName);
list->pushBack(L"");
}
for ( int j = 0; j < 2; j++ ) {
Utils::WString installText = (j == 0) ? L"Install" : L"Uninstall";
const CInstallText *pText = (j == 0) ? this->installText() : this->uninstallText();
if ( pText->any() )
{
list->pushBack(L"# " + installText + L" Texts, display text before and/or after " + installText + L"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(installText + "Before: " + iLang + L" " + pText->getBefore(iLang));
if ( !pText->getAfter(iLang).empty() ) list->pushBack(installText + "After: " + iLang + L" " + pText->getAfter(iLang));
}
list->pushBack(L"");
}
}
list->pushBack(L"# 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(L"PluginType: Normal");
break;
case PLUGIN_STABLE:
list->pushBack(L"PluginType: Stable");
break;
case PLUGIN_EXPERIMENTAL:
list->pushBack(L"PluginType: Experimental");
break;
case PLUGIN_CHEAT:
list->pushBack(L"PluginType: Cheat");
break;
case PLUGIN_MOD:
list->pushBack(L"PluginType: Mod");
break;
}
list->pushBack(L"");
if (!_lGlobals.empty())
{
list->pushBack(L"# Globals, add global overrides to a package when generating the globals file, to override game settings");
for (auto itr = _lGlobals.begin(); itr != _lGlobals.end(); itr++)
list->pushBack(L"Global: " + (*itr)->str + L";" + (*itr)->data);
list->pushBack(L"");
}
return true;
}
bool CBaseFile::GeneratePackagerScriptFile(bool wildcard, Utils::WStringList *list, int game, const Utils::WStringList &gameAddons)
{
// now do files and wildcards
Utils::WStringList files;
for ( CListNode<C_File> *f = m_lFiles.Front(); f; f = f->next() )
{
if (game && !(f->Data()->game() & 1 << game))
continue;
Utils::WString name = L"$GAMEDIR/";
// addon directory?
unsigned int checkGame = f->Data()->game() & ~(1 << 31);
unsigned int foundGame = 0;
for (int i = 0; i < 31; ++i)
{
if (checkGame == 1 << i)
{
foundGame = i;
break;
}
}
if (foundGame)
{
Utils::WString str = gameAddons.findString(Utils::WString::Number(foundGame));
if(!str.empty())
name += str + L"/";
}
bool done = false;
if ( wildcard )
{
Utils::WString base = f->Data()->baseName();
if ( f->Data()->fileType() == FILETYPE_SCRIPT )
{
if ( base.token(L".", 1).Compare(L"plugin") || base.token(L".", 1).Compare(L"lib") )
{
name += f->Data()->getDirectory(this) + "/" + base.tokens(L".", 1, 2) + L".*";
done = true;
}
else if ( base.token(L".", 1).Compare(L"al") && !base.token(L".", 2).Compare(L"plugin") )
{
name += f->Data()->getDirectory(this) + L"/" + base.tokens(L".", 1, 2) + L".*";
done = true;
}
}
else if ( f->Data()->fileType() == FILETYPE_TEXT )
{
if ( base.contains(L"-L") )
{
name += f->Data()->getDirectory(this) + L"/" + base.token(L"-L", 1) + L"-L*";
done = true;
}
else
{
name += f->Data()->getDirectory(this) + L"/*" + base.right(4) + L".*";
done = true;
}
}
}
if ( !done )
name += f->Data()->getNameDirectory(this);
if ( !f->Data()->dir().empty() )
{
name += L"|";
name += f->Data()->dir();
}
Utils::WString s = L"GAME ";
if (!f->Data()->game() || f->Data()->game() == GAME_ALLNEW)
s += CBaseFile::ConvertGameToString(f->Data()->game());
else
{
bool first = true;
for (int i = 0; i < 31; ++i)
{
if (f->Data()->game() & 1 << i)
{
if (first)
first = false;
else
s += L"|";
s += CBaseFile::ConvertGameToString(i);
}
}
}
s += L" " + name;
if(!files.contains(s))
files.pushBack(s, f->Data()->fileTypeString());
}
if ( !files.empty() )
{
list->pushBack(L"# Files List, all the files to add, can include wild cards");
for(auto itr = files.begin(); itr != files.end(); itr++)
list->pushBack((*itr)->data + L": " + (*itr)->str);
list->pushBack(L"");
}
return true;
}
Utils::WString CBaseFile::getAutosaveName() const
{
return this->name() + L"-V" + this->version() + L"-" + this->creationDate().findReplace(L"/", L".");
}
void CBaseFile::addGlobal(const Utils::WString& global, const Utils::WString& setting)
{
if (!_lGlobals.changeData(global, setting))
_lGlobals.pushBack(global, setting);
}
void CBaseFile::removeGlobal(const Utils::WString& global)
{
_lGlobals.remove(global);
}
void CBaseFile::clearGlobals()
{
_lGlobals.clear();
}
bool CBaseFile::CheckGameCompatability(int game)
{
if ( m_lGames.empty() )
return true; // no game compatability added, assume its ok for all
for ( 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, const Utils::WString &sVersion, int iVersion) const
{
if (m_lGames.empty())
return true; // no game compatability added, assume its ok for all
bool 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 version
if ( node->Data()->sVersion.empty() ) {
if (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::WString &version)
{
// first check if we already have it on the list
SGameCompat *Found = this->GetGameCompatability(game);
if ( !Found ) {
Found = new SGameCompat;
m_lGames.push_back(Found);
}
Found->iGame = game;
Found->iVersion = -1;
Found->sVersion = L"";
if ( version.contains(L".") || !version.isNumber() )
Found->sVersion = version;
else
Found->iVersion = version;
_changed();
}
Utils::WString CBaseFile::_replaceFilename(const Utils::WString &fname)
{
Utils::WString filename = fname;
Utils::WString cdate = this->creationDate().findReplace(L"/", L".").remove(' ');
if (filename.contains(L"$AUTOSAVE"))
{
if (this->GetType() == TYPE_XSP)
filename = filename.findReplace(L"$AUTOSAVE", L"$NAME-V$VERSION-$CDATE.xsp");
else
filename = filename.findReplace(L"$AUTOSAVE", L"$NAME-V$VERSION-$CDATE.spk");
}
filename = filename.findReplace(L"$NAME", this->name().remove(L' '));
filename = filename.findReplace(L"$AUTHOR", this->author().remove(L' '));
filename = filename.findReplace(L"$DATE", cdate);
filename = filename.findReplace(L"$CDATE", cdate);
filename = filename.findReplace(L"$VERSION", this->version());
return filename;
}
bool CBaseFile::loadPackageData(const Utils::WString &sFirst, const Utils::WString &sRest, const Utils::WString &sMainGame, Utils::WStringList &otherGames, Utils::WStringList &gameAddons, CProgressInfo *progress)
{
if (sFirst.Compare(L"Name")) this->setName(sRest);
else if (sFirst.Compare(L"Author")) this->setAuthor(sRest);
else if (sFirst.Compare(L"ScriptName")) addName(parseLanguage(sRest.token(L" ", 1)), sRest.tokens(L" ", 2));
else if (sFirst.Compare(L"UninstallBefore")) this->addUninstallText(parseLanguage(sRest.token(L" ", 1)), true, sRest.tokens(L" ", 2));
else if (sFirst.Compare(L"UninstallAfter")) this->addUninstallText(parseLanguage(sRest.token(L" ", 1)), false, sRest.tokens(L" ", 2));
else if (sFirst.Compare(L"InstallBefore")) this->addInstallText(parseLanguage(sRest.token(L" ", 1)), true, sRest.tokens(L" ", 2));
else if (sFirst.Compare(L"InstallAfter")) this->addInstallText(parseLanguage(sRest.token(L" ", 1)), false, sRest.tokens(L" ", 2));
else if (sFirst.Compare(L"Date")) this->setCreationDate(sRest);
else if (sFirst.Compare(L"Version")) this->setVersion(sRest);
else if (sFirst.Compare(L"GameVersion"))
this->AddGameCompatability(-1, sRest);
else if (sFirst.Compare(L"PluginType")) {
if (sRest.isNumber()) this->setPluginType(sRest);
else if (sRest.Compare(L"Normal")) this->setPluginType(PLUGIN_NORMAL);
else if (sRest.Compare(L"Stable")) this->setPluginType(PLUGIN_STABLE);
else if (sRest.Compare(L"Experimental")) this->setPluginType(PLUGIN_EXPERIMENTAL);
else if (sRest.Compare(L"Cheat")) this->setPluginType(PLUGIN_CHEAT);
else if (sRest.Compare(L"Mod")) this->setPluginType(PLUGIN_MOD);
}
// new version
else if (sFirst.Compare(L"GenerateUpdateFile"))
m_bAutoGenerateUpdateFile = true;
else if (sFirst.Compare(L"Game"))
{
Utils::WString sGame = sRest.token(L" ", 1);
this->AddGameCompatability(CBaseFile::GetGameFromString(sGame), sRest.token(L" ", 2));
}
else if (sFirst.Compare(L"Description")) this->setDescription(sRest);
else if (sFirst.Compare(L"AutoSave") || sFirst.Compare(L"AutoExport") || sFirst.Compare(L"AutoRarExport") || sFirst.Compare(L"AutoZipExport"))
{
Utils::WString filename = _replaceFilename(sRest);
if (sFirst.Compare(L"AutoZipExport") || sFirst.Compare(L"AutoExport"))
this->setExportFilename(CFileIO(filename).changeFileExtension(L"zip"));
else if (sFirst.Compare(L"AutoRarExport"))
this->setExportFilename(CFileIO(filename).changeFileExtension(L"rar"));
else
this->setFilename(filename);
}
else if (sFirst.Compare(L"WebSite")) this->setWebSite(sRest);
else if (sFirst.Compare(L"ForumLink") || sFirst.Compare(L"Forum")) this->setForumLink(sRest);
else if (sFirst.Compare(L"Email")) this->setEmail(sRest);
else if (sFirst.Compare(L"WebAddress")) this->setWebAddress(sRest);
else if (sFirst.Compare(L"WebMirror"))
this->addWebMirror(sRest);
else if (sFirst.Compare(L"WebMirror1"))
this->addWebMirror(sRest);
else if (sFirst.Compare(L"WebMirror2"))
this->addWebMirror(sRest);
else if (sFirst.Compare(L"Ftp"))
_sFtpAddr = sRest;
else if (sFirst.Compare(L"Ratings")) _setRatings(sRest.token(L" ", 1), sRest.token(L" ", 2), sRest.token(L" ", 3));
else if (sFirst.Compare(L"EaseOfUse")) setEaseOfUse(sRest);
else if (sFirst.Compare(L"GameChanging")) setGameChanging(sRest);
else if (sFirst.Compare(L"Recommended")) setRecommended(sRest);
else if (sFirst.Compare(L"Depend"))
{
Utils::WString version = sRest.token(L"|", 2);
Utils::WString name = sRest.token(L"|", 1);
Utils::WString author = sRest.tokens(L"|", 3);
this->addNeededLibrary(name, author, version);
}
else if (sFirst.Compare(L"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(L"Icon"))
{
C_File *icon = new C_File(sRest);
if (icon->ReadFromFile())
this->setIcon(icon, CFileIO(sRest).extension());
}
else if (sFirst.Compare(L"CombineGameFiles"))
_bCombineFiles = sRest.Compare(L"true") || sRest.Compare(L"yes") || sRest.toInt();
else if (sFirst.Compare(L"UpdateFile"))
this->createUpdateFile(sRest);
else if (sFirst.Compare(L"ExportZip"))
{
Utils::WString ext = L"zip";
Utils::WString game = sRest.word(1);
Utils::WString file = _replaceFilename(CFileIO(sRest.words(2)).fullFilename());
if (game.contains(L"|"))
{
std::vector<Utils::WString> games;
if(game.tokenise(L"|", games))
{
for (size_t i = 0; i < games.size(); ++i)
{
unsigned int g = CBaseFile::GetGameFromString(games[i]);
Utils::WString filename = CFileIO(file).dir() + L"/" + CFileIO(file).baseName() + L"_" + CBaseFile::ConvertGameToString(g) + L"." + ext;
this->addAutoExport(g, filename);
}
}
}
else
{
unsigned int g = CBaseFile::GetGameFromString(game);
Utils::WString filename = CFileIO(file).dir() + L"/" + CFileIO(file).baseName() + L"_" + CBaseFile::ConvertGameToString(g) + L"." + ext;
this->addAutoExport(g, filename);
}
}
else if (sFirst.Compare(L"Extract"))
{
Utils::WString game = sRest.word(1);
Utils::WString dir = CDirIO(sRest.words(2)).dir();
if (game.contains(L"|"))
{
std::vector<Utils::WString> games;
if (game.tokenise(L"|", games))
{
for(size_t i = 0; i < games.size(); ++i)
this->addAutoExtract(CBaseFile::GetGameFromString(games[i]), dir);
}
}
else
this->addAutoExtract(CBaseFile::GetGameFromString(game), dir);
}
else if (sFirst.Compare(L"Global"))
this->addGlobal(sRest.token(L";", 1), sRest.token(L";", 2));
else
{
Utils::WString checkType = sFirst;
bool shared = false;
if (checkType.left(6).Compare(L"Shared"))
{
checkType = sFirst.right(-6);
shared = true;
}
bool packed = false;
if (checkType.right(3).Compare(L"PCK"))
{
checkType = sFirst.left(-3);
packed = true;
}
// now check type name
FileType filetype = GetFileTypeFromString(checkType);
if (filetype != FILETYPE_UNKNOWN)
this->addFileScript(filetype, shared, packed, sRest, sMainGame, otherGames, gameAddons, progress);
else if ( !checkType.Compare(L"changelog") )
return false;
}
return true;
}
void CBaseFile::addFileScript(FileType filetype, bool shared, bool packed, const Utils::WString &sRest, const Utils::WString &sMainGame, Utils::WStringList &otherGames, Utils::WStringList &gameAddons, CProgressInfo *progress)
{
Utils::WString dir;
Utils::WString rest = sRest;
unsigned int mainGame = CBaseFile::GetGameFromString(sMainGame);
unsigned int game = 0;
if ( rest.token(L" ", 1).left(4).Compare(L"GAME") ) {
Utils::WString gameStr = rest.token(L" ", 2);
if (gameStr.contains(L"|"))
{
std::vector<Utils::WString> games;
gameStr.tokenise(L"|", games);
for (size_t i = 0; i < games.size(); ++i)
{
unsigned int g = CBaseFile::GetGameFromString(games[i]);
if (g)
game |= 1 << g;
}
}
else
{
unsigned int g = CBaseFile::GetGameFromString(gameStr);
if (g)
game = 1 << g;
}
rest = rest.tokens(L" ", 3);
}
if (game)
game |= 1 << 31;
if (rest.contains(L"|"))
{
dir = rest.tokens(L"|", 2);
rest = rest.token(L"|", 1);
}
rest = rest.findReplace(L"\\", L"/");
// wild cards
if ( rest.containsAny(L"*?") )
{
CDirIO Dir(CFileIO(rest).dir());
Utils::WStringList dirList;
if(Dir.dirList(dirList))
{
for(auto itr = dirList.begin(); itr != dirList.end(); itr++)
{
Utils::WString file = Dir.file((*itr)->str);
if (file.match(rest))
{
int addGame = game;
// check if the file exists in the subdirectory too, if it does, add for each game
if ( game == GAME_ALL && !sMainGame.empty() && !otherGames.empty() ) {
CFileIO F(file);
for(Utils::WString g = otherGames.firstString(); !g.empty(); g = otherGames.nextString()) {
Utils::WString checkDir = F.dir() + L"/" + g;
if ( CDirIO(checkDir).exists(F.filename()) ) {
addGame = mainGame;
C_File *newfile = this->appendFile(CDirIO(checkDir).file(F.filename()), filetype, CBaseFile::GetGameFromString(g), packed, dir);
if (newfile && progress)
progress->UpdateFile(newfile);
if ( newfile ) newfile->SetShared(shared);
}
}
}
C_File *newfile = this->appendFile(file, filetype, addGame, packed, dir);
if (newfile && progress)
progress->UpdateFile(newfile);
if ( newfile )
newfile->SetShared(shared);
}
}
}
}
else
{
unsigned int addGame = game;
// check if the file exists in the subdirectory too, if it does, add for each game
if ( game == GAME_ALL && !sMainGame.empty() && !otherGames.empty() ) {
CFileIO F(rest);
for(Utils::WString g = otherGames.firstString(); !g.empty(); g = otherGames.nextString()) {
Utils::WString checkDir = F.dir() + L"/" + g;
if ( CDirIO(checkDir).exists(F.filename()) ) {
addGame = mainGame;
C_File *newfile = this->appendFile(CDirIO(checkDir).file(F.filename()), filetype, CBaseFile::GetGameFromString(g), packed, dir);
if (newfile && progress)
progress->UpdateFile(newfile);
if ( newfile ) newfile->SetShared(shared);
}
}
}
unsigned int checkGame = addGame & ~(1 << 31);
unsigned int foundGame = 0;
for (int i = 0; i < 31; ++i)
{
if (1 << i == checkGame)
{
foundGame = i;
break;
}
}
C_File *file = NULL;
if (foundGame)
{
Utils::WString addon = gameAddons.findString(Utils::WString::Number(foundGame));
if (!addon.empty())
{
Utils::WString dir = C_File::GetDirectory(filetype, rest, this);
Utils::WString filename = rest;
if (CCatFile::IsAddonDir(dir) && !filename.contains(addon))
{
filename = filename.findReplace(dir + L"/", addon + L"/" + dir + L"/");
file = this->appendFile(filename, filetype, addGame, packed, dir);
}
}
}
if(!file)
file = this->appendFile(rest, filetype, addGame, packed, dir);
if (file && progress)
progress->UpdateFile(file);
if (file)
file->SetShared(shared);
}
}
Utils::WString CBaseFile::fileSizeString() const { return SPK::GetSizeString ( this->fileSize() ); }
// used for a multiple spk file
unsigned char *CBaseFile::createData(size_t *size, CProgressInfo *progress)
{
if ( this->writeFile(L"temp.dat", progress) ) {
CFileIO File(L"temp.dat");
File.setAutoDelete(true);
return File.readAll(size);
}
return NULL;
}
void CBaseFile::convertNormalMod(C_File *f, const Utils::WString &to) const
{
C_File *match = this->findMatchingMod(f);
if ( match )
{
// file link
if ( !match->GetData() )
match->ReadFromFile();
match->changeBaseName(to);
}
// file link
if ( !f->GetData() )
f->ReadFromFile();
f->changeBaseName(to);
}
void CBaseFile::convertAutoText(C_File *f) const
{
Utils::WString to;
if ( f->baseName().contains(L"-L") )
to = L"0000-L" + f->baseName().token(L"-L", 2);
else if ( f->baseName().contains(L"-l") )
to = L"0000-L" + f->baseName().token(L"-l", 2);
else
to = f->baseName().left(-4) + L"0000";
// file link
if ( !f->GetData() )
f->ReadFromFile();
f->changeBaseName(to);
}
void CBaseFile::convertFakePatch(C_File *f) const
{
// find next available fake patch
int num = 0;
bool found = true;
while ( found )
{
++num;
found = false;
Utils::WString find = Utils::WString::PadNumber(num, 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()->baseName().Compare(find) )
{
found = true;
break;
}
}
}
Utils::WString to = Utils::WString::PadNumber(num, 2);
C_File *match = this->findMatchingMod(f);
// file link
if ( !f->GetData() )
f->ReadFromFile();
f->changeBaseName(to);
if ( match )
{
// file link
if ( !match->GetData() )
match->ReadFromFile();
match->changeBaseName(to);
}
}
C_File *CBaseFile::findMatchingMod(C_File *f) const
{
for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
{
if ( node->Data()->GetFileType() != FILETYPE_MOD )
continue;
if ( f->fileExt().Compare(node->Data()->fileExt()) )
continue;
if ( f->baseName().Compare(node->Data()->baseName()) )
return node->Data();
}
return NULL;
}
void CBaseFile::renameFile(C_File *f, const Utils::WString &baseName) const
{
if ( f->GetFileType() == FILETYPE_MOD )
{
C_File *match = this->findMatchingMod(f);
if ( match )
match->changeBaseName(baseName);
}
// need to edit the file
if (f->GetFileType() == FILETYPE_SCRIPT || f->GetFileType() == FILETYPE_UNINSTALL)
f->renameScript(baseName);
f->changeBaseName(baseName);
}
Utils::WString CBaseFile::createUpdateFile(const Utils::WString &dir) const
{
Utils::WString file = this->getNameValidFile() + L"_" + this->author() + L".dat";
file.removeChar(' ');
Utils::WStringList write;
write.pushBack(L"Package: " + this->name());
write.pushBack(L"Author: " + this->author());
write.pushBack(L"Version: " + this->version());
write.pushBack(L"File: " + CFileIO(this->filename()).filename());
CFileIO File(dir + L"/" + file);
if (File.writeFile(&write))
return File.fullFilename();
return Utils::WString::Null();
}
Utils::WString CBaseFile::ErrorString(int error, const Utils::WString &errorStr)
{
if (error == SPKERR_NONE) return Utils::WString::Null();
Utils::WString err;
switch (error)
{
case SPKERR_MALLOC:
err = L"Memory Failed";
break;
case SPKERR_FILEOPEN:
err = L"Failed to open file";
break;
case SPKERR_FILEREAD:
err = L"Failed to read file";
break;
case SPKERR_UNCOMPRESS:
err = L"Failed to Uncompress";
break;
case SPKERR_WRITEFILE:
err = L"Failed to write file";
break;
case SPKERR_CREATEDIRECTORY:
err = L"Failed to create directory";
break;
case SPKERR_FILEMISMATCH:
err = L"File count mismatch";
break;
}
if (!err.empty())
{
if (!errorStr.empty())
{
err += L" (";
err += errorStr + L")";
}
return err;
}
return Utils::WString::Number((long)error);
}
bool CBaseFile::saveToArchive(const Utils::WString &filename, int game, const CGameExe *exes, CProgressInfo *progress)
{
CDirIO Dir(CFileIO(filename).dir());
if (!Dir.exists())
Dir.create();
HZIP hz = CreateZip(filename.c_str(), 0);
if (!hz) return false;
// read files and compress
ReadAllFilesToMemory();
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()->game() & (1 << game)))
continue;
// extracting for all games, so ignore files that have a game set
if (!game && node->Data()->game() && node->Data()->game() != GAME_ALLNEW)
continue;
}
Utils::WString fname = node->Data()->getNameDirectory(this);
// use the addon directory
if (node->Data()->isFileInAddon() && exes)
{
unsigned int whatGame = game;
if (game == -1)
{
unsigned int checkGame = node->Data()->game() & ~GAME_ALLNEW;
whatGame = 0;
for (int i = 0; i < 31; ++i)
{
if (1 << i == checkGame)
{
whatGame = i;
break;
}
}
}
if (whatGame > 0)
{
SGameExe *e = exes->game(whatGame - 1);
if (e)
{
if (e->iFlags & EXEFLAG_ADDON)
fname = e->sAddon + L"/" + fname;
}
}
}
// create the directory
//wsprintf(buf, L"%hs", fname.c_str());
if (node->Data()->isExternalFile())
{
CFileIO file(node->Data()->filePointer());
size_t size = 0;
unsigned char *data = file.readAll(&size);
ZipAdd(hz, fname.c_str(), data, size);
delete data;
}
else
ZipAdd(hz, fname.c_str(), node->Data()->GetData(), node->Data()->GetDataSize());
}
// if its a ship, then add any generated files
this->addGeneratedFiles(hz);
// add the data file
Utils::WStringList list;
Utils::WStringList addons;
if ( this->GeneratePackagerScript(false, &list, game, addons, true) )
{
if ( CFileIO(L"test.tmp").writeFile(&list) )
{
ZipAdd(hz, L"pluginmanager.txt", L"test.tmp");
CFileIO::Remove(L"test.tmp");
}
}
CloseZip(hz);
return true;
}
int CBaseFile::GetGameFromString(const Utils::WString &sGame)
{
int iGame = GAME_ALL;
if ( sGame.Compare(L"ALL") )
iGame = GAME_ALL;
else if ( sGame.Compare(L"X3") )
iGame = GAME_X3;
else if ( sGame.Compare(L"X2") )
iGame = GAME_X2;
else if ( sGame.Compare(L"X3TC") )
iGame = GAME_X3TC;
else if (sGame.Compare(L"X3AP"))
iGame = GAME_X3AP;
else if (sGame.Compare(L"X3FL"))
iGame = GAME_X3FL;
else if ( sGame.isNumber() )
iGame = sGame;
return iGame;
}
Utils::WString CBaseFile::ConvertGameToString(int iGame)
{
Utils::WString game = L"ALL";
switch(iGame) {
case GAME_ALL:
case GAME_ALLNEW:
game = L"ALL";
break;
case GAME_X2:
game = L"X2";
break;
case GAME_X3:
game = L"X3";
break;
case GAME_X3TC:
game = L"X3TC";
break;
case GAME_X3AP:
game = L"X3AP";
break;
case GAME_X3FL:
game = L"X3FL";
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::WString builtInWares()
{
Utils::WString str;
str += L"28;0;0;0;0;59;5753;0;35714;1;1;0;35714;-100000;0;0;SS_WARE_SW_NEW1;\n";
str += L"28;0;0;0;0;60;5763;0;33232;1;1;0;33232;0;1043;0;SS_WARE_SW_NEW2;\n";
str += L"28;0;0;0;0;61;5773;0;21428;1;1;0;21428;0;1043;0;SS_WARE_SW_NEW3;\n";
str += L"28;0;0;0;0;62;5783;0;56;1;1;0;56;-100000;0;0;SS_WARE_SW_NEW4;\n";
str += L"28;0;0;0;0;63;5793;0;88;1;1;0;88;-100000;0;0;SS_WARE_SW_NEW5;\n";
str += L"28;0;0;0;0;64;5803;0;283;1;1;0;283;-100000;0;0;SS_WARE_SW_NEW6;\n";
str += L"28;0;0;0;0;65;5813;0;383;1;1;0;383;-100000;0;0;SS_WARE_SW_NEW7;\n";
str += L"28;0;0;0;0;66;5823;0;1389;1;1;0;1389;-100000;1043;0;SS_WARE_SW_NEW8;\n";
str += L"28;0;0;0;0;67;5833;0;3396;1;1;0;3396;-100000;0;0;SS_WARE_SW_NEW9;\n";
str += L"28;0;0;0;0;68;5843;0;4215;1;1;0;4215;-100000;0;0;SS_WARE_SW_NEW10;\n";
str += L"28;0;0;0;0;69;5853;0;5635;1;1;0;5635;-100000;0;0;SS_WARE_SW_NEW11;\n";
str += L"28;0;0;0;0;70;5863;0;65735;1;1;0;65735;-100000;0;0;SS_WARE_SW_NEW12;\n";
str += L"28;0;0;0;0;71;5873;0;17857;1;1;0;17857;333;1043;0;SS_WARE_SW_NEW13;\n";
str += L"28;0;0;0;0;72;5883;0;21428;1;1;0;21428;0;1043;0;SS_WARE_SW_NEW14;\n";
str += L"28;0;0;0;0;73;5893;0;324515;1;1;0;324515;-100000;0;0;SS_WARE_SW_NEW15;\n";
str += L"28;0;0;0;0;74;5903;0;638508;1;1;0;638508;-100000;0;0;SS_WARE_SW_NEW16;\n";
str += L"28;0;0;0;0;75;5913;0;225755;1;1;0;225755;-100000;0;0;SS_WARE_SW_NEW17;\n";
str += L"28;0;0;0;0;76;5923;0;1931535;1;1;0;1931535;1000;0;0;SS_WARE_SW_NEW18;\n";
str += L"28;0;0;0;0;77;5933;0;2209150;1;1;0;2209150;-100000;0;0;SS_WARE_SW_NEW19;\n";
str += L"28;0;0;0;0;78;5943;0;6727565;1;1;0;6727565;-100000;0;0;SS_WARE_SW_NEW20;\n";
str += L"28;0;0;0;0;85;9999;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_1;\n";
str += L"28;0;0;0;0;86;15053;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_2;\n";
str += L"28;0;0;0;0;87;15063;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_3;\n";
str += L"28;0;0;0;0;88;15073;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_4;\n";
str += L"28;0;0;0;0;89;15083;0;105;1;5;0;105;0;1043;0;SS_WARE_SW_X3TC_5;\n";
str += L"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::WString &wares, enum WareTypes eType)
{
if (_pTextDB)
{
std::vector<Utils::WString> w;
wares.tokenise(L"\n", w);
for (size_t i = 0; i < w.size(); i++) {
int textId = w[i].token(L";", 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(L";", -2);
ware->relval = w[i].token(L";", 9).toLong();
ware->notority = w[i].token(L";", 14).toLong();
ware->type = eType;
ware->position = i;
ware->package = this;
list.push_back(ware);
}
}
}
}
bool CBaseFile::readWares(int iLang, CLinkList<SWareEntry> &list, const Utils::WString &empWares)
{
if (_pTextDB)
{
_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;
}
return false;
}
bool CBaseFile::_readCommands(int iLang, int iStartID, CLinkList<SCommandSlot> &list)
{
if (_pTextDB)
{
_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;
}
return false;
}
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;
}