Rev 245 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "GameExe.h"
#include "File_IO.h"
#include "DirIO.h"
/**
* Add Exe
*
* Adds an exe name available
*/
int CGameExe::addExe(const Utils::WString &exe)
{
// search if it already exists
int iCount = this->_findExe(exe);
if ( iCount != -1 ) return iCount;
// not found, we need to add
SGameExe *sExe = new SGameExe;
sExe->sExe = exe;
sExe->iName = 0;
sExe->iFlags = 0;
sExe->iMaxPatch = 1;
sExe->iAddonTo = 0;
sExe->iTextNum = 0;
m_lExe.push_back(sExe);
return m_lExe.size() - 1;
}
/**
* Find Exe
*
* Find an exe and return its position in the file
*
* Argument: exe, String - name of the exe file to find
*/
int CGameExe::_findExe(const Utils::WString &exe) const
{
CFileIO File(exe);
Utils::WString e = File.filename();
int count = 0;
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
if ( node->Data()->sExe.Compare(e) )
return count;
++count;
}
return -1;
}
SGameExe *CGameExe::gameExe(const Utils::WString &exe) const
{
int e = this->_findExe(exe);
if ( e < 0 ) return nullptr;
return m_lExe.Get(e);
}
int CGameExe::_findVersion(int exe, int size, Utils::WString *fVersion) const
{
if ( fVersion ) *fVersion = -1.0;
if ( exe < 0 ) return -1;
SGameExe *gameExe = m_lExe[exe];
if ( !gameExe ) return -1;
int count = 0;
for ( CListNode<SGameExeVersion> *node = gameExe->lVersions.Front(); node; node = node->next() ) {
for ( CListNode<int> *iNode = node->Data()->lSize.Front(); iNode; iNode = iNode->next() ) {
int checkSize = *iNode->Data();
if ( checkSize == size ) {
*fVersion = node->Data()->fVersion;
return count;
}
}
++count;
}
return -1;
}
int CGameExe::findVersion(const Utils::WString &exe, int size, Utils::WString *fVersion)
{
int iExe = this->_findExe(exe);
if ( iExe < 0 )
return -1;
int iVersion = this->_findVersion(iExe, size, fVersion);
if ( iVersion < 0 )
return -2 - iExe;
return -1;
}
Utils::WString CGameExe::getModKey(int game) const
{
if ( game < 0 ) return L"";
SGameExe *sExe = m_lExe[game];
if ( !sExe ) return L"";
return sExe->sModKey;
}
void CGameExe::getDirectoryData(GameDirectory *gameDir) const
{
if (!gameDir)
return;
if (CDirIO::Exists(gameDir->dir))
{
gameDir->exe = this->gameRunExe(gameDir->dir);
if (CDirIO::Exists(gameDir->exe))
{
int id = _findExe(gameDir->exe);
SGameExe *exe = m_lExe.Get(id);
if (exe)
{
gameDir->addon = exe->sAddon;
gameDir->name = exe->sName;
if (exe->iFlags & EXEFLAG_MYDOCLOG)
gameDir->logdir = exe->sMyDoc;
else
gameDir->logdir = gameDir->dir;
gameDir->basename = exe->sName;
}
this->getGameVersion(gameDir->exe, &gameDir->version);
gameDir->name = this->gameName(gameDir->exe);
}
}
}
void CGameExe::parseExe(const Utils::WString &line)
{
// get the exe file
Utils::WString exe = line.token(L":", 1);
int iTextNum = -1;
if ( exe.contains(L"|") ) {
iTextNum = exe.token(L"|", 2);
exe = exe.token(L"|", 1);
}
int iExe = this->addExe(exe);
SGameExe *sExe = m_lExe[iExe];
sExe->iMaxPatch = line.token(L":", 2);
sExe->iAudioStream = line.token(L":", 3).toInt();
sExe->iFlags = this->parseFlags(line.token(L":", 4));
sExe->sModKey = line.token(L":", 5);
sExe->iTextNum = iTextNum;
// get the name
Utils::WString gameName = line.token(L":", 6);
this->_setExeName(&sExe->sName, &sExe->iName, gameName);
// get mydocs
sExe->sMyDoc = line.token(L":", 7);
// now get the versions
int pos = EXE_VERSIONPOS;
int namestart = EXE_VERSION_NAMESTART;
int sizestart = EXE_VERSION_SIZESTART;
if ( sExe->iFlags & EXEFLAG_ADDON ) {
++pos;
++namestart;
++sizestart;
sExe->sAddon = line.token(L":", EXE_VERSIONPOS);
if ( sExe->sAddon.contains(L"!") ) {
sExe->iAddonTo = this->_findExe(sExe->sAddon.token(L"!", 2));
sExe->sAddon = sExe->sAddon.token(L"!", 1);
}
}
int iVersions = line.token(L":", pos);
int i;
for ( i = 0; i < iVersions; i++ )
{
SGameExeVersion *sGameVersion = new SGameExeVersion;
sGameVersion->iName = 0;
sGameVersion->fVersion = line.token(L":", namestart + (i * 2)).token(L" ", 1);
Utils::WString sSize = line.token(L":", sizestart + (i * 2));
// multiple versions available, we need to split them up
if ( sSize.contains(L"!") ) {
std::vector<Utils::WString> sizes;
if(sSize.tokenise(L"!", sizes))
{
for (size_t j = 0; j < sizes.size(); j++ )
{
int *size = new int;
(*size) = sizes[j];
if ( *size ) sGameVersion->lSize.push_back(size);
}
}
}
else {
int *size = new int;
(*size) = sSize;
if ( *size ) sGameVersion->lSize.push_back(size);
}
if ( !sGameVersion->lSize.empty() ) {
// finally, add the version names
this->_setExeName(&sGameVersion->sName, &sGameVersion->iName, line.token(L":", namestart + (i * 2)));
sExe->lVersions.push_back(sGameVersion);
}
}
}
int CGameExe::parseFlags(const Utils::WString &flags)
{
std::vector<Utils::WString> sFlags;
if (!flags.tokenise(L"|", sFlags))
return EXEFLAG_NONE;
int f = 0;
for (size_t i = 0; i < sFlags.size(); i++) {
Utils::WString str = sFlags[i];
if ( str.Compare(L"TC_TEXT") )
f |= EXEFLAG_TCTEXT;
else if (str.Compare(L"NO_XOR"))
f |= EXEFLAG_NOXOR;
else if (str.Compare(L"SETTINGFILE"))
f |= EXEFLAG_SETTINGFILE;
else if ( str.Compare(L"ADDON") )
f |= EXEFLAG_ADDON;
else if ( str.Compare(L"MYDOCLOG") )
f |= EXEFLAG_MYDOCLOG;
else if ( str.Compare(L"NOSAVESUBDIR") )
f |= EXEFLAG_NOSAVESUBDIR;
else {
if ( str.isNumber() )
f |= (long)str;
}
}
return f;
}
void CGameExe::_setExeName(Utils::WString *sName, int *iName, const Utils::WString &n)
{
Utils::WString str = n;
if ( n.contains(L"!") )
{
Utils::WString gameNum = str.token(L"!", 1);
str = str.token(L"!", 2);
if ( gameNum.isNumber() )
*iName = gameNum;
else
*sName = gameNum;
}
if ( str.isNumber() )
*iName = str;
else
*sName = str;
}
void CGameExe::Reset()
{
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
for ( CListNode<SGameExeVersion> *vNode = node->Data()->lVersions.Front(); vNode; vNode = vNode->next() )
vNode->Data()->lSize.MemoryClear();
node->Data()->lVersions.MemoryClear();
}
m_lExe.MemoryClear();
}
bool CGameExe::readFile(const Utils::WString &file)
{
CFileIO File(file);
if ( !File.startRead() ) return false;
while ( !File.atEnd() ) {
Utils::WString line = File.readEndOfLine();
line.removeFirstSpace();
if ( line.empty() ) continue;
if ( line[0] == '/' ) continue;
this->parseExe(line);
}
File.close();
return true;
}
Utils::WString CGameExe::gameRunExe(const Utils::WString &dir) const
{
CDirIO Dir(dir);
int count = 0;
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
SGameExe *exe = node->Data();
if ( Dir.exists(exe->sExe) )
return dir + L"/" + exe->sExe;
if ( !exe->sAddon.empty() ) {
if ( Dir.topDir().Compare(exe->sAddon) )
return this->gameDir(dir) + L"/" + exe->sExe;
}
++count;
}
return L"";
}
Utils::WString CGameExe::gameBaseName(const Utils::WString &gameExe) const
{
int gameType = this->getGameType(gameExe);
Utils::WString gameDir = this->properDir(gameExe);
Utils::WString gameName = this->extractGameName(gameDir);
if (gameName.empty()) gameName = this->gameNameFromType(gameType);
return gameName;
}
Utils::WString CGameExe::gameName(const Utils::WString &gameExe) const
{
Utils::WString gameName = gameBaseName(gameExe);
if (gameName.empty()) return L"";
// no version
Utils::WString fVersion;
Utils::WString versionName;
if ( this->getGameVersionName(gameExe, &versionName) )
{
if ( !versionName.empty() )
return gameName + L" V" + versionName;
else
return gameName;
}
int gameType = this->getGameType(gameExe);
Utils::WString sGameVersion = this->gameVersionFromType(gameType, this->getGameVersion(gameExe, &fVersion), fVersion);
if ( sGameVersion.empty() )
{
if ( !fVersion.empty() )
return gameName + L" V" + fVersion;
else
return gameName;
}
// return the name and the version
return gameName + L" " + sGameVersion;
}
int CGameExe::getGameAddons(const Utils::WString &dir, Utils::WStringList &exes) const
{
int count = 0;
CDirIO Dir(dir);
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
SGameExe *exe = node->Data();
if ( !(exe->iFlags & EXEFLAG_ADDON) )
continue;
if ( Dir.exists(exe->sExe) ) {
if ( Dir.exists(exe->sAddon) ) {
exes.pushBack(exe->sExe, exe->sAddon);
++count;
}
}
}
return count;
}
Utils::WString CGameExe::addonDir(const Utils::WString &dir) const
{
int gameType = this->getGameType(dir);
if ( gameType != -1 ) {
return m_lExe[gameType]->sAddon;
}
return L"";
}
bool CGameExe::isAddon(const Utils::WString &exe) const
{
SGameExe *e = gameExe(exe);
if (e && (e->iFlags & EXEFLAG_ADDON))
return true;
return false;
}
int CGameExe::getTextID(const Utils::WString &dir) const
{
SGameExe *e = gameExe(dir);
if (e)
return e->iTextNum;
e = gameExe(this->gameRunExe(dir));
if (e)
return e->iTextNum;
return 0;
}
Utils::WString CGameExe::properDir(const Utils::WString &dir) const
{
CDirIO Dir(dir);
int gameType = this->getGameType(dir);
if ( gameType != -1 ) {
if ( !m_lExe[gameType]->sAddon.empty() ) {
if ( CDirIO(dir).isFile() ) return this->gameDir(dir) + L"/" + m_lExe[gameType]->sAddon;
return CDirIO(this->gameDir(dir)).dir(m_lExe[gameType]->sAddon);
}
}
return CDirIO(dir).isFile() ? CFileIO(dir).dir() : dir;
}
int CGameExe::gameFlags(int game)
{
if ( game == -1 )
return 0;
SGameExe *exe = m_lExe[game];
if ( !exe )
return 0;
return exe->iFlags;
}
int CGameExe::maxPatch(int game)
{
if ( game == -1 )
return 0;
SGameExe *exe = m_lExe[game];
if ( !exe )
return 0;
return exe->iMaxPatch;
}
Utils::WString CGameExe::gameNameFromType(int type) const
{
if ( type == -1 ) return L"";
SGameExe *exe = m_lExe.Get(type);
if ( !exe ) return L"";
return exe->sName;
}
Utils::WString CGameExe::gameVersionFromType(int game, int gameVersion, const Utils::WString &fGameVersion) const
{
SGameExe *exe = m_lExe[game];
if ( !exe ) return L"";
SGameExeVersion *version = exe->lVersions[gameVersion];
if ( !version )
{
if ( !fGameVersion.empty() ) return fGameVersion;
return L"";
}
return version->sName;
}
Utils::WString CGameExe::gameDir(const Utils::WString &dir) const
{
CDirIO Dir(dir);
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
SGameExe *exe = node->Data();
if ( CDirIO(dir).isFile() ) {
if ( CFileIO(dir).filename().Compare(exe->sExe) )
return CFileIO(dir).dir();
}
else {
if ( Dir.exists(exe->sExe) ) return dir;
// check for addon dir
if ( !exe->sAddon.empty() ) {
if ( exe->sAddon.Compare(Dir.topDir()) )
return Dir.back();
}
}
}
return dir;
}
int CGameExe::findAddonType(const Utils::WString &addon) const
{
int i = 0;
for (CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next())
{
SGameExe *exe = node->Data();
if (exe->iFlags & EXEFLAG_ADDON)
{
if (exe->sAddon.Compare(addon))
return i;
}
++i;
}
return -1;
}
int CGameExe::getGameType(const Utils::WString &gameExe) const
{
CDirIO Dir (gameExe);
int count = 0;
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
SGameExe *exe = node->Data();
if ( CDirIO(gameExe).isFile() ) {
if ( CFileIO(gameExe).filename().Compare(exe->sExe) )
return count;
}
else {
if ( Dir.exists(exe->sExe) )
return count;
// check for addon dir
if ( !exe->sAddon.empty() ) {
if ( exe->sAddon.Compare(Dir.topDir()) )
return count;
}
}
++count;
}
return -1;
}
Utils::WString CGameExe::extractGameName(const Utils::WString &gameDir, Utils::WString *extra) const
{
Utils::WString dir = this->properDir(gameDir);
CDirIO Dir(dir);
Utils::WString sText = this->_extractTextData(this->_readTextFile(dir), 1910, 1216, this->getTextID(gameDir));
if ( sText.empty() ) return L"";
int end = sText.findPos(L"\\n");
if (end >= 0)
{
sText = sText.left(end);
sText = sText.findReplace(L"\\(", L"(").findReplace(L"\\)", L")");
if (sText.contains(L"(") && sText.contains(L")"))
{
int pos = sText.findPos(L"(");
int endPos;
for (endPos = sText.length() - 1; endPos > pos; pos--)
{
if (sText[endPos] == ')')
break;
}
Utils::WString e = sText.substr(pos, endPos);
sText = sText.findRemove(e).removeEndSpace();
if (extra)
(*extra) = e.mid(1, -1);
}
return sText;
}
return "";
}
Utils::WString CGameExe::_textFileName(const Utils::WString &sGameDir) const
{
int lang = 44;
CDirIO Dir(sGameDir);
if (Dir.exists(L"t"))
{
if (Dir.exists(L"t/0002.pck")) return L"t/0002.pck";
else if (Dir.exists(L"t/0002.xml")) return L"t/0002.xml";
else if (Dir.exists(Utils::WString::Format(L"t/%d0002.pck", lang))) return Utils::WString::Format(L"t/%d0002.pck", lang);
else if (Dir.exists(Utils::WString::Format(L"t/%d0002.xml", lang))) return Utils::WString::Format(L"t/%d0002.xml", lang);
else if (Dir.exists(Utils::WString::Format(L"t/0002-L%03d.pck", lang))) return Utils::WString::Format(L"t/0002-L%03d.pck", lang);
else if (Dir.exists(Utils::WString::Format(L"t/0002-L%03d.xml", lang))) return Utils::WString::Format(L"t/0002-L%03d.xml", lang);
}
return L"";
}
Utils::WString CGameExe::_readTextFile(const Utils::WString &sGameDir) const
{
Utils::WString textFileName = _textFileName(sGameDir);
if ( !textFileName.empty() )
{
Utils::WString sVersion;
CDirIO Dir(sGameDir);
CFileIO File(Dir.file(textFileName));
size_t fileSize;
unsigned char *fileData = File.readAll(&fileSize);
if ( fileData && fileSize)
{
if (CFileIO(textFileName).isFileExtension(L"pck"))
{
size_t newFileSize;
unsigned char *pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize);
delete fileData;
pckData[newFileSize - 1] = '\0';
Utils::WString Data((char *)pckData);
delete pckData;
return Data;
}
else if (CFileIO(textFileName).isFileExtension(L"xml"))
{
Utils::WString Data((char *)fileData);
delete fileData;
return Data;
}
}
}
return L"";
}
Utils::WString CGameExe::_extractTextData(const Utils::WString &sData, long iPage, long iID, int iGameID) const
{
Utils::WString sID = Utils::WString(L"<t id=\"") + iID + L"\">";
if (iGameID > 0)
{
Utils::WString sPage = Utils::WString(L"<page id=\"") + Utils::WString::Number(iGameID) + iPage + L"\"";
int startpage = sData.findPos(sPage);
if (startpage >= 0) {
int start = sData.findPos(sID, startpage);
if (start >= 0) {
start += sID.length();
int end = sData.findPos(L"</t>", start);
return sData.mid(start, end);
}
}
}
{
Utils::WString sPage = Utils::WString(L"<page id=\"") + iPage + L"\"";
int startpage = sData.findPos(sPage);
if (startpage >= 0) {
int start = sData.findPos(sID, startpage);
if (start >= 0) {
start += sID.length();
int end = sData.findPos(L"</t>", start);
return sData.mid(start, end);
}
}
}
return L"";
}
bool CGameExe::getGameVersionName(const Utils::WString &sGameExe, Utils::WString *versionName) const
{
int gameType = this->getGameType(sGameExe);
if ( gameType == -1 )
return false;
Utils::WString gameExe = this->gameDir(sGameExe) + L"/" + m_lExe[gameType]->sExe;
Utils::WString gameDir = this->properDir(gameExe);
int size = (int)CFileIO(gameExe).GetFilesize();
Utils::WString fVersion;
int version = this->_findVersion(gameType, size, &fVersion);
// not matched version
// lets read the text file
if ( version != -1 ) {
(*versionName) = this->gameVersionFromType(gameType, version, fVersion);
return true;
}
Utils::WString sText = this->_extractTextData(this->_readTextFile(gameDir), 1910, 1216, this->getTextID(gameExe));
Utils::WString sVersion = sText.between(L"Version ", L", ");
if ( sVersion.empty() ) sVersion = sText.between(L"ver=", L"&");
if ( !sVersion.empty() ) {
// lets match the version
(*versionName) = sVersion;
float fVersion = sVersion;
SGameExe *gameExe = m_lExe[gameType];
if ( gameExe )
{
int count = 0;
int lower = -1;
for ( CListNode<SGameExeVersion> *node = gameExe->lVersions.Front(); node; node = node->next() )
{
if ( (float)node->Data()->fVersion == fVersion )
{
(*versionName) = node->Data()->sName;
return true;
}
++count;
}
version = lower;
}
}
return true;
}
int CGameExe::getGameVersion(const Utils::WString &sGameExe, Utils::WString *a_fVersion) const
{
Utils::WString gameExe = sGameExe;
int gameType = this->getGameType(gameExe);
if ( gameType == -1 )
return -1;
Utils::WString gameDir = gameExe;
if ( !m_lExe[gameType]->sAddon.empty() )
gameExe = CDirIO(gameExe).back() + L"/" + m_lExe[gameType]->sExe;
else
gameExe = gameExe + L"/" + m_lExe[gameType]->sExe;
int size = (int)CFileIO(gameExe).fileSize();
int version = this->_findVersion(gameType, size, a_fVersion);
Utils::WString sText = this->_extractTextData(this->_readTextFile(gameDir), 1910, 10000, this->getTextID(gameExe));
Utils::WString sVersion = sText.between(L"ver=", L"&");
if ( !sVersion.empty() )
{
// lets match the version
Utils::WString fVersion = sVersion;
if ( a_fVersion ) *a_fVersion = fVersion;
SGameExe *gameExe = m_lExe[gameType];
if ( gameExe ) {
int count = 0;
int lower = -1;
for ( CListNode<SGameExeVersion> *node = gameExe->lVersions.Front(); node; node = node->next() )
{
if ( fVersion.compareVersion(node->Data()->fVersion) == COMPARE_OLDER )
lower = count;
if ( fVersion.compareVersion(node->Data()->fVersion) == COMPARE_SAME )
return count;
++count;
}
version = lower;
}
}
return version;
}
int CGameExe::convertGameType(int gametype, int *version) const
{
int count = 0, game = 0;
switch ( gametype )
{
case 1:
*version = 0;
return 1;
case 2:
*version = 0;
return 2;
case 3:
*version = 1;
return 2;
case 4:
*version = 0;
return 3;
case 5:
*version = 1;
return 3;
case 6:
*version = 2;
return 3;
}
for ( CListNode<SGameExe> *node = m_lExe.Front(); node; node = node->next() )
{
++count;
++game;
SGameExe *exe = node->Data();
// found the game type, the version is 0, which is any version
if ( count == gametype )
{
*version = 0;
return game;
}
int v = 0;
for ( CListNode<SGameExeVersion> *vNode = exe->lVersions.Front(); vNode; vNode = vNode->next() )
{
++count;
++v;
if ( count == gametype )
{
*version = v;
return game;
}
}
}
// not found ?? just set to all versions
*version = 0;
return 0;
}
SGameExe *CGameExe::game(int game) const
{
if (game >= 0 && game < m_lExe.size())
return m_lExe.Get(game);
return NULL;
}
unsigned int CGameExe::gameCount() const
{
return static_cast<unsigned int>(m_lExe.size());
}