Subversion Repositories spk

Rev

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

#include "XspFile.h"
#include "File_IO.h"
#include "Packages.h"
#include "CatFile.h"

#define ADDSECTIONLIST(T, L) for(CListNode<T> *node = L.Front(); node; node = node->next()) \
                _addSection(list, node->Data()->sSection, node->Data()->sData);

#define GENERATESECTION(F, T, L) if ( !L.empty() ) {                                            \
                                                                        type = F;                                                               \
                                                                        Utils::CList<STypesSection> list;               \
                                                                        ADDSECTIONLIST(T, L);                                   \
                                                                        data = CXspFile::TypesListToString(list, true); \
                                                                }

#define GENERATESTRINGSECTION(F, L, FIRST) if ( !L.Empty() ) {                          \
                                                                        type = F;                                                               \
                                                                        Utils::CList<STypesSection> list;               \
                                                                        _addDataSection(L, list, FIRST);                \
                                                                        data = CXspFile::TypesListToString(list, true); \
                                                                }


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

void CXspFile::Delete ()
{
        CBaseFile::Delete();
        m_pSceneFile = m_pCockpitFile = NULL;

        // delete lists
        m_lMissileMasks.MemoryClear();
        m_lWeaponMasks.MemoryClear();

        m_lMissileMasks.destroy();
        m_lWeaponMasks.destroy();

        for ( CListNode<SCockpit> *n = m_lCockpit.Front(); n; n = n->next() )
                n->Data()->lWeaponMask.MemoryClear();
        m_lCockpit.MemoryClear();
        m_lCockpit.destroy();
        m_lCutData.Clear();
        m_lBodies.Clear();
}

void CXspFile::SetDefaults ()
{
        CBaseFile::SetDefaults ();

        m_pSceneFile = m_pCockpitFile = NULL;
        m_bLanguageText = m_bExistingShip = false;
        m_iOrgDesc = 0;

        m_iShipyard = SHIPYARD_NONE;
}

void CXspFile::AddText ( int id, const Utils::String &name, const Utils::String &desc )
{
        // first check if theres an existing id
        SText *newtext = NULL;
        for ( SText *t = m_lText.First(); t; t = m_lText.Next() )
        {
                if ( t->iId == id )
                {
                        newtext = t;
                        break;
                }
        }

        if ( !newtext )
        {
                newtext = new SText;
                newtext->iId = id;
                m_lText.push_back ( newtext );
        }

        newtext->sName = name;
        newtext->sDesc = desc;

        _changed();
}

void CXspFile::RemoveText(int id)
{
        for ( SText *t = m_lText.First(); t; t = m_lText.Next() )
        {
                if ( t->iId == id )
                {
                        _changed();
                        m_lText.RemoveCurrent();
                        return;
                }
        }
}

void CXspFile::AddDummy ( const Utils::String &section, const Utils::String &data )
{
        SDummy *d = new SDummy;
        d->sData = data;
        d->sSection = section;

        if ( d->sData.right(1) != ";" )
                d->sData += ";";

        m_lDummy.push_back ( d );

        _changed();
}

void CXspFile::AddComponent ( const Utils::String &section, const Utils::String &section2, const Utils::String &data )
{
        SComponent *c = new SComponent;
        c->sData = data;
        c->sSection = section;
        c->sSection2 = section2;

        if ( c->sData.right(1) != ";" )
                c->sData += ";";

        m_lComponent.push_back ( c );

        _changed();
}

void CXspFile::AddWeaponMask ( int game, int mask )
{
        // first check if we have one for the game
        SWeaponMask *m = NULL;
        for ( CListNode<SWeaponMask> *node = m_lWeaponMasks.Front(); node; node = node->next() )
        {
                if ( node->Data()->iGame == game )
                {
                        m = node->Data();
                        break;
                }
        }

        // not found, create one
        if ( !m )
        {
                m = new SWeaponMask;
                m_lWeaponMasks.push_back(m);
                m->iGame = game;
        }

        m->iMask = mask;
        _changed();
}
void CXspFile::AddMissileMask ( int game, int mask )
{
        // first check if we have one for the game
        SWeaponMask *m = NULL;
        for ( CListNode<SWeaponMask> *node = m_lMissileMasks.Front(); node; node = node->next() )
        {
                if ( node->Data()->iGame == game )
                {
                        m = node->Data();
                        break;
                }
        }

        // not found, create one
        if ( !m )
        {
                m = new SWeaponMask;
                m_lMissileMasks.push_back(m);
                m->iGame = game;
        }

        m->iMask = mask;
        _changed();
}

bool CXspFile::IsValid ()
{
        if ( this->name().empty() )
                return false;
        if ( this->author().empty() )
                return false;

        return true;
}

Utils::String CXspFile::CreateValuesLine() const
{
        Utils::String values = CBaseFile::CreateValuesLine ();

        values += "Data: " + m_sData + "\n";
        values += "ID: " + m_sID + "\n";

        if ( m_bLanguageText )
                values += "LanguageText\n";
        if ( m_bExistingShip )
                values += "ExistingShip\n";
        values += Utils::String("OrgDesc: ") + (long)m_iOrgDesc + "\n";
        values += Utils::String("Shipyard: ") + (long)m_iShipyard + "\n";

        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() ) {
                SCockpit *cockpit = node->Data();
                values += Utils::String("CockpitNew: ") + (long)cockpit->lWeaponMask.size();
                for ( CListNode<SWeaponMask> *wNode = cockpit->lWeaponMask.Front(); wNode; wNode = wNode->next() ) {
                        values += ":";
                        values += (long)wNode->Data()->iGame;
                        values += ":";
                        values += (long)wNode->Data()->iMask;
                }
                values += " ";
                values += cockpit->sCockpit + "\n";
        }

        for ( CListNode<SText> *tNode = m_lText.Front(); tNode; tNode = tNode->next() )
                values += Utils::String("Text: ") + (long)tNode->Data()->iId + "|" + tNode->Data()->sName.findReplace("|", "<::PiPe::>") + "|" + tNode->Data()->sDesc + "\n";

        for ( CListNode<SComponent> *cNode = m_lComponent.Front(); cNode; cNode = cNode->next() )
                values += "Component: " + cNode->Data()->sData.findReplace("|", "<::PiPe::>") + "|" + cNode->Data()->sSection.findReplace("|", "<::PiPe::>") + "|" + cNode->Data()->sSection2 + "\n";

        for ( CListNode<SDummy> *dNode = m_lDummy.Front(); dNode; dNode = dNode->next() )
                values += "Dummy: " + dNode->Data()->sData.findReplace("|", "<::PiPe::>") + "|" + dNode->Data()->sSection + "\n";

        for ( CListNode<SWeaponMask> *wNode = m_lWeaponMasks.Front(); wNode; wNode = wNode->next() )
                values += Utils::String("WeaponMask: ") + (long)wNode->Data()->iGame + " " + (long)wNode->Data()->iMask + "\n";
        for ( CListNode<SWeaponMask> *mNode = m_lMissileMasks.Front(); mNode; mNode = mNode->next() )
                values += Utils::String("MissileMask: ") + (long)mNode->Data()->iGame + " " + (long)mNode->Data()->iMask + "\n";
        for ( SStringList *cut = m_lCutData.Head(); cut; cut = cut->next )
                values += Utils::String("CutData: ") + cut->str.ToString() + "\n";
        for ( SStringList *body = m_lBodies.Head(); body; body = body->next )
                values += Utils::String("Bodies: ") + body->str.ToString() + "\n";
        for ( SStringList *ani = m_lAnimations.Head(); ani; ani = ani->next )
                values += Utils::String("Animations: ") + ani->str.ToString() + "\n";

        return values;
}

bool CXspFile::ParseValueLine(const Utils::String &sLine)
{
        Utils::String first = sLine.token(":", 1);
        Utils::String rest  = sLine.tokens(":", 2).removeFirstSpace();

        if ( first.Compare("Data") )
                m_sData = rest;
        else if ( first.Compare("ID") )
                m_sID = rest;
        else if ( sLine.Compare("LanguageText") )
                m_bLanguageText = true;
        else if ( sLine.Compare("ExistingShip") )
                m_bExistingShip = true;
        else if ( first.Compare("OrgDesc") || first.Compare("OriginalDesc") )
                m_iOrgDesc = rest;
        else if ( first.Compare("Shipyard") )
                m_iShipyard = rest;
        else if ( first.Compare("CutData") )
                this->AddCutData(rest);
        else if ( first.Compare("Bodies") )
                this->AddBodies(rest);
        else if ( first.Compare("Animations") )
                this->AddAnimation(rest);
        else if ( first.Compare("Cockpit") )
                this->AddCockpit(rest, 0);
        else if ( first.Compare("CockpitNew") )
        {
                Utils::String cockpit = rest.tokens(" ", 2);
                Utils::String sMasks = rest.token(" ", 1);
                this->AddCockpit(cockpit, 0, -1);
                int num = sMasks.token(":", 1);
                for ( int i = 0; i < num; i++ )
                {
                        int mask = sMasks.token(":", ((i + 1) * 2) + 1);
                        int game = sMasks.token(":", ((i + 1) * 2));
                        this->AddCockpit(cockpit, game, mask);
                }
        }
        else if ( first == "Web" )      this->setWebSite(rest);
        else if ( first.Compare("WeaponMask") )
                this->AddWeaponMask(rest.token(" ", 1), rest.token(" ", 2));
        else if ( first.Compare("MissileMask") )
                this->AddMissileMask(rest.token(" ", 1), rest.token(" ", 2));
        else if ( first == "Text" )
        {
                SText *text = new SText;
                text->iId = rest.token("|", 1);
                text->sName = rest.token("|", 2);
                text->sName = text->sName.findReplace("<::PiPe::>", "|");
                text->sDesc = rest.tokens( "|", 3);
                m_lText.push_back ( text );
        }
        else if ( first == "Component" )
        {
                SComponent *c = new SComponent;
                c->sData = rest.token("|", 1);
                c->sData = c->sData.findReplace ("<::PiPe::>", "|");
                c->sSection = rest.token("|", 2);
                c->sSection = c->sSection.findReplace ("<::PiPe::>", "|");
                c->sSection2 = rest.tokens("|", 3);
                m_lComponent.push_back ( c );
        }
        else if ( first == "Dummy" )
        {
                SDummy *d = new SDummy;
                d->sData = rest.token("|", 1);
                d->sData = d->sData.findReplace ("<::PiPe::>", "|");
                d->sSection = rest.tokens( "|", 2);
                m_lDummy.push_back ( d );
        }
        else if ( first == "Comment" )  this->setDescription(rest.findReplace("<newline>", "<br>"));
        else
                return CBaseFile::ParseValueLine(sLine);

        return true;
}

