Subversion Repositories spk

Rev

Rev 227 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

//header
#include "SpkFile.h"

// debug logging
#include "Logging/Log.h"

// old style compression (needed to convert old formats)
#include "../../HiP/HiP.h"

#include "TextDB.h"

//TODO: remove this
unsigned char *LineByLineRead ( unsigned char *data, const Utils::String &end, Utils::String *readData )
{
        Utils::String line;
        while ( true )
        {
                data = line.readToEndOfLine(data);

                if ( (*readData).empty() && line.empty() ) continue;
                if ( line == end )
                        break;
                *readData += (line + "\r\n");
        }

        return data;
}


CSpkFile::CSpkFile() : CBaseFile ()
{
        SetDefaults ();
}

CSpkFile::~CSpkFile()
{
        Delete ();
}
/*
        Func:   SetDefaults
        Desc:   Sets the  default values when class is created
*/
void CSpkFile::SetDefaults ()
{
        m_pLastWare = NULL;
        m_iPackageType = PACKAGETYPE_NORMAL;
        m_bForceProfile = false;
        m_iScriptType = SCRIPTTYPE_CUSTOM;

        CBaseFile::SetDefaults ();
}

void CSpkFile::Delete ()
{
        m_lWares.clear(true);
        m_lSettings.clear(true);

        CBaseFile::Delete ();
}


bool CSpkFile::_checkHeader(const Utils::WString &header) const
{
        if ( header.Compare(L"SPKCycrow") )
                return true;
        return false;
}
/*
        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 CSpkFile::parseValueLine(const Utils::WString &line)
{
        Utils::WString first = line.token(L" ", 1);
        Utils::WString rest  = line.tokens(L" ", 2);

        if ( first == L"AnotherMod:" )
        {
                m_sOtherAuthor = rest.token(L"|", 1);
                m_sOtherName = rest.tokens(L"|", 2);
        }
        else if ( line == L"CustomStart" )
                m_iPackageType = PACKAGETYPE_CUSTOMSTART;
        else if ( line == L"PackageUpdate" )
                m_iPackageType = PACKAGETYPE_UPDATE;
        else if ( line == L"Patch" )
                m_iPackageType = PACKAGETYPE_PATCH;
        else if ( line == L"ForceProfile" )
                m_bForceProfile = true;
        else if ( line == L"Signed" )
                m_bSigned = true;
        else if ( first == L"ScriptType:" )
                m_sScriptType = rest;
        else if ( first == L"ScriptTypeNew:" )
                m_iScriptType = rest;
        else if ( first == L"PackageType:" )
                m_iPackageType = rest;
        else if ( first == L"Ware:" )
                addWare(rest);
        else if ( (first == L"WareText:") && (m_pLastWare) )
                addWareText(rest);
        else if ( first == L"Setting:" )
        {
                SSettingType *t = addSetting(rest.token(L"|", 2), rest.token(L"|", 1));
                convertSetting(t, rest.tokens(L"|", 3));
        }
        else
                return CBaseFile::parseValueLine ( line );

        return true;
}

/*
        Func:   CreateValuesLine
        Return: String - returns the full string for values
        Desc:   Creates a single string for all values, this is used when compressing to write to the spk file
*/
Utils::WString CSpkFile::createValuesLine () const
{
        Utils::WString values = CBaseFile::createValuesLine ();
        // combine all values together
        if ( (!m_sOtherAuthor.empty()) && (!m_sOtherName.empty()) )
                values += L"AnotherMod: " + m_sOtherAuthor + L"|" + m_sOtherName + L"\n";
        if ( m_bForceProfile )
                values += L"ForceProfile\n";
        if ( !m_sScriptType.empty() )
                values += L"ScriptType: " + m_sScriptType + L"\n";
        values += Utils::WString(L"PackageType: ") + (long)m_iPackageType + L"\n";
        values += Utils::WString(L"ScriptTypeNew: ") + Utils::WString::Number(m_iScriptType) + L"\n";

        for ( CListNode<SSettingType> *node = m_lSettings.Front(); node; node = node->next() )
                values += Utils::WString(L"Setting: ") + (long)node->Data()->iType + L"|" + node->Data()->sKey + L"|" + getSetting(node->Data()) + L"\n";

        for ( CListNode<SWares> *wNode = m_lWares.Front(); wNode; wNode = wNode->next() ) {
                SWares *ware = wNode->Data();
                if ( wNode->Data()->iTextID > 0 )
                        values += Utils::WString(L"Ware: ") + Utils::WString(ware->cType) + L":" + ware->iPrice + L":" + (long)ware->iSize + L":" + (long)ware->iVolumn + L":" + ware->sID + L":" + (long)ware->iNotority + L":" + (long)ware->iTextID + L"," + (long)ware->iTextPage + L"\n";
                else
                        values += Utils::WString(L"Ware: ") + Utils::WString(ware->cType) + L":" + ware->iPrice + L":" + (long)ware->iSize + ":" + (long)ware->iVolumn + L":" + ware->sID + L":" + (long)ware->iNotority + L"\n";
                for ( CListNode<SWaresText> *wtNode = ware->lText.Front(); wtNode; wtNode = wtNode->next() )
                        values += Utils::WString(L"WareText: ") + (long)wtNode->Data()->iLang + L" " + wtNode->Data()->sName + L"|" + wtNode->Data()->sDesc + L"\n";
        }

        return values;
}

