Subversion Repositories spk

Rev

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"&amp");

        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"&amp");

        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());
}