Utils::String CXspFile::GetShipName(int lang)
{
        Utils::String name;
        if ( (m_bLanguageText) && (lang) )
        {
                for ( SText *text = m_lText.First(); text; text = m_lText.Next() )
                {
                        if ( text->iId == lang )
                        {
                                name = text->sName;
                                break;
                        }
                }
        }

        if ( name.empty() )
                name = GetLanguageName(lang).ToString();

        return name;
}

//TODO: split this up
bool CXspFile::ConvertOld(const Utils::String &file)
{
        // open the file
        FILE *id = fopen(file.c_str(), "rb");
        if ( !id )
                return false;

        // read to memory
        fseek(id, 0, SEEK_END);
        size_t size = ftell(id);
        fseek(id, 0, SEEK_SET);
        unsigned char *data = new unsigned char[size + 1];
        fread(data, sizeof(unsigned char), size, id);
        data[size] = '\0';
        fclose(id);

        // uncompress the file
        size_t len;
        unsigned char *compr = LZMADecode_C ( data, size, &len, NULL );

        delete data;

        if ( !compr )
                return false;

        this->Delete();
        this->SetDefaults();

        // now read the data line by line
        size_t pos = 0;
        size_t start = 0;
        int packageVersion = 0;
        while ( pos < len )
        {
                while ( compr[pos++] != '\n' && pos < len );

                compr[pos - 1] = '\0';
                Utils::String line = (char *)(compr + start);
                start = pos;
                if ( line.empty() )
                        continue;
                Utils::String first = line.token(":", 1);
                Utils::String rest = line.tokens(":", 2);
                rest = rest.removeFirstSpace();

                // now check each line
                if ( first.Compare("Packager") )
                        packageVersion = rest;
                else if ( first.Compare("Shipyard") )
                {
                        int max;
                        Utils::String *strs = rest.tokenise(";", &max);
                        if ( strs && max )
                        {
                                int cur = 1;
                                for ( int i = 0; i < max; i++, cur *= 2 )
                                {
                                        if ( strs[i] == "1" )
                                                this->AddShipyard(cur);
                                }
                        }
                        CLEANSPLIT(strs, max)
                }
                else if ( first.Compare("ScreenShot") )
                {
                        int size = rest.token(" ", 1);
                        Utils::String ext = rest.token(" ", 2);

                        C_File *newFile = this->AddFile(m_sID + "_" + (long)(this->CountFiles(FILETYPE_SCREEN) + 1) + "." + ext, "", FILETYPE_SCREEN);
                        newFile->ReadFromData((char *)(compr + pos), size); 

                        start += (size + 1);
                }
                else if ( first.Compare("SceneFile") )
                {
                        Utils::String file = rest.tokens(" ", 3);
                        m_pSceneFile = this->AddFile(CFileIO(file).filename(), CFileIO(file).dir(), FILETYPE_SHIPSCENE);
                        m_pSceneFile->SetCreationTime((long)rest.token(" ", 1));
                        m_pSceneFile->ReadFromData((char *)(compr + pos), rest.token(" ", 2));

                        start += m_pSceneFile->GetDataSize();
                }
                else if ( first.Compare("CockpitFile") )
                {
                        Utils::String file = rest.tokens(" ", 3);
                        m_pCockpitFile = this->AddFile(CFileIO(file).filename(), CFileIO(file).dir(), FILETYPE_COCKPITSCENE);
                        m_pCockpitFile->SetCreationTime((long)rest.token(" ", 1));
                        m_pCockpitFile->ReadFromData((char *)(compr + pos), rest.token(" ", 2));

                        start += m_pCockpitFile->GetDataSize();
                }
                else if ( first.Compare("Model") )
                {
                        Utils::String file = rest.tokens(" ", 3);
                        C_File *newFile= this->AddFile(CFileIO(file).filename(), CFileIO(file).dir(), FILETYPE_SHIPMODEL);
                        newFile->SetCreationTime((long)rest.token(" ", 1));
                        newFile->ReadFromData((char *)(compr + pos), rest.token(" ", 2));

                        start += (newFile->GetDataSize() + 1);
                }
                else if ( first.Compare("Files") )
                {
                        Utils::String file = rest.tokens(" ", 3);

                        C_File *newFile = NULL;
                        int special = 0;
                        if ( file.Compare("types/CutData.txt") || file.Compare("types/CutData.pck") )
                        {
                                newFile = new C_File(file);
                                special = 1;
                        }
                        else if ( file.Compare("types/Bodies.txt") || file.Compare("types/Bodies.pck") )
                        {
                                newFile = new C_File(file);
                                special = 2;
                        }
                        else if ( file.Compare("types/Animations.txt") || file.Compare("types/Animations.pck") )
                        {
                                newFile = new C_File(file);
                                special = 3;
                        }
                        else
                        {
                                newFile = this->AddFile(CFileIO(file).filename(), CFileIO(file).dir(), FILETYPE_SHIPOTHER);
                                newFile->SetCreationTime((long)rest.token(" ", 1));
                        }
                        newFile->ReadFromData((char *)(compr + pos), rest.token(" ", 2));
                        start += (newFile->GetDataSize() + 1);

                        if ( special )
                        {
                                if ( newFile->CheckFileExt("pck") )
                                        newFile->UnPCKFile();

                                // read data into lines
                                CyString data((const char *)newFile->GetData());
                                data.RemoveChar('\r');
                                int iLines;
                                CyString *sLines = data.SplitToken("\n", &iLines);
                                
                                if ( sLines && iLines )
                                {
                                        // cut data
                                        int entries = -1;
                                        if ( special == 1 )
                                        {
                                                CyStringList newLines;
                                                for ( int i = 0; i < iLines; i++ )
                                                {
                                                        CyString line = sLines[i];
                                                        line.RemoveChar(' ');
                                                        line.RemoveChar(9);
                                                        if ( line.Empty() || line[0] == '/' )
                                                                continue;
                                                        if ( entries == -1 )
                                                        {
                                                                entries = line.GetToken(";", 1, 1).ToInt() - 36;
                                                                if ( entries <= 0 )
                                                                        break;
                                                        }
                                                        else
                                                        {
                                                                int id = line.GetToken(";", 1, 1).ToInt();
                                                                if ( id >= 9000 && id <= 9017 )
                                                                        continue;

                                                                switch (id)
                                                                {
                                                                        case 948:
                                                                        case 4111:
                                                                        case 4112:
                                                                        case 4064:
                                                                        case 4169:
                                                                        case 4178:
                                                                        case 4177:
                                                                        case 4163:
                                                                        case 4194:
                                                                        case 4162:
                                                                        case 4191:
                                                                        case 4200:
                                                                        case 4161:
                                                                        case 4097:
                                                                        case 4205:
                                                                        case 4206:
                                                                        case 4207:
                                                                        case 4107:
                                                                                break;

                                                                        default:
                                                                                newLines.PushBack(line);
                                                                }
                                                                if ( newLines.Count() == entries )
                                                                        break;
                                                        }
                                                }

                                                for ( SStringList *strNode = newLines.Head(); strNode; strNode = strNode->next )
                                                        this->AddCutData(strNode->str.ToString());
                                        }
                                        // bodies
                                        else if ( special == 2 )
                                        {
                                                entries = 0;
                                                Utils::String section;
                                                for ( int i = 0; i < iLines; i++ )
                                                {
                                                        Utils::String line = sLines[i].ToString();
                                                        line.removeChar(' ');
                                                        line.removeChar(9);
                                                        if ( line.empty() || line[0] == '/' )
                                                                continue;
                                                        if ( entries <= 0)
                                                        {
                                                                section = line.token(";", 1);
                                                                entries = line.token(";", 2);
                                                        }
                                                        else
                                                        {
                                                                if ( !line.isin(";") )
                                                                        continue;
                                                                if ( line.countToken(";") <= 2 )
                                                                {
                                                                        this->AddBodies(section + ";" + line.token(";", 1) + ";");
                                                                        --entries;
                                                                }
                                                                else
                                                                {
                                                                        bool done = false;
                                                                        while (!done)
                                                                        {
                                                                                int num;
                                                                                Utils::String *strs = line.tokenise(";", &num);
                                                                                done = true;
                                                                                for ( int j = 0; j < num; j++ )
                                                                                {
                                                                                        if ( !entries )
                                                                                        {
                                                                                                line = line.tokens(";", j + 1);
                                                                                                if ( !line.empty() )
                                                                                                        done = false;
                                                                                                break;
                                                                                        }

                                                                                        if ( strs[j].empty() )
                                                                                                continue;
                                                                                        
                                                                                        this->AddBodies(section + ";" + strs[j] + ";");
                                                                                        --entries;
                                                                                }
                                                                                //we must be at another section
                                                                                if ( !done )
                                                                                {
                                                                                        section = line.token(";", 1);
                                                                                        entries = line.token(";", 2);
                                                                                        line = line.remToken(";", 1);
                                                                                        line = line.remToken(";", 1);

                                                                                        if (line.empty())
                                                                                                done = true;
                                                                                }

                                                                                CLEANSPLIT(strs, num)
                                                                        }                                                                       
                                                                }
                                                        }
                                                }
                                        }
                                        // animations
                                        else if ( special == 3 )
                                        {
                                                CyStringList in;
                                                for ( int i = 0; i < iLines; i++ )
                                                {
                                                        CyString line = sLines[i];
                                                        in.PushBack(line);
                                                }

                                                CyStringList out;
                                                if ( CXspFile::ReadAnimations(&in, &out, 87) )
                                                        this->AddAnimation(&out);
                                        }
                                }
                                CLEANSPLIT(sLines, iLines)
                                delete newFile;
                        }
                }
                else if ( first.Compare("Script") )
                {
                        Utils::String file = rest.words(3);
                        C_File *newFile= this->AddFile(file, NullString, FILETYPE_SCRIPT);
                        newFile->SetCreationTime((long)rest.word(1));
                        newFile->ReadFromData((char *)(compr + pos), rest.word(2));

                        start += (newFile->GetDataSize() + 1);
                }
                else if ( first.Compare("Text") )
                        this->AddText(rest.token(":", 1), rest.token(":", 2), rest.tokens(":", 3));
                else if ( first.Compare("Component") )
                        this->AddComponent(rest.token(";", 1), rest.token(";", 2), rest.tokens(";", 3));
                else if ( first.Compare("Dummy") )
                {
                        SDummy *d = new SDummy;
                        d->sData = rest.tokens(";", 2);
                        d->sSection = rest.token(";", 1);
                        m_lDummy.push_back ( d );
                }
                else if ( !this->ParseValueLine(line) )
                {
                //      printf ( "Command: %s, Rest: %s\n", first.c_str(), rest.c_str());
                }

                pos = start;
        }

        // assume all old ones are for X3
        if ( !m_sData.empty() )
        {
                this->AddWeaponMask(GAME_X3 - 1, m_sData.token(";", 19));
                this->AddMissileMask(GAME_X3 - 1, m_sData.token(";", 25));
        }

        return true;
}