Utils::WString CSpkFile::customScriptType(int lang) const
{
        if ( !m_sScriptType.empty() )
        {
                std::vector<Utils::WString> split;
                if(m_sScriptType.tokenise(L"<br>", split))
                {
                        for (size_t i = 1; i < split.size(); i++)
                        {
                                Utils::WString str = split[i];
                                int num = str.token(L":", 1);
                                Utils::WString name = str.tokens(L":", 2);

                                if ( num == lang )
                                        return name;
                        }

                        return split[0];
                }
        }
        return m_sScriptType;
}

bool CSpkFile::readWares(int iLang, CLinkList<SWareEntry> &list, const Utils::WString &empWares)
{
        if ( CBaseFile::readWares(iLang, list, empWares) ) {
                for(CListNode<SWares> *node = m_lWares.Front(); node; node = node->next()) {

                        SWareEntry *ware = new SWareEntry;

                        for(CListNode<SWaresText> *textNode = node->Data()->lText.Front(); textNode; textNode = textNode->next()) {
                                ware->name = textNode->Data()->sName;
                                ware->description = textNode->Data()->sDesc;
                                if ( textNode->Data()->iLang == iLang ) {
                                        break;
                                }
                        }

                        if ( ware->name.empty() ) {
                                ware->name = _pTextDB->get(iLang, node->Data()->iTextPage, node->Data()->iTextID);
                        }
                        if ( ware->description.empty() ) {
                                ware->description = _pTextDB->get(iLang, node->Data()->iTextPage, node->Data()->iDescID);
                        }

                        ware->id = node->Data()->sID;
                        ware->relval = node->Data()->iPrice;
                        ware->position = node->Data()->iPosID;
                        ware->type = Ware_Custom;
                        ware->notority = node->Data()->iNotority;
                        ware->position = 0;
                        ware->package = this;

                        list.push_back(ware);
                }

                return true;
        }

        return false;
}

bool CSpkFile::writeHeader(CFileIO &file, int valueheader, int valueComprLen) const
{
        return file.write("SPKCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen);
}

void CSpkFile::addWareText(const Utils::WString &sData)
{
        if ( !m_pLastWare )
                return;

        SWaresText *wt = new SWaresText;
        wt->iLang = sData.token(L" ", 1);
        wt->sName = sData.tokens(L" ", 2).token(L"|", 1);
        wt->sDesc = sData.tokens(L" ", 2).tokens(L"|", 2);
        m_pLastWare->lText.push_back ( wt );

        _changed();
}

void CSpkFile::addWare(const Utils::WString &sData)
{
        SWares *ware = new SWares;
        ware->iTextID = -1;
        ware->iTextPage = 0;
        ware->cType = sData.token(L":", 1)[0];
        ware->iPrice = sData.token(L":", 2);
        ware->iSize = sData.token(L":", 3);
        ware->iVolumn = sData.token(L":", 4);
        ware->sID = sData.token(L":", 5);
        ware->iNotority = sData.token(L":", 6);
        if ( !sData.token(L":", 7).empty() )
        {
                Utils::WString r = sData.token(L":", 7);
                ware->iTextID = r.token(L",", 1);
                ware->iTextPage = r.token(L",", 2);
        }
        m_lWares.push_back ( ware );
        m_pLastWare = ware;

        _changed();
}

bool CSpkFile::CheckValidReadmes () const
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != FILETYPE_README )
                        continue;
                if ( !file->CheckValidFilePointer() )
                        continue;
                return true;
        }

        return false;
}

void CSpkFile::clearWares()
{
        for ( CListNode<SWares> *node = m_lWares.Front(); node; node = node->next() ) {
                node->Data()->lText.clear(true);
                node->DeleteData();
        }
        _changed();
        m_lWares.clear(true);
}

SWares *CSpkFile::findWare(const Utils::WString &id) const
{
        for ( CListNode<SWares> *node = m_lWares.Front(); node; node = node->next() ) {
                SWares *w = node->Data();
                if ( w->sID.Compare(id) ) {
                        return w;
                }
        }
        return NULL;
}

void CSpkFile::removeWare(const Utils::WString &id )
{
        for ( CListNode<SWares> *node = m_lWares.Front(); node; node = node->next() ) {
                SWares *w = node->Data();
                if ( w->sID.Compare(id) ) {
                        m_lWares.RemoveCurrent ();
                        delete w;
                        _changed();
                        return;
                }
        }
}

void CSpkFile::addWare(SWares *ware)
{
        ware->sID.remove(' ');

        SWares *newware = this->findWare(ware->sID);
        if ( newware ) {
                m_lWares.remove(newware);
        }

        m_lWares.push_back(ware);
        _changed();
}

void CSpkFile::addWareText(SWares *pWare, int iLang, const Utils::WString &sName, const Utils::WString &sDesc)
{
        SWaresText *wt;
        for ( CListNode<SWaresText> *node = pWare->lText.Front(); node; node = node->next() ) {
                wt = node->Data();
                if ( wt->iLang == iLang ) {
                        wt->sDesc = sDesc;
                        wt->sName = sName;
                        return;
                }
        }

        wt = new SWaresText;
        wt->iLang = iLang;
        wt->sName = sName;
        wt->sDesc = sDesc;

        pWare->lText.push_back(wt);
        _changed();
}


void CSpkFile::clearWareText(const Utils::WString &id)
{
        SWares *w = findWare(id);
        clearWareText(w);
}

void CSpkFile::clearWareText(SWares *w)
{
        if ( !w ) return;

        w->lText.clear(true);
        _changed();
}

void CSpkFile::removeWareText(const Utils::WString &wid, int lang)
{
        SWares *w = findWare(wid);
        if ( w )
        {
                for ( CListNode<SWaresText> *node = w->lText.Front(); node; node = node->next() ) {
                        SWaresText *wt = node->Data();
                        if ( wt->iLang == lang ) {
                                w->lText.RemoveCurrent();
                                _changed();
                                delete wt;
                                break;
                        }
                }
        }
}

int CSpkFile::CheckValidCustomStart () const
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != FILETYPE_SCRIPT )
                        continue;

                Utils::WString basename = file->baseName();
                if ( basename.right(15).Compare(L".initplayership") )
                        return 0;
        }
        if ( !IsAnotherMod() )
                return 1;
        else
                return 2;
}

bool CSpkFile::computeSigned(bool updateFiles) const
{
        // check for any custom wares
        // patch mods and custom starts are also not signed
        if ((!m_lWares.empty()) || (this->IsPatch()) || (this->IsCustomStart()))
                return false;

        return CBaseFile::computeSigned(updateFiles);
}

SSettingType *CSpkFile::addSetting(const Utils::WString &key, int type)
{
        Utils::WString sKey = key.remove('|');

        SSettingType *t;
        for ( t = m_lSettings.First(); t; t = m_lSettings.Next() )
        {
                if ( t->sKey.Compare(sKey) )
                        return NULL;
        }

        switch ( type )
        {
                case SETTING_STRING:
                        t = new SSettingString;
                        break;
                case SETTING_INTEGER:
                        t = new SSettingInteger;
                        break;
                case SETTING_CHECK:
                        t = new SSettingCheck;
                        break;
        }

        if ( !t )
                return NULL;

        t->sKey = sKey;
        t->iType = type;

        m_lSettings.push_back ( t );
        _changed();

        return t;
}

void CSpkFile::convertSetting(SSettingType *t, const Utils::WString &set) const
{
        if ( !t )
                return;

        switch ( t->iType )
        {
                case SETTING_STRING:
                        ((SSettingString *)t)->sValue = set;
                        break;
                case SETTING_INTEGER:
                        ((SSettingInteger *)t)->iValue = set;
                        break;
                case SETTING_CHECK:
                        ((SSettingCheck *)t)->bValue = set;
                        break;
        }
}
Utils::WString CSpkFile::getSetting(SSettingType *t) const
{
        if ( !t )
                return L"";

        switch ( t->iType )
        {
                case SETTING_STRING:
                        return ((SSettingString *)t)->sValue;
                case SETTING_INTEGER:
                        return Utils::WString::Number(((SSettingInteger *)t)->iValue);
                case SETTING_CHECK:
                        return (((SSettingInteger *)t)->iValue) ? L"1" : L"0";
        }

        return L"";
}

void CSpkFile::clearSettings ()
{
        m_lSettings.clear(true);
        _changed();
}


bool CSpkFile::isMatchingMod(const Utils::WString &mod) const
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *file = node->Data();
                if ( file->GetFileType() != FILETYPE_MOD )
                        continue;

                if ( file->IsFakePatch() )
                        continue;

                Utils::WString filename = file->baseName();
                if ( filename.Compare(mod) )
                        return true;
        }
        return false;
}

Utils::WString CSpkFile::scriptTypeString(int lang) const
{
        int iType = m_iScriptType;

        if ( this->IsLibrary() )
                return L"Library";
        else if ( this->IsPackageUpdate() )
                return L"Package Update";
        else if ( this->IsCustomStart() )
                return L"Custom Start";
        else if ( this->IsPatch() )
                return L"Patch";
        else if ( m_iScriptType == SCRIPTTYPE_CUSTOM )
        {
                Utils::WString type = this->customScriptType(lang);
                if ( !type.empty() )
                        return type;
                iType = -1;
        }

        if (iType == -1)  // no script type
        {
                if ( this->IsFakePatch() )
                        return L"Fake Patch";
        }

        Utils::WString sType = CSpkFile::GetScriptTypeStringStatic(m_iScriptType);

        if ( sType.empty() )
                return L"Other";

        return sType;
}

int CSpkFile::ConvertScriptType(const Utils::WString &sType)
{
        for ( int i = 0; i < SCRIPTTYPE_MAX; i++ )
        {
                if ( sType.Compare(CSpkFile::GetScriptTypeStringStatic(i)) )
                        return i;
        }

        return -1;
}