int GetMaxShipyards() { return (int)SHIPYARD_MAX; }

Utils::String GetShipyardName (int s)
{
        switch (s)
        {
                case SHIPYARD_ARGON:
                        return "Argon";
                case SHIPYARD_BORON:
                        return "Boron";
                case SHIPYARD_PARANID:
                        return "Paranid";
                case SHIPYARD_SPLIT:
                        return "Split";
                case SHIPYARD_TELADI:
                        return "Teladi";
                case SHIPYARD_PIRATES:
                        return "Pirates";
                case SHIPYARD_FRIEND:
                        return "Friendly";
                case SHIPYARD_XENON:
                        return "Xenon";
                case SHIPYARD_TERRAN:
                        return "Terran";
        }

        return "Unknown";
}

bool CXspFile::WriteHeader(CFileIO &file, int valueheader, int valueComprLen)
{
        return file.write("XSPCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen);
}

bool CXspFile::CheckHeader(const Utils::String header) const
{
        if ( header.Compare("XSPCycrow") )
                return true;
        return false;
}

void CXspFile::SetLaserMask(int game, int mask)
{
        for ( CListNode<SWeaponMask> *node = m_lWeaponMasks.Front(); node; node = node->next() )
        {
                SWeaponMask *m = node->Data();
                if ( m->iGame == game )
                {
                        m->iMask = mask;
                        return;
                }
        }

        // no found, need to add it
        this->AddWeaponMask(game, mask);
}

void CXspFile::SetMissileMask(int game, int mask)
{
        for ( CListNode<SWeaponMask> *node = m_lMissileMasks.Front(); node; node = node->next() )
        {
                SWeaponMask *m = node->Data();
                if ( m->iGame == game )
                {
                        m->iMask = mask;
                        return;
                }
        }

        // no found, need to add it
        this->AddMissileMask(game, mask);
}
int CXspFile::GetLaserMask(int game, bool getOnly)
{
        int mask = -1;
        for ( CListNode<SWeaponMask> *node = m_lWeaponMasks.Front(); node; node = node->next() )
        {
                SWeaponMask *m = node->Data();
                if ( m->iGame == game )
                        return m->iMask;

                if ( !mask && !getOnly )
                        mask = m->iMask;
        }

        return mask;
}

int CXspFile::GetMissileMask(int game, bool getOnly)
{
        int mask = -1;
        for ( CListNode<SWeaponMask> *node = m_lMissileMasks.Front(); node; node = node->next() )
        {
                SWeaponMask *m = node->Data();
                if ( m->iGame == game )
                        return m->iMask;

                if ( !mask && !getOnly )
                        mask = m->iMask;
        }

        return mask;
}

Utils::String CXspFile::GetShipClass()
{
        if ( !m_sData.empty() )
                return m_sData.token(";", TSHIPPOS_CLASS);
        return "OBJ_SHIP_M5";
}

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

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

        if ( m_iShipyard )
        {
                list->pushBack("# Shipyards, Set which shipyards to add ships for sale to");
                for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
                {
                        if ( this->IsShipyard(i) )
                                list->pushBack("Shipyard: " + GetShipyardName(i));
                }
                list->pushBack("");
        }

        if ( m_iOrgDesc > 0 )
        {
                list->pushBack("# Use Original Description, overrides text entrys to use one of the built in text");
                list->pushBack(Utils::String("OriginalDescription: ") + (long)m_iOrgDesc);
                list->pushBack("");
        }

        if ( !m_sID.empty() )
        {
                list->pushBack("# Ship ID, the ship id to identify the ship as");
                list->pushBack("ShipID: " + m_sID);
        }

        if ( m_bExistingShip )
        {
                list->pushBack("# Existing Ship, replaces an existing ship in the game with ship package instead of creating a new entry");
                list->pushBack("ExistingShip");
                list->pushBack("");
        }

        if ( !m_sData.empty() )
        {
                list->pushBack("# Ship Data, the TShip data entry to add to the game (parts of this are adjusted and auto generated by the installer)");
                list->pushBack("ShipData: " + m_sData);
                list->pushBack("");
        }

        if ( m_lText.size() ) {
                list->pushBack("# Ship Texts, the name/description of the ship in each language: <LANGID> <NAME>|<DESCRIPTION>");
                for(CListNode<SText> *node = m_lText.Front(); node; node = node->next()) {
                        list->pushBack(Utils::String("ShipText: ") + (long)node->Data()->iId + " " + node->Data()->sName + "|" + node->Data()->sDesc);
                }

                list->pushBack("");
        }

        if ( this->m_lWeaponMasks.size() ) {
                list->pushBack("# Weapon Masks, the weapons for each game: <GAME> <MASK>");
                for(CListNode<SWeaponMask> *node = m_lWeaponMasks.Front(); node; node = node->next()) {
                        list->pushBack(Utils::String("WeaponMask: ") + (long)node->Data()->iGame + " " + (long)node->Data()->iMask);
                }

                list->pushBack("");
        }

        if ( this->m_lMissileMasks.size() ) {
                list->pushBack("# Missile Masks, the missiles for each game: <GAME> <MASK>");
                for(CListNode<SWeaponMask> *node = m_lMissileMasks.Front(); node; node = node->next()) {
                        list->pushBack(Utils::String("WeaponMask: ") + (long)node->Data()->iGame + " " + (long)node->Data()->iMask);
                }

                list->pushBack("");
        }

        if ( this->m_lComponent.size() ) {
                list->pushBack("# Ship Components, each component used in the ships scene: <SECTION> <MODELENTRY> <VALUES>");
                for(CListNode<SComponent> *node = m_lComponent.Front(); node; node = node->next()) {
                        list->pushBack(Utils::String("Component: ") + node->Data()->sSection + " " + node->Data()->sSection2 + " " + node->Data()->sData);
                }
                list->pushBack("");
        }

        if ( this->m_lDummy.size() ) {
                list->pushBack("# Ship Dummies, each dummy entry used in the ships scene: <SECTION> <VALUE>");
                for(CListNode<SDummy> *node = m_lDummy.Front(); node; node = node->next()) {
                        list->pushBack("Dummy: " + node->Data()->sSection + " " + node->Data()->sData);
                }
                list->pushBack("");
        }

        if ( this->m_lCockpit.size() ) {
                list->pushBack("# Cockpit entries, each cockpit value with thier weapons mask");
                for(CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next()) {
                        list->pushBack("Cockpit: " + node->Data()->sCockpit.token(";", 19) + " " + node->Data()->sCockpit);
                        for(SWeaponMask *mask = node->Data()->lWeaponMask.First(); mask; mask = node->Data()->lWeaponMask.Next()) {
                                list->pushBack("CockpitWeapon: " + node->Data()->sCockpit.token(";", 19) + " " + (long)mask->iGame + " " + (long)mask->iMask);
                        }
                }

                list->pushBack("");
        }

        if ( !this->m_lCutData.Empty() ) {
                list->pushBack("# Ship Cut Data");
                for(SStringList *str = m_lCutData.Head(); str; str = str->next) {
                        list->pushBack(Utils::String("CutData: ") + str->str.ToString());
                }
                list->pushBack("");
        }

        if ( !this->m_lBodies.Empty() ) {
                list->pushBack("# Ship Bodies");
                for(SStringList *str = m_lBodies.Head(); str; str = str->next) {
                        list->pushBack(Utils::String("Bodies: ") + str->str.ToString());
                }
                list->pushBack("");
        }

        if ( !this->m_lAnimations.Empty() ) {
                list->pushBack("# Ship Animations");
                for(SStringList *str = m_lAnimations.Head(); str; str = str->next) {
                        list->pushBack(Utils::String("Animation: ") + str->str.ToString());
                }
                list->pushBack("");
        }

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

        return true;
}

void CXspFile::_addSection(Utils::CList<STypesSection> &list, const Utils::String &section, const Utils::String &data)
{
        STypesSection *currentSubSection = NULL;
        for(Utils::CList<STypesSection>::iterator itr = list.begin(); itr != list.end(); itr++) {
                if ( (*itr)->sSection.Compare(section) ) {
                        currentSubSection = *itr;
                        break;
                }
        }

        if ( !currentSubSection ) {
                currentSubSection = new STypesSection;
                currentSubSection->sSection = section;
                list.push_back(currentSubSection);
        }

        currentSubSection->lEntries.pushBack(data, "");
}

void CXspFile::_addDataSection(CyStringList &list, Utils::CList<STypesSection> &sectionList, bool bUseFirst)
{
        for(SStringList *str = list.Head(); str; str = str->next) {
                if ( bUseFirst ) {
                        Utils::String data = str->str.ToString();
                        _addSection(sectionList, data.token(";", 1), data.tokens(";", 2));
                }
                else {
                        _addSection(sectionList, str->str.ToString(), str->data.ToString());
                }
        }
}