Utils::WString CSpkFile::GetScriptTypeStringStatic(int type)
{
        switch ( type )
        {
                case SCRIPTTYPE_CUSTOM:
                        return L"Custom";
                case SCRIPTTYPE_NAVIGATION:
                        return L"Navigation";
                case SCRIPTTYPE_COMBAT:
                        return L"Combat";
                case SCRIPTTYPE_MISSION:
                        return L"Mission";
                case SCRIPTTYPE_ALPLUGIN:
                        return L"AL Plugin";
                case SCRIPTTYPE_HOTKEY:
                        return L"Hotkey";
                case SCRIPTTYPE_SHIPUPGRADE:
                        return L"Ship Upgrade";
                case SCRIPTTYPE_SHIPCOMMAND:
                        return L"Ship Command";
                case SCRIPTTYPE_STATIONCOMMAND:
                        return L"Station Command";
                case SCRIPTTYPE_FLEET:
                        return L"Fleet Management";
                case SCRIPTTYPE_TRADE:
                        return L"Trade";
                case SCRIPTTYPE_PIRACY:
                        return L"Piracy";
                case SCRIPTTYPE_CHEAT:
                        return L"Cheat";
                case SCRIPTTYPE_EXTENSION:
                        return L"Extension Mods";
                case SCRIPTTYPE_REBALANCE:
                        return L"Rebalance";
                case SCRIPTTYPE_FIX:
                        return L"Vanilla Fix";
                case SCRIPTTYPE_GENERALMOD:
                        return L"General Mod";
                case SCRIPTTYPE_TOTAL:
                        return L"Totel Conversion";
                case SCRIPTTYPE_WINGCOMMAND:
                        return L"Wing Command";
        }

        return L"Other";
}

bool CSpkFile::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"ScriptType") )
        {
                if ( sRest.Compare(L"Library") || sRest.Compare(L"Library Script") )
                        this->SetLibrary();
                else if ( sRest.Compare(L"Update") || sRest.Compare(L"Package Update") || sRest.Compare(L"Mod Update") )
                        this->SetPackageUpdate();
                else if ( sRest.Compare(L"Start") || sRest.Compare(L"Custom Start") )
                        this->SetCustomStart();
                else if ( sRest.Compare(L"Patch") || sRest.Compare(L"Patch Mod") )
                        this->SetPatch();
                else
                {
                        int check = sRest;
                        if ( check || sRest == L"0" )
                                m_iScriptType = check;
                        else
                        {
                                m_iScriptType = CSpkFile::ConvertScriptType(sRest);
                                if (m_iScriptType == -1)
                                {
                                        m_sScriptType = sRest;
                                        m_iScriptType = SCRIPTTYPE_CUSTOM;
                                }
                        }
                }
        }
        else if (sFirst.Compare(L"AnotherMod"))
        {
                m_sOtherName = sRest.token(L"|", 1);
                m_sOtherAuthor = sRest.tokens(L"|", 2);
        }
        else if ( sFirst.Compare(L"WareName") || sFirst.Compare(L"WareDesc") )
        {
                // find the ware to use
                SWares *useWare = m_pLastWare;
                Utils::WString id = sRest.token(L" ", 1);
                for ( CListNode<SWares> *wNode = m_lWares.Front(); wNode; wNode = wNode->next() )
                {
                        if ( wNode->Data()->sID.Compare(id.c_str()) )
                        {
                                useWare = wNode->Data();
                                break;
                        }
                }

                // check if we have the id already
                if ( useWare )
                {
                        int lang = sRest.token(L" ", 2);
                        SWaresText *wt = NULL;
                        for ( CListNode<SWaresText> *tNode = useWare->lText.Front(); tNode; tNode = tNode->next() )
                        {
                                if ( tNode->Data()->iLang == lang )
                                {
                                        wt = tNode->Data();
                                        break;
                                }
                        }

                        if ( !wt )
                        {
                                wt = new SWaresText;
                                wt->iLang = lang;
                                useWare->lText.push_back(wt);
                        }

                        if ( sFirst.Compare(L"WareName") )
                                wt->sName = sRest.tokens(L" ", 3);
                        else
                                wt->sDesc = sRest.tokens(L" ", 3);
                }
        }
        else if ( sFirst.left(4).Compare(L"Ware") )
        {
                SWares *ware = new SWares;
                ware->iTextID = -1;
                ware->iTextPage = 0;
                ware->cType = sFirst[4];
                ware->iPrice = sRest.token(L" ", 2);
                ware->iSize = sRest.token(L" ", 3);
                ware->iVolumn = sRest.token(L" ", 4);
                ware->sID = sRest.token(L" ", 1);
                ware->iNotority = sRest.token(L" ", 5);
                if ( !sRest.token(L" ", 6).empty() )
                {
                        ware->iTextID = sRest.token(L" ", 6).token(L",", 2);
                        ware->iTextPage = sRest.token(L" ", 6).token(L",", 1);
                }
                m_lWares.push_back ( ware );
                m_pLastWare = ware;
        }

        else if ( CBaseFile::loadPackageData(sFirst, sRest, sMainGame, otherGames, gameAddons, progress) )
                return true;
        else
                return false;

        return true;
}