void CXspFile::addDummiesToList(Utils::CList<STypesSection> &list)
{
        ADDSECTIONLIST(SDummy, m_lDummy);
}

void CXspFile::addComponentsToList(CLinkList<SComponentEntry> &componentList)
{
        for(CListNode<SComponent> *node = m_lComponent.Front(); node; node = node->next()) {
                SComponentEntry *currentSection = NULL;
                for(CListNode<SComponentEntry> *cNode = componentList.Front(); cNode; cNode = cNode->next()) {
                        if ( cNode->Data()->sSection.Compare(node->Data()->sSection) ) {
                                currentSection = cNode->Data();
                                break;
                        }
                }

                if ( !currentSection ) {
                        currentSection = new SComponentEntry();
                        currentSection->sSection = node->Data()->sSection;
                        componentList.push_back(currentSection);
                }

                _addSection(currentSection->lEntries, node->Data()->sSection2, node->Data()->sData);
        }
}

void CXspFile::addCutDataToList(Utils::CList<STypesSection> &list)
{
        _addDataSection(this->m_lCutData, list, false);
}

void CXspFile::addBodiesToList(Utils::CList<STypesSection> &list)
{
        _addDataSection(this->m_lBodies, list, true);
}

void CXspFile::addAnimationsToList(Utils::CList<STypesSection> &list)
{
        _addDataSection(this->m_lAnimations, list, false);
}


void CXspFile::addGeneratedFiles(HZIP &hz)
{
        TCHAR buf[5000];

        CFileIO tmpFile(CPackages::tempDirectory() + "/temp.tmp");

        for(int i = 0; i < 5; i++) {
                Utils::String type;
                Utils::String data;
                switch(i) {
                        case 0:
                                data = "50;1\r\n" + this->m_sData;
                                type = "TShips";
                                break;
                        case 1:
                                if ( !this->m_lCockpit.empty() ) {
                                        data = Utils::String("51;") + (long)this->m_lCockpit.size() + ";\r\n";
                                        for(CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next()) {
                                                data += node->Data()->sCockpit + "\r\n";
                                        }
                                        type = "TCockpits";
                                }
                                break;
                        case 2:
                                if ( !this->m_lComponent.empty() ) {
                                        type = "Components";
                                        CLinkList<SComponentEntry> componentList;
                                        this->addComponentsToList(componentList);
                                        for(CListNode<SComponentEntry> *node = componentList.Front(); node; node = node->next()) {
                                                data += node->Data()->sSection + "; " + (long)node->Data()->lEntries.size() + ";\r\n";
                                                data += CXspFile::TypesListToString(node->Data()->lEntries, true);
                                        }
                                }
                                break;
                        case 3:
                                GENERATESECTION("Dummies", SDummy, this->m_lDummy);
                                break;
                        case 4:
                                GENERATESTRINGSECTION("Bodies", this->m_lBodies, true);
                                break;
                }

                if ( type.empty() ) continue;

                Utils::String fname = "GENERATED/types/" + type + ".txt";
                
                Utils::String fileData = "// Exported " + type + " file, Generated by SPK Libraries V" + Utils::String::FromFloat(GetLibraryVersion(), 2) + "\r\n" + data;
                if ( tmpFile.startWrite() ) {
                        tmpFile.write(fileData.c_str(), fileData.length());
                        tmpFile.close();
                }

                // tships files
                wsprintf(buf, L"%hs", fname.c_str());
                        
                if ( tmpFile.startRead() ) {
                        size_t dataSize = 0;
                        unsigned char *data = tmpFile.readAll(&dataSize);
                        ZipAdd(hz, buf, data, dataSize);
                }
        }
}

bool CXspFile::LoadPackageData(const Utils::String &sFirst, const Utils::String &sRest, const Utils::String &sMainGame, Utils::CStringList &otherGames, Utils::CStringList &gameAddons, CProgressInfo *progress)
{
        if ( sFirst.Compare("Shipyard") )
        {
                for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
                {
                        if ( sRest.Compare(GetShipyardName(i)) )
                        {
                                this->AddShipyard(i);
                                break;
                        }
                }
        }
        else if ( sFirst.Compare("OriginalDescription") )
                m_iOrgDesc = sRest;
        else if ( sFirst.Compare("ShipData") )
                m_sData = sRest;
        else if ( sFirst.Compare("ReadData") ) // read data from a tships file
        {
                CPackages p;
                m_sData = p.ReadShipData(sRest.tokens(" ", 2), sRest.token(" ", 1)).ToString();
        }
        else if ( sFirst.Compare("ExistingShip") )
                m_bExistingShip = true;
        else if ( sFirst.Compare("ShipID") )
                m_sID = sRest;
        else if ( sFirst.Compare("ShipText") )
                this->AddText(sRest.token(" ", 1).toLong(), sRest.tokens(" ", 2).token("|", 1), sRest.tokens(" ", 2).tokens("|", 2));
        else if ( sFirst.Compare("Component") )
                this->AddComponent(sRest.token(" ", 1), sRest.token(" ", 2), sRest.tokens(" ", 3));
        else if ( sFirst.Compare("Cockpit") )
                this->AddCockpit(sRest.tokens(" ", 2).replaceToken(";", 19, sRest.token(" ", 1)), 0);
        else if ( sFirst.Compare("CockpitWeapon") )
                this->AddCockpitWeapon(sRest.token(" ", 1), sRest.token(" ", 2), sRest.token(" ", 3));
        else if ( sFirst.Compare("WeaponMask") )
                this->AddWeaponMask(sRest.token(" ", 1), sRest.token(" ", 2));
        else if ( sFirst.Compare("MissileMask") )
                this->AddMissileMask(sRest.token(" ", 1), sRest.token(" ", 2));
        else if ( sFirst.Compare("Dummy") )
                this->AddDummy(sRest.token(" ", 1), sRest.token(" ", 2));
        else if ( sFirst.Compare("CutData") )
                this->AddCutData(sRest);
        else if ( sFirst.Compare("Animation") )
                this->AddAnimation(sRest);
        else if ( sFirst.Compare("Bodies") )
                this->AddBodies(sRest);
        else if ( !CBaseFile::LoadPackageData(sFirst, sRest, sMainGame, otherGames, gameAddons, progress) )
        {
                return false;
        }

        return true;
}


Utils::String CXspFile::GetX3ShipData()
{
        Utils::String data = m_sData;

        // change the ship subtype, Reunion uses number, TC uses a define
        Utils::String sSubType = data.token(";", 6);
        if ( !((long)sSubType) && sSubType != "0" )
                data = data.replaceToken(";", 6, (long)CShipData::ConvertShipSubType(CyString(sSubType)));

        Utils::String sClass = data.token(";", TSHIPPOS_CLASS);
        if ( !((long)sClass) && sClass != "0" )
        {
                int num = 0;
                for ( int i = 0; i < OBJ_SHIP_MAX; i++ )
                {
                        if ( sClass.Compare(CShipData::ConvertShipClass(CShipData::GetShipClassFromNum(i))) )
                        {
                                num = i;
                                break;
                        }
                }

                data = data.replaceToken(";", TSHIPPOS_CLASS, (long)num);
        }

        return data;
}

Utils::String CXspFile::GetTCShipData()
{
        Utils::String data = m_sData;

        Utils::String sSubType = data.token(";", 6);
        if ( ((long)sSubType) || sSubType == "0" )
                data = data.replaceToken(";", 6, CShipData::ConvertShipSubType((long)sSubType).ToString());

        Utils::String sClass = data.token(";", TSHIPPOS_CLASS);
        if ( ((long)sClass) || sClass == "0" )
                data = data.replaceToken(";", TSHIPPOS_CLASS, CShipData::ConvertShipClass((long)sClass));

        return data;
}

bool CXspFile::RemoveCockpit(const Utils::String &sCockpitId)
{
        Utils::String cockpitid = sCockpitId;
        // if its a whole line, just get the end
        if ( cockpitid.isin(";") )
        {
                cockpitid = cockpitid.tokens(";", -2);
                while ( cockpitid.right(1) == ";" )
                        cockpitid.truncate(-1);
        }

        bool ret = false;
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(cockpitid) )
                {
                        node->DeleteData();
                        ret = true;
                        break;
                }
        }

        m_lCockpit.RemoveEmpty();

        return ret;
}

bool CXspFile::RemoveComponent(const Utils::String &section1, const Utils::String &section2, const Utils::String &data)
{
        bool ret = false;
        for ( CListNode<SComponent> *node = m_lComponent.Front(); node; node = node->next() )
        {
                if ( node->Data()->sSection.Compare(section1) && node->Data()->sSection2.Compare(section2) && node->Data()->sData.Compare(data) )
                {
                        ret = true;
                        node->DeleteData();
                        break;
                }
        }
        m_lComponent.RemoveEmpty();
        return ret;
}

bool CXspFile::RemoveDummy(const Utils::String &section, const Utils::String &data)
{
        bool ret = false;
        for ( CListNode<SDummy> *node = m_lDummy.Front(); node; node = node->next() )
        {
                if ( node->Data()->sSection.Compare(section) && node->Data()->sData.Compare(data) )
                {
                        ret = true;
                        node->DeleteData();
                        break;
                }
        }
        m_lDummy.RemoveEmpty();

        return ret;
}

Utils::String CXspFile::GetCockpitData(const Utils::String &cid)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(cid) )
                        return node->Data()->sCockpit;
        }

        return "";
}

SCockpit *CXspFile::FindCockpit(const Utils::String &cid)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(cid) )
                        return node->Data();
        }

        return NULL;
}

void CXspFile::EditCockpit(const Utils::String &cid, const Utils::String &cockpit)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(cid) )
                {
                        node->Data()->sCockpit = cockpit;
                        break;
                }
        }
}

void CXspFile::EditCockpit(const Utils::String &cid, const Utils::String &scene, int mask)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(cid) )
                {
                        Utils::String cockpit = node->Data()->sCockpit;
                        cockpit = cockpit.replaceToken(";", 8, scene);
                        cockpit = cockpit.replaceToken(";", 9, (long)mask);
                        node->Data()->sCockpit = cockpit;
                        break;
                }
        }
}
void CXspFile::NewCockpit(const Utils::String &id, const Utils::String &scene, int mask)
{
        Utils::String cockpit = "0;0;0;0;0;0;0;";
        cockpit += scene + ";";
        cockpit += (long)mask;
        cockpit += ";0;0;0;0;0;0;-100000;0;0;";
        cockpit += id + ";";
        this->AddCockpit(cockpit, -1);
}

bool CXspFile::RemoveCutData(const Utils::String &cut)
{
        bool ret = false;
        for ( SStringList *str = m_lCutData.Head(); str; str = str->next )
        {
                if ( str->str.GetToken(";", 1, 1).Compare(CyString(cut.token(";", 1))) )
                {
                        ret = true;
                        str->remove = true;
                        break;
                }
        }

        m_lCutData.RemoveMarked();

        return ret;
}

bool CXspFile::RemoveBodies(const Utils::String &cut)
{
        bool ret = false;
        for ( SStringList *str = m_lBodies.Head(); str; str = str->next )
        {
                if ( str->str.Remove(' ').Compare(CyString(cut.remove(' '))) )
                {
                        ret = true;
                        str->remove = true;
                        break;
                }
        }

        m_lBodies.RemoveMarked();

        return ret;
}

bool CXspFile::RemoveAnimation(const Utils::String &cut)
{
        bool ret = false;
        for ( SStringList *str = m_lAnimations.Head(); str; str = str->next )
        {
                if ( str->str.Remove(' ').Remove('\n').Compare(CyString(cut.remove(' ').remove('\n'))) )
                {
                        ret = true;
                        str->remove = true;
                        break;
                }
        }

        m_lAnimations.RemoveMarked();

        return ret;
}

SCockpit *CXspFile::_findCockpit(const Utils::String &sID)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() ) {
                Utils::String id = node->Data()->sCockpit.token(";", 19);
                if ( id.Compare(sID) )
                        return node->Data();
        }

        return NULL;
}

void CXspFile::AddCockpitWeapon(const Utils::String &cockpit, int game, int mask)
{
        SCockpit *pCockpit = _findCockpit(cockpit);

        // search if the game mask already exists
        SWeaponMask *wm = NULL;
        for ( CListNode<SWeaponMask> *node = pCockpit->lWeaponMask.Front(); node; node = node->next() )
        {
                if ( node->Data()->iGame == game )
                {
                        wm = node->Data();
                        break;
                }
        }

        if ( !wm )
        {
                wm = new SWeaponMask;
                pCockpit->lWeaponMask.push_back(wm);
        }

        wm->iGame = game;
        if ( mask == -1 )
                wm->iMask = pCockpit->sCockpit.token(";", 9);
        else
                wm->iMask = mask;
        _changed();
}

void CXspFile::AddCockpit(const Utils::String &cockpit, int game, int mask, int index)
{
        Utils::String cid = cockpit.token(";", 19);
        SCockpit *pCockpit = _findCockpit(cid);

        if ( !pCockpit )
        {
                pCockpit = new SCockpit;
                pCockpit->sCockpit = cockpit;
                pCockpit->iIndex = index;
                m_lCockpit.push_back(pCockpit);
        }
        if ( index != -1 )
                pCockpit->iIndex = index;

        // now add the game mask
        if ( game > 0 )
                AddCockpitWeapon(cid, game, mask);
        _changed();
}

void CXspFile::AddBody(const Utils::String &section, const Utils::String &data)
{
        this->AddBodies(section + ";" + data);
        _changed();
}
void CXspFile::AddBodies(const Utils::String &sData)
{
        Utils::String data = sData;
        if ( !data.isNumber() )
        {
                if ( data[(int)(data.length() - 5)] == '.' )
                        data = data.left(-5);
                else if ( data[(int)(data.length() - 4)] == '.' )
                        data = data.left(-4);
                if ( data.right(1) != ";" )
                        data += ";";
        }
        m_lBodies.PushBack(CyString(data), true);
        _changed();
}

int CXspFile::GetAnimationType(const Utils::String &type)
{
        if ( type.Compare("TAT_TAGSINGLESTEP") )
                return TAT_SINGLE;
        else if ( type.Compare("TAT_TAGONESHOT") 
                                || type.Compare("TAT_TAGONESHOT_REINIT") 
                                || type.Compare("TAT_PINGPONG")
                                || type.Compare("TAT_TAGLOOP") )
                return TAT_3;

        return TAT_NONE;
}

Utils::String CXspFile::TypesListToString(Utils::CList<STypesSection> &list, bool deleteAfter)
{
        Utils::String data;

        for(Utils::CList<STypesSection>::iterator itr = list.begin(); itr != list.end(); itr++) {
                data += (*itr)->sSection + "; " + (long)(*itr)->lEntries.size() + ";\r\n";
                for(Utils::SStringList *str = (*itr)->lEntries.first(); str; str = (*itr)->lEntries.next()) {
                        data += str->str + "\r\n";
                }

                if ( deleteAfter ) {
                        (*itr)->lEntries.clear();
                }
        }

        return data;
}

bool CXspFile::ReadAnimations(CyStringList *lIn, CyStringList *lOut, int startRecord)
{
        Utils::String lastComment;
        Utils::String addEntry;
        int remaining = 0;
        Utils::String lastType;
        int newEntries = 0;
        int entries = -1;
        for ( SStringList *strNode = lIn->Head(); strNode; strNode = strNode->next )
        {
                Utils::String line = strNode->str.ToString();
                line.removeChar('\r');
                line.removeChar(9);
                if ( line.empty() || line[0] == '/' )
                        continue;
                if ( entries == -1)
                {
                        entries = line.token(";", 1);
                        newEntries = entries - startRecord;
                }
                else
                {
                        // remove comments, endspaces and last ;
                        Utils::String sStriped = line;
                        if ( sStriped.isin("//") )
                        {
                                if ( entries <= newEntries )
                                        lastComment = "//" + sStriped.tokens("//", 2);
                                sStriped = sStriped.token("//", 1);
                        }
                        sStriped.removeEndSpace();
                        if ( sStriped.right(1) == ";" )
                                sStriped.truncate(-1);

                        Utils::String sRemainingLeft;
                        if ( remaining > 0 )
                                sRemainingLeft = sStriped;
                        else
                        {
                                // each line should be a new entry, with some exceptions
                                // potection for new lines
                                lastType = sStriped.token(";", 1);

                                switch (CXspFile::GetAnimationType(lastType))
                                {
                                        case TAT_SINGLE:
                                                remaining = (long)sStriped.token(";", 5) + 1;
                                                sRemainingLeft = sStriped.tokens(";", 6);
                                                if ( entries <= newEntries )
                                                        addEntry = sStriped.tokens(";", 1, 5) + ";";
                                                break;
                                        case TAT_3:
                                                remaining = (long)sStriped.token(";", 5) + 1;
                                                sRemainingLeft = sStriped.tokens(";", 6);
                                                if ( entries <= newEntries )
                                                        addEntry = sStriped.tokens(";", 1, 5) + ";";
                                                break;

                                        default:
                                                remaining = 0;
                                                if ( entries <= newEntries )
                                                        addEntry = sStriped;

                                }
                        }

                        if ( !sRemainingLeft.empty() )
                        {
                                int tatType = CXspFile::GetAnimationType(lastType);
                                if ( tatType == TAT_SINGLE )
                                {
                                        if ( sRemainingLeft.isin(";") )
                                                remaining -= sRemainingLeft.countToken(";");
                                        else if ( !sRemainingLeft.empty() )
                                                --remaining;

                                        if ( entries <= newEntries )
                                                addEntry += sRemainingLeft + ";";
                                }
                                else if ( tatType == TAT_3 )
                                {
                                        // last entry
                                        if ( remaining == 1 )
                                                --remaining;
                                        else if ( sRemainingLeft.isin(";") )
                                        {
                                                if ( !sRemainingLeft.isin("TATF_COORDS") )
                                                {
                                                        int amt = sRemainingLeft.countToken(";") / 3;
                                                        remaining -= amt;
                                                        if ( remaining == 1 && (sRemainingLeft.countToken(";") - (amt * 3)) == 1 )
                                                                --remaining;
                                                }
                                                else
                                                {
                                                        int iRem = sRemainingLeft.countToken(";");
                                                        int iPos = 1;
                                                        while ( iPos < iRem )
                                                        {
                                                                Utils::String first = sRemainingLeft.token(";", iPos);
                                                                if ( first.isin("TATF_COORDS") )
                                                                        iPos += 5;
                                                                else
                                                                        iPos += 3;
                                                                --remaining;
                                                        }

                                                        if ( remaining == 1 && iPos == iRem )
                                                                --remaining;
                                                }
                                        }

                                        if ( entries <= newEntries )
                                                addEntry += sRemainingLeft + ";";
                                }
                        }

                        if ( remaining <= 0 )
                        {
                                if ( entries <= newEntries && !addEntry.empty())
                                {
                                        if ( addEntry[(int)addEntry.length() - 1] != ';' )
                                                addEntry += ";";
                                        lOut->PushBack(CyString(addEntry + lastComment));
                                }
                                --entries;
                        }
                }
        }

        return !lOut->Empty();
}

void CXspFile::AddAnimation(CyStringList *list)
{
        for ( SStringList *strNode = list->Head(); strNode; strNode = strNode->next )
                this->AddAnimation(strNode->str.ToString());
        _changed();
}

SText *CXspFile::FindShipText(int lang)
{
        if ( m_lText.empty() )
                return NULL;

        SText *english = NULL;
        SText *german = NULL;
        SText *found = NULL;
        for ( CListNode<SText> *node = m_lText.Front(); node; node = node->next() )
        {
                SText *text = node->Data();
                // matched language
                if ( text->iId == lang )
                        return text;
                else if ( text->iId == 44 )
                        english = text;
                else if ( text->iId == 49 )
                        german = text;
                else if ( !found )
                        found = text;
        }

        // if we've found an english version, use that
        if ( english )
                return english;
        // otherwise try a german
        if ( german )
                return german;
        // otherwise use any we've found (usually first one)
        return found;
}