bool CSpkFile::GeneratePackagerScript(bool wildcard, Utils::WStringList *list, int game, const Utils::WStringList &gameAddons, bool datafile)
{
        if ( !CBaseFile::GeneratePackagerScript(wildcard, list, game, gameAddons, datafile) )
                return false;

        list->pushBack(L"# File Type, Script or Ship");
        list->pushBack(L"FileType: Script");
        list->pushBack(L"");

        list->pushBack(L"# Script Type, the type of package file, some are special types, others are just for show");
        if ( this->IsLibrary() )
                list->pushBack(L"ScriptType: Library");
        else if ( this->IsPackageUpdate() )
                list->pushBack(L"ScriptType: Package Update");
        else if ( this->IsCustomStart() )
                list->pushBack(L"ScriptType: Custom Start");
        else if ( this->IsPatch() )
                list->pushBack(L"ScriptType: Patch");
        else
                list->pushBack(L"ScriptType: " + this->scriptTypeString(44));
        list->pushBack(L"");

        if ( this->IsAnotherMod() )
        {
                list->pushBack(L"# For another mod/package, this is a child package");
                list->pushBack(L"AnotherMod: " + m_sOtherName + L"|" + m_sOtherAuthor);
                list->pushBack(L"");
        }

        if ( !m_lWares.empty() )
        {
                list->pushBack(L"# Custom Wares, Ware<type>: <id> <price> <size> <volumn> <notority>");
                for ( CListNode<SWares> *node = m_lWares.Front(); node; node = node->next() )
                {
                        SWares *w = node->Data();
                        if ( w->iTextID > 0 )
                                list->pushBack(Utils::WString(L"Ware") + Utils::WString(w->cType) + L": " + w->sID + L" " + (long)w->iPrice + L" " + (long)w->iSize + L" " + (long)w->iVolumn + L" " + (long)w->iNotority + L" " + (long)w->iTextPage + L"," + (long)w->iTextID);
                        else
                                list->pushBack(Utils::WString(L"Ware") + Utils::WString(w->cType) + L": " + w->sID + L" " + (long)w->iPrice + L" " + (long)w->iSize + L" " + (long)w->iVolumn + L" " + (long)w->iNotority);
                        for ( CListNode<SWaresText> *wNode = w->lText.Front(); wNode; wNode = wNode->next() )
                        {
                                SWaresText *wt = wNode->Data();
                                if ( !wt->sName.empty() )
                                        list->pushBack(L"WareName: " + w->sID + L" " + (long)wt->iLang + L" " + wt->sName);
                                if ( !wt->sDesc.empty() )
                                        list->pushBack(L"WareDesc: " + w->sID + L" " + (long)wt->iLang + L" " + wt->sDesc);
                        }
                        list->pushBack(L"");
                }
        }

        if ( !datafile )
        {
                if ( !CBaseFile::GeneratePackagerScriptFile(wildcard, list, game, gameAddons) )
                        return false;
        }

        return true;

}

Utils::WString CSpkFile::GetWareText(SWares *w, int lang)
{
        // return the text page if being used
        if ( w->iTextID > 0 && w->iTextPage > 0 )
                return Utils::WString(L"{") + (long)w->iTextPage + L"," + (long)w->iTextID + L"}";

        Utils::WString name;
        for ( CListNode<SWaresText> *wt = w->lText.Front(); wt; wt = wt->next() )
        {
                if ( wt->Data()->sName.empty() )
                        continue;
                if ( wt->Data()->iLang == lang )
                        name = wt->Data()->sName;
                else if ( name.empty() && wt->Data()->iLang == 44 )
                        name = wt->Data()->sName;
                else if ( name.empty() )
                        name = wt->Data()->sName;
        }

        return name;
}
Utils::WString CSpkFile::GetWareDesc(SWares *w, int lang)
{
        // return the text page if being used
        if ( w->iTextID > 0 && w->iTextPage > 0 )
                return Utils::WString(L"{") + (long)w->iTextPage + L"," + ((long)w->iTextID + 1) + L"}";

        Utils::WString name;
        for ( CListNode<SWaresText> *wt = w->lText.Front(); wt; wt = wt->next() )
        {
                if ( wt->Data()->sDesc.empty() )
                        continue;

                if ( wt->Data()->iLang == lang )
                        name = wt->Data()->sDesc;
                else if ( name.empty() && wt->Data()->iLang == 44 )
                        name = wt->Data()->sDesc;
                else if ( name.empty() )
                        name = wt->Data()->sDesc;
        }

        return name;
}

Utils::WString CSpkFile::customStartName() const
{
        if ( !this->IsCustomStart() )
                return L"";

        // else find the custom start script
        C_File *file = NULL;
        for ( file = this->GetFirstFile(FILETYPE_SCRIPT); file; file = this->GetNextFile(file) )
        {
                if ( file->filename().contains(L"initplayership") && file->filename().token(L".", 1).Compare(L"galaxy") )
                        break;
        }

        if ( !file )
                return L"";

        return file->filename().token(L".", 2);
}

void CSpkFile::MergePackage(CBaseFile *base)
{
        // update possible changes
        if ( base->GetType() == TYPE_SPK )
        {
                CSpkFile *spk = (CSpkFile *)base;
                m_bForceProfile = spk->IsForceProfile();

                // add any new wares
                if ( spk->anyWares() )
                {
                        for ( CListNode<SWares> *node = spk->GetWaresList()->Front(); node; node = node->next() )
                                this->addWare(node->Data());
                        spk->GetWaresList()->clear(); // remove wares so they aren't deleted
                }

                // add any settings
                if ( spk->anySettings() )
                {
                        for ( CListNode<SSettingType> *node = spk->settingsList()->Front(); node; node = node->next() )
                                this->addSetting(node->Data()->sKey, node->Data()->iType);
                }

                if ( !spk->otherName().empty() )
                {
                        m_sOtherName = spk->otherName();
                        m_sOtherAuthor = spk->otherAuthor();
                }
        }

        // copy settings from base class
        _merge(base);

        for ( CListNode<SGameCompat> *gNode = base->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
                if ( !gNode->Data()->sVersion.empty() )
                        this->AddGameCompatability(gNode->Data()->iGame, gNode->Data()->sVersion);
                else
                        this->AddGameCompatability(gNode->Data()->iGame, (long)gNode->Data()->iVersion);
        }

        // copy over needed librarys
        for ( CListNode<SNeededLibrary> *lNode = base->GetNeededLibraries()->Front(); lNode; lNode = lNode->next() )
                this->addNeededLibrary(lNode->Data()->sName, lNode->Data()->sAuthor, lNode->Data()->sMinVersion);

        // web mirror address, add any new ones
        for(auto itr = base->webMirrors().begin(); itr != base->webMirrors().end(); itr++)
                this->addWebMirror((*itr)->str);

        // copy over package names
        for(auto itr = base->namesList()->begin(); itr != base->namesList()->end(); itr++)
                this->addName((*itr)->iLanguage, (*itr)->sName);

        // finally do all the files
        for ( CListNode<C_File> *node = base->fileList().Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                // if it exists, remove the old
                for ( CListNode<C_File> *thisNode = m_lFiles.Front(); thisNode; thisNode = thisNode->next() )
                {
                        if ( thisNode->Data()->GetFileType() == f->GetFileType() && thisNode->Data()->filename().Compare(f->filename()) && thisNode->Data()->dir().Compare(f->dir()) )
                        {
                                m_lFiles.remove(thisNode);
                                break;
                        }
                }

                m_lFiles.push_back(f);
        }

        // clear the files so we dont delete them later
        base->GetFileList()->clear();
}

unsigned char *CSpkFile::_convert_uncompressFile(const Utils::WString &sOldFilename, int *pLen)
{
        // firstcheck if the file exists
        FILE *id = _wfopen(sOldFilename.c_str(), L"rb" );
        if ( !id ) {
                CLog::logf(CLog::Log_IO, 1, L"Unable to open file: %s", sOldFilename.c_str());
                return false;
        }

        // read the first 3 charaters to check if its using the original "HiP" compression
        Utils::String check((char)fgetc(id));
        check += (char)fgetc ( id );
        check += (char)fgetc ( id );

        Utils::String removeFile;

        unsigned char *uncomprData = NULL;
        unsigned char *data = NULL;
        long len = 0, newlen = 0;

        if ( check == "HiP" ) {
                fclose ( id );
                bool opened = false;
                if ( DecompressFile ( (char *)sOldFilename.c_str(), "uncompr.tmp" ) ) {
                        removeFile = "uncompr.tmp";
                        id = fopen ( "uncompr.tmp", "r" );
                        if ( id )
                                opened = true;
                }

                if ( !opened ) {
                        CLog::log(CLog::Log_IO, 1, L"Unable to uncompress file, exiting...");
                        return false;
                }

                CLog::log(CLog::Log_IO, 1, L"* Reading file into memory...");
                // get file length
                fseek ( id, 0, SEEK_END );
                len = ftell ( id );
                
                // move back to beginning
                fseek ( id, 0, SEEK_SET );

                // read the data from file into memory
                uncomprData = new unsigned char[len + 1];
                fread ( uncomprData, sizeof(unsigned char), len, id );

                newlen = len;
        }
        else
        {
                CLog::log(CLog::Log_IO, 1, L"* Reading file into memory...");
                // get file length
                fseek ( id, 0, SEEK_END );
                len = ftell ( id );
                
                // move back to beginning
                fseek ( id, 0, SEEK_SET );

                // read the data from file into memory
                data = new unsigned char[len + 1];
                fread ( data, sizeof(unsigned char), len, id );

                // uncompress the file (currently only 7zip compression)
                CLog::log(CLog::Log_IO, 1, L"* Uncompressing file...");
                newlen = len;
        #ifdef _INCLUDE7ZIP
                uncomprData = LZMADecodeData ( data, len, newlen, progress );
        #else
                uncomprData = LZMADecode_C ( (unsigned char *)data, len, (size_t*)&newlen, NULL );
        #endif
        }

        *pLen = newlen;

        if ( !removeFile.empty() ) {
                CFileIO::Remove(removeFile);
        }

        return uncomprData;
}

Utils::WString CSpkFile::_convert_fileEndString(const Utils::WString &sFile)
{
        if ( sFile.Compare(L"Text") )
                return L"-- End of Script --";
        else if ( sFile.Compare(L"Uninstall") )
                return L"-- End of Uninstall --";
        else if ( sFile.Compare(L"Readme") )
                return L"-- End of Readme --";
        else if ( sFile.Compare(L"Map") )
                return L"-- End of Map --";
        else if ( sFile.Compare(L"Mod") || sFile.Compare(L"Extra") || sFile.Compare(L"Screen") || sFile.Compare(L"Sound") )
                return L"";
        return L"-- End of Script --";
}

FileType CSpkFile::_convert_fileType(const Utils::WString &sFile)
{
        if ( sFile.Compare(L"Text") )
                return FILETYPE_TEXT;
        else if ( sFile.Compare(L"Uninstall") )
                return FILETYPE_UNINSTALL;
        else if ( sFile.Compare(L"Readme") )
                return FILETYPE_README;
        else if ( sFile.Compare(L"Map") )
                return FILETYPE_MAP;
        else if ( sFile.Compare(L"Mod") )
                return FILETYPE_MOD;
        else if ( sFile.Compare(L"Extra") )
                return FILETYPE_EXTRA;
        else if ( sFile.Compare(L"Screen") )
                return FILETYPE_SCREEN;
        else if ( sFile.Compare(L"Sound") )
                return FILETYPE_SOUND;

        return FILETYPE_SCRIPT;
}