Utils::String CXspFile::GetTextName(int lang)
{
        SText *t = FindShipText(lang);
        if ( t )
        {
                // return the correct language text
                if ( !t->sName.empty() )
                        return t->sName;
                // we have found a text, but there is no ship name ??
                else if ( lang != 44 )
                {
                        // reget the english one
                        t = FindShipText(44);
                        if ( t && !t->sName.empty() )
                                return t->sName;
                }
        }

        // still not found one, return the ships name
        return this->GetShipName(lang);
}

Utils::String CXspFile::GetTextDescription(int lang)
{
        SText *t = FindShipText(lang);
        if ( t )
        {
                // return the correct language text
                if ( !t->sDesc.empty() )
                        return t->sDesc;
                // we have found a text, but there is no ship name ??
                else if ( lang != 44 )
                {
                        // reget the english one
                        t = FindShipText(44);
                        if ( t && !t->sDesc.empty() )
                                return t->sDesc;
                }
        }

        // still not found one, return the ships name
        if ( !this->description().empty() ) return this->description();
        return this->GetShipName(lang);
}

bool CXspFile::startExtractShip(CVirtualFileSystem *pVfs, const Utils::String &sId, CProgressInfo *pProgress)
{
        m_sID = sId.remove('\r');
        while ( m_sID.right(1) == ";" )
                m_sID.truncate(-1);

        m_sData = pVfs->getTShipsEntry(m_sID);

        // get scene files
        if ( pProgress ) pProgress->UpdateStatus(IMPORTSHIP_SCENE);
        if ( !this->extractSceneFiles(pVfs) )
                return false;

        return true;
}

bool CXspFile::extractShip(CVirtualFileSystem *pVfs, const Utils::String &sId, CProgressInfo *progress)
{
        if ( !this->startExtractShip(pVfs, sId, progress) )
                return false;

        // read the scene file and get the files list
        if ( !this->processSceneFiles(pVfs, progress) )
                return false;

        // pack all the ship files
        this->PackAllFiles();

        return true;
}

bool CXspFile::extractSceneFiles(CVirtualFileSystem *pVfs)
{
        m_pSceneFile = pVfs->extractGameFileToPackage(this, "objects\\" + m_sData.token(";", 17) + ".pbd", FILETYPE_SHIPSCENE, "objects\\" + m_sData.token(";", 17) + ".bod");
        if ( !m_pSceneFile ) return false;
        m_pCockpitFile = pVfs->extractGameFileToPackage(this, "objects\\" + m_sData.token(";", 18) + ".pbd", FILETYPE_COCKPITSCENE, "objects\\" + m_sData.token(";", 18) + ".bod");

        return true;
}

CyStringList *CXspFile::ReadSceneModels()
{
        // read the scene file
        if ( !m_pSceneFile )
                m_pSceneFile = this->GetFirstFile(FILETYPE_SHIPSCENE);

        if ( !m_pSceneFile )
                return NULL;

        // check if its packed
        size_t datasize;
        unsigned char *data = m_pSceneFile->UncompressData((long *)&datasize, 0);
        
        // if data wasn't compressed, then copy it itself as we are editing it
        bool deleteData = false;
        if ( data == m_pSceneFile->GetData() )
        {
                data = new unsigned char[m_pSceneFile->GetDataSize()];
                memcpy(data, m_pSceneFile->GetData(), m_pSceneFile->GetDataSize());
                deleteData = true;
        }

        if ( data && datasize )
        {
                if ( m_pSceneFile->CheckPackedExtension() )
                        data = UnPCKData(data, datasize, &datasize);
        }

        if ( !data || !datasize )
                return NULL;

        CyStringList *lModels = new CyStringList;

        size_t pos = 0;
        bool newline = true;
        bool online = false;
        while ( pos < datasize )
        {
                char c = data[pos];
                ++pos;

                // skip until next line
                if ( !newline && !online )
                {
                        if ( c == '\n' )
                                newline = true;
                        continue;
                }

                if ( newline )
                {
                        if ( c == ' ' || c == 9 || c == '\n' || c == '\r' )
                                continue;

                        newline = false;

                        if ( c == 'P' || c == 'p' )
                        {
                                while ( data[pos] == ' ' ) pos++;
                                unsigned char *line = (data + pos);
                                while ( data[pos] != ' ' && data[pos] != ';' ) pos++;
                                data[pos] = '\0';
                                if ( atoi((const char *)line) == lModels->Count() )
                                        online = true;
                        }
                }
                // this line is out model
                else if ( online )
                {
                        if ( c == 'B' || c == 'b' )
                        {
                                while(data[pos] == ' ') pos++;
                                unsigned char *line = (data + pos);
                                while(data[pos] != ';') pos++;
                                data[pos] = '\0';

                                lModels->PushBack((char *)line);
                                online = false;
                        }
                }
        }

        if ( deleteData )
                delete data;

        return lModels;
}

void CXspFile::extractCutData(CVirtualFileSystem *pVfs, CyStringList *sceneModels, bool add)
{
        std::vector<int> cuts;
        for ( CListNode<SDummy> *node = m_lDummy.Front(); node; node = node->next() ) {
                Utils::String data = node->Data()->sData;
                int states = data.token(";", 3);
                for ( int i = 0; i < states; i++ ) {
                        int cutid = data.token(";", (i * 2) + 5);
                        if ( !cutid ) continue;
                        cuts.push_back(cutid);
                }
        }

        if ( cuts.empty() ) return;

        if ( pVfs->ExtractGameFile("types/CutData.pck", CPackages::tempDirectory() + "tmp.dat").empty() ) return;
        
        CFileIO File(CPackages::tempDirectory() + "tmp.dat");
        if ( !File.exists() ) return;
        
        CyStringList *lines = File.ReadLinesStr();
        int count = -1;
        for ( SStringList *node = lines->Head(); node; node = node->next ) {
                Utils::String line = node->str.ToString();
                line.removeChar('\r');
                line.removeChar(' ');
                line.removeFirstSpace();
                if ( line[0] == '/' ) continue;
                if ( count == -1 ) count = line.token(";", 1);
                else {
                        int max;
                        Utils::String *words = line.tokenise(";", &max);
                        if ( words && max ) {
                                for ( int i = 0; i < max; i += 2 ) {
                                        int cutid = words[i];
                                        if ( !cutid ) continue;
                                        for ( std::vector<int>::iterator itr = cuts.begin(); itr != cuts.end(); itr++ ) {
                                                if ( (*itr) == cutid ) {
                                                        this->AddCutData(words[i] + ";" + words[i + 1] + ";");
                                                        if ( add ) {
                                                                sceneModels->PushBack(CyString(words[i + 1]));
                                                        }
                                                        break;
                                                }
                                        }
                                }
                                CLEANSPLIT(words, max);
                        }
                }
        }
}

void CXspFile::extractDummies(CVirtualFileSystem *pVfs, CyStringList *sceneModels, bool add)
{
        if ( !sceneModels ) return;

        bool extracted = false;
        if ( !pVfs->ExtractGameFile("types/dummies.pck", CPackages::tempDirectory() + "tmp.dat").empty() ) {
                CFileIO File(CPackages::tempDirectory() + "tmp.dat");
                if ( File.exists() )
                {
                        Utils::String section;
                        int secCount = 0;
                        CyStringList *lines = File.ReadLinesStr();
                        for ( SStringList *node = lines->Head(); node; node = node->next )
                        {
                                node->str.RemoveFirstSpace();
                                node->str.RemoveChar(9);
                                node->str.RemoveChar('\r');
                                if ( node->str.Empty() )
                                        continue;
                                if ( node->str[0] == '/' )
                                        continue;

                                // not in a section yet
                                if ( secCount <= 0 )
                                {
                                        section = node->str.GetToken(";", 1, 1).ToString();
                                        secCount = node->str.GetToken(";", 2, 2).ToInt();
                                }
                                else
                                {
                                        Utils::String first = node->str.GetToken(";", 1, 1).ToString();
                                        if ( sceneModels->FindString(first) )
                                        {
                                                this->AddDummy(section, node->str.ToString());

                                                if ( add )
                                                {
                                                        int pos = 4;
                                                        int scene = node->str.GetToken(";", 3, 3).ToInt();
                                                        for ( int i = 0; i < scene; i++ )
                                                        {
                                                                sceneModels->PushBack(node->str.GetToken(";", 5 + (i * 2), 5 + (i * 2)));
                                                                pos += 2;
                                                        }

                                                        int model = node->str.GetToken(";", pos, pos).ToInt();
                                                        for ( int i = 0; i < model; i++ )
                                                                sceneModels->PushBack(node->str.GetToken(";", pos + (i * 2) + 1, pos + (i * 2) + 1));
                                                }
                                        }
                                        --secCount;

                                }
                        }

                        delete lines;
                        File.remove();
                }
        }
}

void CXspFile::extractComponants(CVirtualFileSystem *pVfs, CyStringList *sceneModels)
{
        if ( !sceneModels ) return;
        if ( !pVfs->ExtractGameFile("types/components.pck", CPackages::tempDirectory() + "tmp.dat").empty() )
        {
                CFileIO File(CPackages::tempDirectory() + "tmp.dat");
                if ( File.exists() )
                {
                        Utils::String file;
                        Utils::String section;
                        int secCount = 0;
                        int secCount2 = 0;
                        CyStringList *lines = File.ReadLinesStr();
                        for ( SStringList *node = lines->Head(); node; node = node->next )
                        {
                                node->str.RemoveFirstSpace();
                                node->str.RemoveChar(9);
                                node->str.RemoveChar('\r');
                                if ( node->str.Empty() )
                                        continue;
                                if ( node->str[0] == '/' )
                                        continue;

                                // not in a section yet
                                if ( secCount2 )
                                {
                                        if ( sceneModels->FindString(file) )
                                                this->AddComponent(section, file, node->str.ToString());
                                        --secCount2;
                                }
                                else if ( secCount <= 0 )
                                {
                                        section = node->str.GetToken(";", 1, 1).ToString();
                                        secCount = node->str.GetToken(";", 2, 2).ToInt();
                                }
                                else
                                {
                                        file = node->str.GetToken(";", 1, 1).ToString();
                                        secCount2 = node->str.GetToken(";", 2, 2).ToInt();
                                        --secCount;
                                }
                        }

                        delete lines;
                        File.remove();
                }
        }
}

bool CXspFile::GetTextureList(CyStringList *list, const unsigned char *olddata, size_t size)
{
        if ( !olddata || !size )
                return false;

        size_t startLine = 0;
        Utils::String line;
        size_t pos = 0;

        unsigned char *data = new unsigned char[size];
        memcpy(data, olddata, size);

        while ( (pos++) < size )
        {
                if ( data[pos] == '\n' )
                {
                        data[pos] = '\0';
                        line = (char *)(data + startLine);
                        line.removeChar(9);
                        line.removeChar('\r');
                        line.removeFirstSpace();

                        if ( !line.empty() && line[0] != '/' )
                        {
                                Utils::String first = line.token(":", 1);
                                if ( first.Compare("MATERIAL6") )
                                {
                                        int max;
                                        Utils::String material = line.tokens(":", 2);
                                        Utils::String *strs = material.tokens(";", 6).tokenise("; ", &max);

                                        int num = material.token(";", 5);
                                        if ( strs && max >= 2)
                                        {
                                                for(int i = 0; i < max; i++) {
                                                        Utils::String type = strs[i].token(";", 1);
                                                        Utils::String valtype = strs[i].token(";", 2);                  
                                                        if ( valtype.Compare("SPTYPE_STRING") ) {
                                                                Utils::String file = strs[i].token(";", 3);
                                                                list->PushBack(CyString(file), "", true);
                                                        }
                                                }
                                        }

                                        CLEANSPLIT(strs, max)
                                }
                        }

                        startLine = pos + 1;
                }
        }
        
        delete [] data;

        return true;
}

void CXspFile::extractTextures(CVirtualFileSystem *pVfs)
{
        CyStringList lTextures;

        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() != FILETYPE_SHIPMODEL )
                        continue;

                bool deleteData = false;
                unsigned char *data = NULL;
                size_t size;

                // cant do these yet
                if ( f->CheckFileExt("pbb") || f->CheckFileExt("bob") )
                {
                        data = f->BobDecompile(&size);
                        if ( !data || !size ) {
                                // search for the pbd or bod files that match and use them instead
                                Utils::String file;

                                file = CFileIO(f->GetNameDirectory(NULL)).ChangeFileExtension("pbd").ToString();
                                file = pVfs->ExtractGameFile(file, CPackages::tempDirectory() + "tmp.tmp");
        
                                if ( file.empty() ) {
                                        file = CFileIO(f->GetNameDirectory(NULL)).ChangeFileExtension("bod").ToString();
                                        file = pVfs->ExtractGameFile(file, CPackages::tempDirectory() + "tmp.tmp");
                                }

                                if ( !file.empty() ) {
                                        CFileIO File(file);
                                        if ( File.exists() ) {
                                                data = File.readAll(&size);
                                                File.remove();
                                        }
                                }

                                if ( data && size ) 
                                        this->GetTextureList(&lTextures, data, size);
                                continue;
                        }

                        deleteData = true;
                }

                if ( !data || !size ) {
                        if ( !f->GetData() )
                        {
                                if ( !f->ReadFromFile() )
                                        continue;
                        }


                        if ( f->CheckFileExt("pbb") || f->CheckFileExt("pbd") )
                                data = f->UnPCKFile(&size);
                        else
                        {
                                data = f->GetData();
                                size = f->GetDataSize();
                        }
                }
                this->GetTextureList(&lTextures, data, size);

                if ( deleteData ) delete data;
        }

        for ( SStringList *node = lTextures.Head(); node; node = node->next )
        {
                CFileIO F(node->str);
                if ( F.CheckFileExtension("fx") ) {
                        /*
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\1_1\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\1_1\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\1_4\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\1_4\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\2_0\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\2_0\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\2_a\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\2_a\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\2_b\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\2_b\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\3_0\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                pVfs->extractGameFileToPackage(this, "shader\\3_0\\" + CFileIO(node->str.ToString()).ChangeFileExtension("fb").ToString(), FILETYPE_SHIPOTHER);
                                */
                }
                else {
                        if ( pVfs->extractGameFileToPackage(this, "dds\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "dds\\" + Utils::String(node->str.ToString()).token(".", -1), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "dds\\" + Utils::String(node->str.ToString()) + ".dds", FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "dds\\" + Utils::String(F.ChangeFileExtension("dds").ToString()), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "textures\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "textures\\" + Utils::String(node->str.ToString()).token(".", -1), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "textures\\" + Utils::String(node->str.ToString()) + ".jpg", FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, "textures\\" + Utils::String(F.ChangeFileExtension("jpg").ToString()), FILETYPE_SHIPOTHER) )
                                continue;
                }
        }
}

bool CXspFile::AddTextFromFile(const Utils::String &sFile, int textId)
{
        Utils::String file = sFile;

        bool remove = false;
        if ( CFileIO(file).CheckFileExtension("pck") )
        {
                C_File F;
                F.SetFilename(CyString(file));
                F.UnPCKFile();
                if (F.writeToFile(CPackages::tempDirectory() + "tmp.dat"))
                {
                        remove = true;
                        file = CPackages::tempDirectory() + "tmp.dat";
                }
        }

        /*
        std::wfstream fileStream(file.c_str());
        if ( fileStream.is_open() ) {
                while(!fileStream.eof()) {
                        std::wstring line;
                        std::getline(fileStream, line);
                        int i =0;
                }
                fileStream.close();
        }
        */

        CFileIO F(file);
        if ( F.exists() && F.startRead() ) {
                bool ret = this->_addTextFromFile(F, textId);
                F.close();

                if ( remove ) CFileIO::Remove(file);
                _changed();
                return ret;
        }

        return false;
}

bool CXspFile::ImportBodies(CyStringList *sceneModels, const Utils::String &filename)
{
        CFileIO File(filename);
        if ( File.exists() )
        {
                Utils::String sSection;
                int section = 0;
                CyStringList *lines = File.ReadLinesStr();
                for ( SStringList *node = lines->Head(); node; node = node->next )
                {
                        node->str.RemoveChar(9);
                        node->str.RemoveChar('\r');
                        node->str.RemoveFirstSpace();
                        if ( node->str.Empty() )
                                continue;
                        if ( node->str[0] == '/' )
                                continue;
                        
                        // are we looking for a section
                        if ( section <= 0 )
                        {
                                sSection = node->str.GetToken(";", 1, 1).ToString();
                                section = node->str.GetToken(";", 2, 2).ToInt();
                        }
                        else
                        {
                                int max;
                                CyString *strs = node->str.SplitToken(";", &max);
                                if ( strs && max )
                                {
                                        for ( int i = 0; i < max; i++ )
                                        {
                                                strs[i].RemoveSpaces();
                                                if (strs[i].Empty() )
                                                        continue;
                                                if ( sceneModels->FindString(strs[i]) )
                                                        this->AddBody(sSection, strs[i].ToString());
                                                --section;
                                        }
                                }
                                CLEANSPLIT(strs, max)
                        }
                }
                delete lines;
                return true;
        }

        return false;
}

bool CXspFile::ImportCockpits(const Utils::String &filename)
{
        CFileIO File(filename);
        if ( File.exists() )
        {
                CyStringList *lines = File.ReadLinesStr();
                int entries = 0;
                for ( SStringList *node = lines->Head(); node; node = node->next )
                {
                        node->str.RemoveChar(9);
                        node->str.RemoveChar('\r');
                        node->str.RemoveFirstSpace();
                        if ( node->str.Empty() )
                                node->remove = true;
                        else if ( node->str[0] == '/' )
                                node->remove = true;
                        else if ( !entries )
                        {
                                entries = node->str.GetToken(";", 2, 2).ToInt();
                                node->remove = true;
                        }
                }
                lines->RemoveMarked();

                // now get all the entries from TShips
                for ( int i = 0; i < 6; i++ )
                {
                        int idx = m_sData.token(";", 32 + (i * 2));
                        if ( idx < lines->Count() && idx )
                        {
                                Utils::String turret = lines->GetAt(idx)->str.ToString();
                                int pos = -1;
                                Utils::String id;
                                while ( id.empty() && pos > -100 ) id = turret.token(";", pos--);
                                m_sData = m_sData.replaceToken(";", 32 + (i * 2), id + "(" + (long)idx + ")");

                                this->AddCockpit(turret, 0);
                        }
                }

                delete lines;
                return true;
        }
        return false;
}

bool CXspFile::extractCockpits(CVirtualFileSystem *pVfs)
{
        if ( !pVfs->ExtractGameFile("types/TCockpits.pck", CPackages::tempDirectory() + "tmp.dat").empty() ) {
                bool ret = this->ImportCockpits(CPackages::tempDirectory() + "tmp.dat");
                CFileIO::Remove(CPackages::tempDirectory() + "tmp.dat");

                return ret;
        }

        return false;
}

bool CXspFile::extractBodies(CVirtualFileSystem *pVfs, CyStringList *sceneModels)
{
        if ( !sceneModels ) return false;

        if ( !pVfs->ExtractGameFile("types/Bodies.pck", CPackages::tempDirectory() + "tmp.dat").empty() ) {
                bool ret = this->ImportBodies(sceneModels, CPackages::tempDirectory() + "tmp.dat");
                CFileIO::Remove(CPackages::tempDirectory() + "tmp.dat");

                return ret;
        }

        return false;
}