void CSpkFile::_convert_parse(const Utils::WString &sCmd, const Utils::WString &sRest)
{
        if ( sCmd == L"Name:" )
        {
                this->setName(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Name: %s", sRest.c_str() );
        }
        else if ( sCmd == L"Author:" )
        {
                this->setAuthor(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Author: %s", sRest.c_str() );
        }
        else if ( sCmd == L"CustomStart" )
        {
                this->SetCustomStart();
                CLog::logf(CLog::Log_EditPackage, 3, L"\tPackage is a custom start!!" );
        }
        else if ( sCmd == L"AnotherMod:" )
        {
                this->setAnotherMod(sRest.token(L"|", 1), sRest.tokens(L"|", 2));
                CLog::logf(CLog::Log_EditPackage, 3, L"\tFor another Mod, Name: %s, Author: %s", this->otherName().c_str(), this->otherAuthor().c_str() );
        }
        else if ( sCmd == L"PATCH" )
        {
                this->SetPatch();
                CLog::logf(CLog::Log_EditPackage, 3, L"\tPackage is a Patch Mod!!" );
        }
        else if ( sCmd == L"Version:" )
        {
                this->setVersion(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Version: %s", sRest.c_str() );
        }
        else if ( sCmd == L"Date:" )
        {
                this->setCreationDate ( sRest );
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Creation Date: %s", sRest.c_str() );
        }
        else if ( sCmd == L"Desc:" ) {
                this->setDescription(sRest.findReplace(L"<br>", L"\n") );
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Description: %s", this->description().c_str() );
        }
        else if ( sCmd == L"WebAddress:" ) {
                this->setWebAddress(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tWeb Address: %s", sRest.c_str() );
        }
        else if ( sCmd == L"WebMirror1:" )
        {
                this->addWebMirror(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tWeb Mirror Address: %s", sRest.c_str() );
        }
        else if ( sCmd == L"WebMirror2:" )
        {
                this->addWebMirror(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tWeb Mirror Address: %s", sRest.c_str() );
        }
        
        else if ( sCmd == L"ScriptType:" )
                this->setScriptType (sRest);
        else if ( sCmd == L"WebSite:" ) {
                this->setWebSite ( sRest );
                CLog::logf(CLog::Log_EditPackage, 3, L"\tWeb Site: %s", sRest.c_str() );
        }
        else if ( sCmd == L"Email:" ) {
                this->setEmail(sRest);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tAuthor Email Address: %s", sRest.c_str() );
        }
        else if ( sCmd == L"GameVersion:" )
        {
                //TODO: fix this for new game version
                /*
                int version = sRest.ToInt();
                if ( version == 0 )
                        this->SetGameVersion ( 1 );
                else if (version == 1 )
                        this->SetGameVersion ( 0 );
                else
                        this->SetGameVersion ( version );
                CLog::logf(CLog::Log_EditPackage, "\tGame Version: %d", this->GetGameVersion () );
                */
        }
        
        else if ( sCmd == L"Ware:" )
        {
                this->addWare ( sRest );
                CLog::logf(CLog::Log_EditPackage, 3, L"\tAdding Custom Ware" );
        }
        else if ( sCmd == L"WareText:" )
                this->addWareText ( sRest );
        else if ( sCmd == L"UninstallAfter:" )  this->addUninstallText(sRest.token(L" ", 1).toLong(), false, sRest.tokens(L" ", 2));
        else if ( sCmd == L"UninstallBefore:" ) this->addUninstallText(sRest.token(L" ", 1).toLong(), true, sRest.tokens(L" ", 2));
        else if ( sCmd == L"InstallAfter:" )    this->addInstallText(sRest.token(L" ", 1).toLong(), false, sRest.tokens(L" ", 2));
        else if ( sCmd == L"InstallBefore:" )   this->addInstallText(sRest.token(L" ", 1).toLong(), true, sRest.tokens(L" ", 2));
        else if ( sCmd == L"ScriptName:" )
        {
                Utils::WString lang = sRest.token(L":", 1);
                Utils::WString name = sRest.tokens(L":", 2);
                this->addName(lang.toLong(), name);
                CLog::logf(CLog::Log_EditPackage, 3, L"\tScript Name Language (%s) %s", lang.c_str(), name.c_str() );
        }
}

Utils::WString CSpkFile::_convert_parseFilename(const Utils::WString &sRest, float fVersion, Utils::WString *pDir)
{
        Utils::WString sFilename;

        if ( fVersion >= 3.00f )
                sFilename = sRest.tokens(L" ", 3);
        else if ( fVersion >= 2.00f )
                sFilename = sRest.tokens(L" ", 2);
        else
                sFilename = sRest;

        if ( sFilename.contains(L"<br>") ) {
                sFilename = sFilename.findReplace(L"<br>", L"|");
                if ( sFilename[0] == L'|' ) {
                        sFilename = sFilename.token(L"|", 1);
                }
                else {
                        *pDir = sFilename.token(L"|", 1);
                        sFilename = sFilename.tokens(L"|", 2);
                }
        }

        return sFilename;
}

unsigned char *CSpkFile::_convert_parseFile(const Utils::WString &sCmd, const Utils::WString &sRest, float fVersion, unsigned char *d)
{
        bool bShared = (sCmd.left(9) == L"$$$Shared") ? true : false;
        Utils::WString sFile = sCmd.right(-3).left(-1);
        Utils::WString sEnd = this->_convert_fileEndString(sFile);
        FileType iType = this->_convert_fileType(sFile);

        // convert the filename and directory
        Utils::WString dir, filename = _convert_parseFilename(sRest, fVersion, &dir);

        // get the size and time
        long time = 0, size = 0;
        if ( fVersion >= 2.00f ) time = sRest.token(L" ", 1).toLong();
        if ( fVersion >= 3.00f ) size = sRest.token(L" ", 2).toLong();
        bool binaryRead = (CFileIO(filename).isFileExtension(L"PCK")) ? true : false;
        if ( sEnd.empty() ) binaryRead = true;
        
        C_File *file = new C_File ();

        if ( bShared )  CLog::logf(CLog::Log_File, 2, L"\tFound %s File (Shared): %s, Reading...", sFile.c_str(), filename.c_str() );
        else                    CLog::logf(CLog::Log_File, 2, L"\tFound %s File: %s, Reading...", sFile.c_str(), filename.c_str() );

        // read the data
        if ( binaryRead )
        {
                file->ReadFromData ( (char *)d, size );
                d += size;
        }
        else
        {
                Utils::String readData;
                d = LineByLineRead ( d, sEnd.toString(), &readData);
                file->ReadFromData ( (char *)readData.c_str(), (long)readData.length() );
        }

        // setup the file
        file->setName ( filename );
        file->setFileType(iType);
        file->SetShared ( bShared );
        file->SetCreationTime ( time );
        if ( !dir.empty() )
                file->setDir ( dir );
        
        this->AddFile ( file );

        CLog::logf(CLog::Log_File, 3, L"Size: %s", file->dataSizeString().c_str() );

        return d;
}

CSpkFile *CSpkFile::convertFromOld(const Utils::WString &sOldFilename)
{
        // check if the old file is actually in an old format
        int ret = CBaseFile::CheckFile ( sOldFilename );
        if ( ret != SPKFILE_INVALID && ret != SPKFILE_OLD ) {
                return NULL;
        }

        CSpkFile *pSpkFile = new CSpkFile();
        if ( !pSpkFile->convertOld(sOldFilename) ) {
                delete pSpkFile;
                return NULL;
        }

        return pSpkFile;
}

bool CSpkFile::convertOld(const Utils::WString &sOldFilename)
{
        // check if the old file is actually in an old format
        int ret = CBaseFile::CheckFile ( sOldFilename );
        if ( ret != SPKFILE_INVALID && ret != SPKFILE_OLD ) {
                return false;
        }

        //uncomress the data
        int len;
        unsigned char *uncomprData = this->_convert_uncompressFile(sOldFilename, &len);

        // uncomressed failed
        if ( !uncomprData ) {
                CLog::log(CLog::Log_IO, 1, L"Error: Unable to uncompress the file");
                return false;
        }

        // now we can read the data
        unsigned char *d = uncomprData;
        Utils::String str;

//      CMultiSpkFile *mspk = NULL;
        //SMultiSpkFile *cur_mspk = NULL;

        int numscripts = 0, curscript = 0;
        float fVersion = 1;

        CLog::log(CLog::Log_IO, 1, L"* Reading spk data...");
        while ( d )
        {
                // read the next line
                d = str.readToEndOfLine(d);
                if ( !d || d[0] == 0 ) {
                        break;
                }
                if ( str.empty() ) {
                        continue;
                }

                Utils::String sCmd = str.token(" ", 1);

                //TODO: split this into CMultiSpkFile
                /*
                if ( first == "MultiPackage:" )
                        mspk = new CMultiSpkFile;
                else if ( (first == "SelectScript:") && (mspk) )
                {
                        mspk->AddFileEntry ( rest.GetToken ( 2, -1, ' ' ) + ".spk" );
                        ++numscripts;
                }
                else if ( (str == "AllowSelection") && (mspk) )
                        mspk->SetSelection ( true );
                else if ( str == "-- Start New Script --" )
                {
                        if ( !mspk )
                        {
                                printf ( "Invalid file format, seems to be multi package file but isn't\n" );
                                CLEANUP
                                exit ( 0 );
                        }
                        cur_mspk = mspk->GetFileList()->Get ( curscript );
                        ++curscript;
                        cur_mspk->pFile = new CSpkFile;
                        spkfile = (CSpkFile *)cur_mspk->pFile;
                }
                */
                Utils::String sRest = str.tokens(" ", 2);
                if ( sCmd == "Packager:" ) {
                        fVersion = sRest;
                        CLog::logf(CLog::Log_Read, 3, L"\tPackager Version: %.2f", fVersion );
                }
                else if ( sCmd == "Icon:" )
                {
                        long size = sRest.token(" ", 1);
                        Utils::String ext = sRest.token(" ", 2);
                        
                        C_File *file = new C_File ();
                        file->ReadFromData ( (char *)d, size );

                        d += size;

                        this->setIcon(file, ext);

                        CLog::logf(CLog::Log_File, 3, L"\tIcon (%s) Size: %s", ext.c_str(), file->dataSizeString ().c_str() );
                }
                else if ( sCmd.left(3) == "$$$" )
                        d = _convert_parseFile(sCmd, sRest, fVersion, d);
                else {
                        this->_convert_parse(sCmd, sRest);
                }
        }

        CLog::logf(CLog::Log_IO, 1, L"* Reading spk data..." );

        return true;
}