bool CXspFile::_addTextFromFile(CFileIO &F, int textId)
{
        if ( textId == -1 && !m_sData.empty() )
                textId = m_sData.token(";", 7);

        if ( textId <= 0 )
                return false;

        if ( !F.isOpened() )
                return false;

        bool added = false;

        Utils::String shipName;
        Utils::String shipDesc;

        int lastAddedGameID = 0;
        int currentGameID = 0;
        int lang = 0;
        bool inpage = false;

        while(!F.atEnd()) {
                if ( !shipName.empty() && !shipDesc.empty() )
                {
                        added = true;
                        break;
                }

                Utils::String line = F.readEndOfLine();
                line.removeChar(9);
                line.removeChar('\r');
                line.removeFirstSpace();
        
                if ( inpage )
                {
                        if ( line.left(6).Compare("</page") ) {
                                inpage = false;
                                continue;
                        }

                        // find matching id
                        if ( line.left(6).Compare("<t id=") )
                        {
                                int pos = line.findPos("id=\"", 0);
                                if ( pos != -1 )
                                {
                                        pos += 4;
                                        int endpos = line.findPos("\"", pos);
                                        if ( endpos != -1 )
                                        {
                                                int id = line.mid(pos, endpos);
                                                if ( id == textId || id == (textId + 1) )
                                                {
                                                        pos = line.findPos(">", endpos);
                                                        if ( pos != -1 )
                                                        {
                                                                endpos = line.findPos("</t>", pos);
                                                                if ( endpos != -1 )
                                                                {
                                                                        if ( id == textId )
                                                                                shipName = line.mid(pos + 1, endpos);
                                                                        else
                                                                                shipDesc = line.mid(pos + 1, endpos);
                                                                        lastAddedGameID = currentGameID;
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
                else if ( lang ) // search for page 17
                {
                        if ( line.left(8).Compare("<page id") )
                        {
                                int pos = line.findPos("id=\"");
                                if ( pos != -1 )
                                {
                                        pos += 4;
                                        int endpos = line.findPos("\"", pos);
                                        if ( endpos != -1 )
                                        {
                                                Utils::String sId = line.mid(pos, endpos);
                                                int id = sId;
                                                if ( sId.length() > 4 ) {
                                                        id = sId.right(4);
                                                        currentGameID = sId.left(sId.length() - 4);
                                                }

                                                if ( currentGameID >= lastAddedGameID && id == 17 )
                                                        inpage = true;
                                        }
                                }
                        }
                }
                else if ( line.left(12).Compare("<language id") )
                {
                        int pos = line.findPos("id=\"");
                        if ( pos != -1 )
                        {
                                // move past the id=
                                pos += 4;

                                Utils::String s = line.right(-pos);
                                Utils::String s2 = line.mid(pos, -1);
                                int endpos = line.findPos("\"", pos);
                                if ( endpos != -1 )
                                        lang = line.mid(pos, endpos);
                        }
                }
        }

        // incase we only found the shipname
        if ( !shipName.empty() )
                added = true;

        if ( added )
        {
                if ( lang == 44 || this->name().empty())
                {
                        this->setName(shipName);
                        this->setDescription(shipDesc.findReplace("&amp", "&"));
                }
                this->AddText(lang, shipName, shipDesc);
                return true;    
        }

        return false;
}

void CXspFile::ExtractTexts(CCatFile *catFile, CCatFile *secondCatFile, int textId)
{
        for (auto itr = catFile->GetFiles()->begin(); itr != catFile->GetFiles()->end(); itr++ )
        {
                SInCatFile *f = *itr;
                if ( !f->sFile.left(2).Compare("t\\") && !f->sFile.left(2).Compare("t/") )
                        continue;

                // extract the text file and read in the data
                bool extracted = catFile->ExtractFile(f->sFile, CPackages::GetTempDirectory() + "/tmp.dat");
                if ( !extracted && secondCatFile ) extracted = secondCatFile->ExtractFile(f->sFile, CPackages::GetTempDirectory() + "/tmp.dat");
                if ( extracted ) {
                        this->AddTextFromFile(CPackages::tempDirectory() + "/tmp.dat", textId);
                        CFileIO::Remove(CPackages::tempDirectory() + "/tmp.dat");
                }
        }
}

bool CXspFile::processSceneFileSection(int section, CVirtualFileSystem *pVfs, CyStringList *lModels, CProgressInfo *progress)
{
        if ( progress ) progress->UpdateStatus(section);

        switch ( section )
        {
                case IMPORTSHIP_COMPONANT:
                        if ( !lModels ) return false;
                        this->extractComponants(pVfs, lModels);
                        break;

                case IMPORTSHIP_MODELS:
                        {
                                if ( !lModels ) return false;
                                for ( SStringList *node = lModels->Head(); node; node = node->next )
                                {
                                        if ( node->str.IsNumber() ) // count be componants or dummy
                                                continue;
                                        if ( pVfs->extractGameFileToPackage(this, "objects\\" + Utils::String(node->str.ToString()) + ".pbb", FILETYPE_SHIPMODEL, "objects\\" + Utils::String(node->str.ToString()) + ".bob") )
                                                continue;
                                        if ( pVfs->extractGameFileToPackage(this, "objects\\" + Utils::String(node->str.ToString()) + ".pbd", FILETYPE_SHIPMODEL, "objects\\" + Utils::String(node->str.ToString()) + ".bod") )
                                                continue;
                                }
                        }
                        break;

                case IMPORTSHIP_DUMMIES:
                        if ( !lModels ) return false;
                        this->extractDummies(pVfs, lModels, true);
                        this->extractCutData(pVfs, lModels, true);
                        break;

                // extract the textures
                case IMPORTSHIP_TEXTURES:
                        this->extractTextures(pVfs);
                        break;

                case IMPORTSHIP_TEXTS:
                        // extract the text file entries
                        pVfs->extractTexts(this, m_sData.token(";", 7));
                        break;

                case IMPORTSHIP_BODIES:
                        // extract the bodies entries
                        if ( !lModels ) return false;
                        this->extractBodies(pVfs, lModels);
                        break;

                case IMPORTSHIP_COCKPITS:
                        // extract the cockpit entries
                        this->extractCockpits(pVfs);
                        break;
        }

        return true;
}

bool CXspFile::processSceneFiles(CVirtualFileSystem *pVfs, CProgressInfo *progress)
{
        // now lets parse our files
        if ( progress ) progress->UpdateStatus(IMPORTSHIP_EXTRACTSCENE);
        CyStringList *lModels = this->ReadSceneModels();
        if ( !lModels )
                return false;

        // extract componants, and add extra items to list
        if ( !this->processSceneFileSection(IMPORTSHIP_COMPONANT, pVfs, lModels, progress) )
                return false;

        //lets first find any model files
        if ( !this->processSceneFileSection(IMPORTSHIP_MODELS, pVfs, lModels, progress) )
                return false;

        // extract the dummies
        if ( !this->processSceneFileSection(IMPORTSHIP_DUMMIES, pVfs, lModels, progress) )
                return false;

        // extract the textures
        if ( !this->processSceneFileSection(IMPORTSHIP_TEXTURES, pVfs, lModels, progress) )
                return false;

        // extract the text file entries
        if ( !this->processSceneFileSection(IMPORTSHIP_TEXTS, pVfs, lModels, progress) )
                return false;

        // extract the bodies entries
        if ( !this->processSceneFileSection(IMPORTSHIP_BODIES, pVfs, lModels, progress) )
                return false;

        // extract the cockpit entries
        if ( !this->processSceneFileSection(IMPORTSHIP_COCKPITS, pVfs, lModels, progress) )
                return false;

        delete lModels;

        return true;
}


void CXspFile::PackAllFiles()
{
        for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
        {
                C_File *f = node->Data();
                if ( f->GetFileType() == FILETYPE_SHIPSCENE || f->GetFileType() == FILETYPE_COCKPITSCENE || f->GetFileType() == FILETYPE_SHIPMODEL || f->GetFileType() == FILETYPE_SHIPOTHER )
                {       
                        /*
                        if ( f->CheckFileExt("bod") )
                        {
                                if ( f->PCKFile() )
                                        f->ChangeFileExt("pbd");
                        }       

                        else */if ( f->CheckFileExt("bob") )
                        {
                                if ( f->PCKFile() )
                                        f->ChangeFileExt("pbb");
                        }
                }
        }
}

void CXspFile::AdjustCockpits()
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                SCockpit *c = node->Data();
                if ( c->iIndex < 0 )
                        continue;

                Utils::String id = c->sCockpit.token(";", 19);

                for ( int i = 0; i < 6; i++ )
                {
                        Utils::String tId = m_sData.token(";", 32 + (i * 2));
                        if ( tId.isNumber() && ((int)tId) == c->iIndex )
                                m_sData = m_sData.replaceToken(";", 32 + (i * 2), (id + "(" + Utils::String::Number(c->iIndex) + ")").c_str());
                }
        }
}

Utils::String CXspFile::FormatShipData(CyStringList *cockpits, int *text, int game)
{
        Utils::String data = (game == GAME_X3) ? this->GetX3ShipData() : this->GetTCShipData();
        // do turrets
        for ( int t = 0; t < 6; t++ )
        {
                int oldPos = 0;
                Utils::String turret = data.token(";", 32 + (t * 2));
                if ( !turret.isNumber() )
                {
                        if ( turret.isin("(") )
                        {
                                oldPos = turret.tokens("(", 2).token(")", 1);
                                turret = turret.token("(", 1);
                        }
                        int pos = cockpits->FindStringPos(turret);
                        if ( pos < 0 )  pos = oldPos;
                        if ( pos >= cockpits->Count() ) pos = 0;
                        data = data.replaceToken(";", 32 + (t * 2), (long)pos);
                }
        }
        // adjust the weapons
        int mask = this->GetLaserMask(game - 1);
        if ( mask != -1 )
                data = data.replaceToken(";", 19, (long)mask);
        mask = this->GetMissileMask(game - 1);
        if ( mask != -1 )
                data = data.replaceToken(";", 25, (long)mask);

        // fix the ship text
        if ( m_iOrgDesc > 0 )
                (*text) = m_iOrgDesc;
        data = data.replaceToken(";", 7, (long)*text);

        // add the ware id
        data.removeChar(9);
        data.removeEndSpace();
        // remove the end ;
        while ( data.right(1) == ";" ) data.truncate(-1);
        data = data.replaceToken(";", data.countToken(";"), this->GetShipID());
        if ( data.right(1) != ";" )
                data += ";";

        return data;
}