Subversion Repositories spk

Rev

Rev 264 | 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();
        _lCutData.clear();
        _lBodies.clear();
        _lAnimations.clear();
}

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

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

        ShipyardRace r = ShipyardRace::None;
        _iShipyard = static_cast<unsigned long>(ShipyardRace::None);
}

void CXspFile::addText(int id, const Utils::WString &name, const Utils::WString &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::WString &section, const Utils::WString &data)
{
        SDummy *d = new SDummy;
        d->sData = data;
        d->sSection = section;

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

        m_lDummy.push_back ( d );

        _changed();
}

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

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

        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::WString CXspFile::createValuesLine() const
{
        Utils::WString values = CBaseFile::createValuesLine ();

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

        if ( m_bLanguageText )
                values += L"LanguageText\n";
        if ( m_bExistingShip )
                values += L"ExistingShip\n";
        values += Utils::WString(L"OrgDesc: ") + (long)m_iOrgDesc + L"\n";
        values += Utils::WString(L"Shipyard: ") + Utils::WString::Number(_iShipyard) + L"\n";

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

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

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

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

        for ( CListNode<SWeaponMask> *wNode = m_lWeaponMasks.Front(); wNode; wNode = wNode->next() )
                values += Utils::WString(L"WeaponMask: ") + (long)wNode->Data()->iGame + L" " + (long)wNode->Data()->iMask + L"\n";
        for ( CListNode<SWeaponMask> *mNode = m_lMissileMasks.Front(); mNode; mNode = mNode->next() )
                values += Utils::WString(L"MissileMask: ") + (long)mNode->Data()->iGame + L" " + (long)mNode->Data()->iMask + L"\n";
        for (auto itr = _lCutData.begin(); itr != _lCutData.end(); itr++)
                values += L"CutData: " + (*itr)->str + L"\n";
        for (auto itr = _lBodies.begin(); itr != _lBodies.end(); itr++)
                values += L"Bodies: " + (*itr)->str + L"\n";
        for (auto itr = _lAnimations.begin(); itr != _lAnimations.end(); itr++)
                values += L"Animations: " + (*itr)->str + L"\n";

        return values;
}

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

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

        return true;
}

Utils::WString CXspFile::shipName(int lang)
{
        Utils::WString 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 = this->name(lang);

        return name;
}

//TODO: split this up
bool CXspFile::convertOld(const Utils::WString &file)
{
        // open the file
        FILE *id = _wfopen(file.c_str(), L"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::WString line = Utils::WString::FromString((char *)(compr + start));
                start = pos;
                if ( line.empty() )
                        continue;
                Utils::WString first = line.token(L":", 1);
                Utils::WString rest = line.tokens(L":", 2);
                rest = rest.removeFirstSpace();

                // now check each line
                if ( first.Compare(L"Packager") )
                        packageVersion = rest;
                else if ( first.Compare(L"Shipyard") )
                {
                        std::vector<Utils::WString> strs;
                        if(rest.tokenise(L";", strs))
                        {
                                int cur = 1;
                                for (size_t i = 0; i < strs.size(); i++, cur *= 2 )
                                {
                                        if ( strs[i] == L"1" )
                                                this->addShipyard(static_cast<ShipyardRace>(cur));
                                }
                        }
                }
                else if ( first.Compare(L"ScreenShot") )
                {
                        int size = rest.token(L" ", 1);
                        Utils::WString ext = rest.token(L" ", 2);

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

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

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

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

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

                        C_File *newFile = NULL;
                        int special = 0;
                        if ( file.Compare(L"types/CutData.txt") || file.Compare(L"types/CutData.pck") )
                        {
                                newFile = new C_File(file);
                                special = 1;
                        }
                        else if ( file.Compare(L"types/Bodies.txt") || file.Compare(L"types/Bodies.pck") )
                        {
                                newFile = new C_File(file);
                                special = 2;
                        }
                        else if ( file.Compare(L"types/Animations.txt") || file.Compare(L"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(L" ", 1));
                        }
                        newFile->ReadFromData((char *)(compr + pos), rest.token(L" ", 2));
                        start += (newFile->GetDataSize() + 1);

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

                                // read data into lines
                                Utils::WString data = Utils::WString::FromString((const char *)newFile->GetData());
                                data.removeChar('\r');

                                std::vector<Utils::WString> sLines;
                                if(data.tokenise(L"\n", sLines))
                                {
                                        // cut data
                                        size_t entries = -1;
                                        if ( special == 1 )
                                        {
                                                Utils::WStringList newLines;
                                                for (size_t i = 0; i < sLines.size(); i++)
                                                {
                                                        Utils::WString line = sLines[i];
                                                        line.removeChar(' ');
                                                        line.removeChar(9);
                                                        if ( line.empty() || line[0] == '/' )
                                                                continue;
                                                        if ( entries == -1 )
                                                        {
                                                                entries = line.token(L";", 1).toInt() - 36;
                                                                if ( entries <= 0 )
                                                                        break;
                                                        }
                                                        else
                                                        {
                                                                int id = line.token(L";", 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.size() == entries )
                                                                        break;
                                                        }
                                                }

                                                for(auto itr = newLines.begin(); itr != newLines.end(); itr++)
                                                        this->addCutData((*itr)->str);
                                        }
                                        // bodies
                                        else if ( special == 2 )
                                        {
                                                entries = 0;
                                                Utils::WString section;
                                                for (size_t i = 0; i < sLines.size(); i++ )
                                                {
                                                        Utils::WString line = sLines[i];
                                                        line.removeChar(' ');
                                                        line.removeChar(9);
                                                        if ( line.empty() || line[0] == '/' )
                                                                continue;
                                                        if ( entries <= 0)
                                                        {
                                                                section = line.token(L";", 1);
                                                                entries = line.token(L";", 2).toInt();
                                                        }
                                                        else
                                                        {
                                                                if ( !line.isin(L";") )
                                                                        continue;
                                                                if ( line.countToken(L";") <= 2 )
                                                                {
                                                                        this->addBodies(section + ";" + line.token(L";", 1) + L";");
                                                                        --entries;
                                                                }
                                                                else
                                                                {
                                                                        bool done = false;
                                                                        while (!done)
                                                                        {
                                                                                std::vector<Utils::WString> strs;
                                                                                done = true;
                                                                                line.tokenise(L";", strs);
                                                                                for (size_t j = 0; j < strs.size(); j++)
                                                                                {
                                                                                        if ( !entries )
                                                                                        {
                                                                                                line = line.tokens(L";", static_cast<int>(j + 1));
                                                                                                if ( !line.empty() )
                                                                                                        done = false;
                                                                                                break;
                                                                                        }

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

                                                                                        if (line.empty())
                                                                                                done = true;
                                                                                }
                                                                        }                                                                       
                                                                }
                                                        }
                                                }
                                        }
                                        // animations
                                        else if ( special == 3 )
                                        {
                                                Utils::WStringList in;
                                                for (size_t i = 0; i < sLines.size(); i++)
                                                        in.pushBack(sLines[i]);

                                                Utils::WStringList out;
                                                if ( CXspFile::ReadAnimations(in, out, 87) )
                                                        this->addAnimation(out);
                                        }
                                }
                                delete newFile;
                        }
                }
                else if ( first.Compare(L"Script") )
                {
                        Utils::WString file = rest.words(3);
                        C_File *newFile= this->addFile(file, Utils::WString::Null(), FILETYPE_SCRIPT);
                        newFile->SetCreationTime((long)rest.word(1));
                        newFile->ReadFromData((char *)(compr + pos), rest.word(2));

                        start += (newFile->GetDataSize() + 1);
                }
                else if ( first.Compare(L"Text") )
                        this->addText(rest.token(L":", 1), rest.token(L":", 2), rest.tokens(L":", 3));
                else if ( first.Compare(L"Component") )
                        this->addComponent(rest.token(L";", 1), rest.token(L";", 2), rest.tokens(L";", 3));
                else if ( first.Compare(L"Dummy") )
                {
                        SDummy *d = new SDummy;
                        d->sData = rest.tokens(L";", 2);
                        d->sSection = rest.token(L";", 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(L";", 19));
                this->AddMissileMask(GAME_X3 - 1, m_sData.token(L";", 25));
        }

        return true;
}


unsigned long GetMaxShipyards() { return static_cast<unsigned long>(ShipyardRace::Max); }

Utils::WString GetShipyardName (ShipyardRace s)
{
        switch (s)
        {
        case ShipyardRace::Argon:
                return L"Argon";
        case ShipyardRace::Boron:
                return L"Boron";
        case ShipyardRace::Paranid:
                return L"Paranid";
        case ShipyardRace::Split:
                return L"Split";
        case ShipyardRace::Teladi:
                return L"Teladi";
        case ShipyardRace::Pirates:
                return L"Pirates";
        case ShipyardRace::Friend:
                return L"Friendly";
        case ShipyardRace::Xenon:
                return L"Xenon";
        case ShipyardRace::Terran:
                return L"Terran";
        case ShipyardRace::ATF:
                return L"ATF";
        case ShipyardRace::Goner:
                return L"Goner";
        case ShipyardRace::Yaki:
                return L"Yaki";
        case ShipyardRace::None:
                return L"";
        }

        return L"Unknown";
}

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

bool CXspFile::_checkHeader(const Utils::WString &header) const
{
        if ( header.Compare(L"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;
}

void CXspFile::clearCutData()
{
        _lCutData.clear();
}
void CXspFile::clearAnimations()
{
        _lAnimations.clear();
}
void CXspFile::clearBodies()
{
        _lBodies.clear();
}

Utils::WString CXspFile::shipClass() const
{
        if ( !m_sData.empty() )
                return m_sData.token(L";", TSHIPPOS_CLASS);
        return L"OBJ_SHIP_M5";
}

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

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

        if (_iShipyard)
        {
                list->pushBack(L"# Shipyards, Set which shipyards to add ships for sale to");
                for (unsigned long i = 0; i <= GetMaxShipyards(); i *= 2 )
                {
                        if ( this->isShipyard(static_cast<ShipyardRace>(i)) )
                                list->pushBack(L"Shipyard: " + GetShipyardName(static_cast<ShipyardRace>(i)));
                }
                list->pushBack(L"");
        }

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

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

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

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

        if ( m_lText.size() ) {
                list->pushBack(L"# 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::WString(L"ShipText: ") + (long)node->Data()->iId + L" " + node->Data()->sName + L"|" + node->Data()->sDesc);
                }

                list->pushBack(L"");
        }

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

                list->pushBack(L"");
        }

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

                list->pushBack(L"");
        }

        if ( this->m_lComponent.size() ) {
                list->pushBack(L"# 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::WString(L"Component: ") + node->Data()->sSection + L" " + node->Data()->sSection2 + L" " + node->Data()->sData);
                }
                list->pushBack(L"");
        }

        if ( this->m_lDummy.size() ) {
                list->pushBack(L"# 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(L"Dummy: " + node->Data()->sSection + L" " + node->Data()->sData);
                }
                list->pushBack(L"");
        }

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

                list->pushBack(L"");
        }

        if (!this->_lCutData.empty())
        {
                list->pushBack(L"# Ship Cut Data");
                for(auto itr = _lCutData.begin(); itr != _lCutData.end(); itr++)
                        list->pushBack(L"CutData: " + (*itr)->str);
                list->pushBack(L"");
        }

        if (!this->_lBodies.empty())
        {
                list->pushBack(L"# Ship Bodies");
                for(auto itr = _lBodies.begin(); itr != _lBodies.end(); itr++)
                        list->pushBack(L"Bodies: " + (*itr)->str);
                list->pushBack(L"");
        }

        if (!this->_lAnimations.empty())
        {
                list->pushBack(L"# Ship Animations");
                for (auto itr = _lAnimations.begin(); itr != _lAnimations.end(); itr++)
                        list->pushBack(L"Animation: " + (*itr)->str);   
                list->pushBack(L"");
        }

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

        return true;
}

void CXspFile::_addSection(Utils::CList<STypesSection> &list, const Utils::WString &section, const Utils::WString &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, L"");
}

void CXspFile::_addDataSection(Utils::WStringList& list, Utils::CList<STypesSection>& sectionList, bool bUseFirst)
{
        for(auto itr = list.begin(); itr != list.end(); itr++)
        {
                if (bUseFirst) {
                        Utils::WString data = (*itr)->str;
                        _addSection(sectionList, data.token(L";", 1), data.tokens(L";", 2));
                }
                else {
                        _addSection(sectionList, (*itr)->str, (*itr)->data);
                }
        }
}

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->_lCutData, list, false);
}

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

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


void CXspFile::addGeneratedFiles(HZIP &hz)
{
        CFileIO tmpFile(CPackages::tempDirectory() + L"/temp.tmp");

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

                if ( type.empty() ) continue;

                Utils::WString fname = L"GENERATED/types/" + type + L".txt";
                
                Utils::WString fileData = L"// Exported " + type + L" file, Generated by SPK Libraries V" + Utils::WString::FromFloat(GetLibraryVersion(), 2) + L"\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() ) {
                        */
                ZipAdd(hz, fname.c_str(), (void *)fileData.c_str(), static_cast<unsigned int>(fileData.length() * sizeof(wchar_t)));
                //}
        }
}

bool CXspFile::loadPackageData(const Utils::WString &sFirst, const Utils::WString &sRest, const Utils::WString &sMainGame, Utils::WStringList &otherGames, Utils::WStringList &gameAddons, CProgressInfo *progress)
{
        if ( sFirst.Compare(L"Shipyard") )
        {
                for (unsigned long i = 0; i <= GetMaxShipyards(); i *= 2)
                {
                        if(sRest.Compare(GetShipyardName(static_cast<ShipyardRace>(i))))
                        {
                                this->addShipyard(static_cast<ShipyardRace>(i));
                                break;
                        }
                }
        }
        else if ( sFirst.Compare(L"OriginalDescription") )
                m_iOrgDesc = sRest;
        else if ( sFirst.Compare(L"ShipData") )
                m_sData = sRest;
        else if ( sFirst.Compare(L"ReadData") ) // read data from a tships file
        {
                CPackages p;
                m_sData = p.readShipData(sRest.tokens(L" ", 2), sRest.token(L" ", 1));
        }
        else if ( sFirst.Compare(L"ExistingShip") )
                m_bExistingShip = true;
        else if ( sFirst.Compare(L"ShipID") )
                m_sID = sRest;
        else if ( sFirst.Compare(L"ShipText") )
                this->addText(sRest.token(L" ", 1).toLong(), sRest.tokens(L" ", 2).token(L"|", 1), sRest.tokens(L" ", 2).tokens(L"|", 2));
        else if ( sFirst.Compare(L"Component") )
                this->addComponent(sRest.token(L" ", 1), sRest.token(L" ", 2), sRest.tokens(L" ", 3));
        else if ( sFirst.Compare(L"Cockpit") )
                this->addCockpit(sRest.tokens(L" ", 2).replaceToken(L";", 19, sRest.token(L" ", 1)), 0);
        else if ( sFirst.Compare(L"CockpitWeapon") )
                this->addCockpitWeapon(sRest.token(L" ", 1), sRest.token(L" ", 2), sRest.token(L" ", 3));
        else if ( sFirst.Compare(L"WeaponMask") )
                this->AddWeaponMask(sRest.token(L" ", 1), sRest.token(L" ", 2));
        else if ( sFirst.Compare(L"MissileMask") )
                this->AddMissileMask(sRest.token(L" ", 1), sRest.token(L" ", 2));
        else if ( sFirst.Compare(L"Dummy") )
                this->addDummy(sRest.token(L" ", 1), sRest.token(L" ", 2));
        else if ( sFirst.Compare(L"CutData") )
                this->addCutData(sRest);
        else if ( sFirst.Compare(L"Animation") )
                this->addAnimation(sRest);
        else if ( sFirst.Compare(L"Bodies") )
                this->addBodies(sRest);
        else if ( !CBaseFile::loadPackageData(sFirst, sRest, sMainGame, otherGames, gameAddons, progress) )
        {
                return false;
        }

        return true;
}


Utils::WString CXspFile::getX3ShipData() const
{
        Utils::WString data = m_sData;

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

        Utils::WString sClass = data.token(L";", TSHIPPOS_CLASS);
        if ( !((long)sClass) && sClass != L"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(L";", TSHIPPOS_CLASS, (long)num);
        }

        return data;
}

Utils::WString CXspFile::getTCShipData() const
{
        Utils::WString data = m_sData;

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

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

        return data;
}

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

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

        m_lCockpit.RemoveEmpty();

        return ret;
}

bool CXspFile::removeComponent(const Utils::WString &section1, const Utils::WString &section2, const Utils::WString &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::WString &section, const Utils::WString &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::WString CXspFile::getCockpitData(const Utils::WString &cid)
{
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                Utils::WString id = node->Data()->sCockpit.token(L";", 19);
                if ( id.Compare(cid) )
                        return node->Data()->sCockpit;
        }

        return L"";
}

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

        return nullptr;
}

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

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

bool CXspFile::removeCutData(const Utils::WString &cut)
{
        bool ret = false;
        for(size_t i = 0; i < _lCutData.size(); i++)
        {
                Utils::WString str = _lCutData[i]->str;
                if (str.token(L";", 1).Compare(cut.token(L";", 1)))
                {
                        ret = true;
                        _lCutData.remove(str);
                        break;
                }
        }

        return ret;
}

bool CXspFile::removeBodies(const Utils::WString &cut)
{
        bool ret = false;
        for (auto itr = _lBodies.begin(); itr != _lBodies.end(); itr++)
        {
                if ((*itr)->str.remove(' ').Compare(cut.remove(' ')))
                {
                        _lBodies.remove(itr);
                        return true;
                }
        }

        return false;
}

bool CXspFile::removeAnimation(const Utils::WString &cut)
{
        for(auto itr = _lAnimations.begin(); itr != _lAnimations.end(); itr++)
        {
                if ((*itr)->str.remove(' ').remove('\n').Compare(cut.remove(' ').remove('\n')))
                {
                        _lAnimations.remove(itr);
                        return true;
                }
        }

        return false;
}

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

        return NULL;
}

void CXspFile::addCockpitWeapon(const Utils::WString &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(L";", 9);
        else
                wm->iMask = mask;
        _changed();
}

void CXspFile::addCockpit(const Utils::WString &cockpit, int game, int mask, int index)
{
        Utils::WString cid = cockpit.token(L";", 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::WString &section, const Utils::WString &data)
{
        this->addBodies(section + L";" + data);
        _changed();
}
void CXspFile::addBodies(const Utils::WString &sData)
{
        Utils::WString 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) != L";" )
                        data += L";";
        }
        if(!_lBodies.contains(data))
                _lBodies.pushBack(data);
        _changed();
}

void CXspFile::addCutData(const Utils::WString& data)
{ 
        if (!_lCutData.contains(data))
        {
                _lCutData.pushBack(data);
                _changed();
        }
}


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

        return TAT_NONE;
}

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

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

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

        return data;
}

bool CXspFile::ReadAnimations(const Utils::WStringList &lIn, Utils::WStringList &lOut, int startRecord)
{
        Utils::WString lastComment;
        Utils::WString addEntry;
        int remaining = 0;
        Utils::WString lastType;
        int newEntries = 0;
        int entries = -1;
        for(auto itr = lIn.begin(); itr != lIn.end(); itr++)
        {
                Utils::WString line = (*itr)->str;
                line.removeChar('\r');
                line.removeChar(9);
                if ( line.empty() || line[0] == '/' )
                        continue;
                if ( entries == -1)
                {
                        entries = line.token(L";", 1);
                        newEntries = entries - startRecord;
                }
                else
                {
                        // remove comments, endspaces and last ;
                        Utils::WString sStriped = line;
                        if ( sStriped.contains(L"//") )
                        {
                                if ( entries <= newEntries )
                                        lastComment = L"//" + sStriped.tokens(L"//", 2);
                                sStriped = sStriped.token(L"//", 1);
                        }
                        sStriped.removeEndSpace();
                        if ( sStriped.right(1) == L";" )
                                sStriped.truncate(-1);

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

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

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

                                }
                        }

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

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

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

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

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

        return !lOut.empty();
}

void CXspFile::addAnimation(const Utils::WStringList &list)
{
        for(auto itr = list.begin(); itr != list.end(); itr++)
                this->addAnimation((*itr)->str);
        _changed();
}

void CXspFile::addAnimation(const Utils::WString& data) 
{ 
        if (!_lAnimations.contains(data))
        {
                _lAnimations.pushBack(data);
                _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::WString CXspFile::textName(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->shipName(lang);
}

Utils::WString CXspFile::textDescription(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->shipName(lang);
}

bool CXspFile::startExtractShip(CVirtualFileSystem *pVfs, const Utils::WString &sId, CProgressInfo *pProgress)
{
        m_sID = sId.remove('\r');
        while ( m_sID.right(1) == L";" )
                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::WString &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, L"objects\\" + m_sData.token(L";", 17) + L".pbd", FILETYPE_SHIPSCENE, L"objects\\" + m_sData.token(L";", 17) + L".bod");
        if ( !m_pSceneFile ) return false;
        m_pCockpitFile = pVfs->extractGameFileToPackage(this, L"objects\\" + m_sData.token(L";", 18) + L".pbd", FILETYPE_COCKPITSCENE, L"objects\\" + m_sData.token(L";", 18) + L".bod");

        return true;
}

bool CXspFile::readSceneModels(Utils::WStringList& out)
{
        // read the scene file
        if (!m_pSceneFile)
                m_pSceneFile = this->GetFirstFile(FILETYPE_SHIPSCENE);

        if (!m_pSceneFile)
                return false;

        // 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 false;

        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) == out.size())
                                        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';

                                out.pushBack(Utils::WString::FromString((char*)line));
                                online = false;
                        }
                }
        }

        if (deleteData)
                delete []data;

        return true;
}

void CXspFile::completeFile()
{
        C_File* f = GetFirstFile(FILETYPE_SHIPSCENE);
        if (f)
                m_pSceneFile = f;

        f = GetFirstFile(FILETYPE_COCKPITSCENE);
        if (f)
                m_pCockpitFile = f;
}

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

        if ( cuts.empty() ) return;

        if ( pVfs->extractGameFile(L"types/CutData.pck", CPackages::tempDirectory() + L"/tmp.dat").empty()) return;
        
        CFileIO File(CPackages::tempDirectory() + L"/tmp.dat");
        if ( !File.exists() ) return;
        
        std::vector<Utils::WString> lines;
        File.readLines(lines);
        int count = -1;
        for (auto itr = lines.begin(); itr != lines.end(); itr++)
        {
                Utils::WString line = *itr;
                line.removeChar('\r');
                line.removeChar(' ');
                line.removeFirstSpace();
                if ( line[0] == '/' ) continue;
                if ( count == -1 ) count = line.token(L";", 1);
                else 
                {
                        std::vector<Utils::WString> words;
                        if(line.tokenise(L";", words))
                        {
                                for (size_t i = 0; i < words.size(); 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] + L";" + words[i + 1] + L";");
                                                        if ( add ) 
                                                                sceneModels.pushBack(words[i + 1]);
                                                        break;
                                                }
                                        }
                                }
                        }
                }
        }
}

void CXspFile::extractDummies(CVirtualFileSystem *pVfs, Utils::WStringList& sceneModels, bool add)
{
        if (sceneModels.empty()) return;

        bool extracted = false;
        if ( !pVfs->extractGameFile(L"types/dummies.pck", CPackages::tempDirectory() + L"/tmp.dat").empty()) 
        {
                CFileIO File(CPackages::tempDirectory() + L"/tmp.dat");
                if ( File.exists() )
                {
                        Utils::WString section;
                        int secCount = 0;
                        std::vector<Utils::WString> lines;
                        if (File.readLines(lines))
                        {
                                for(auto itr = lines.begin(); itr != lines.end(); itr++)
                                {
                                        Utils::WString str = itr->remove(9).remove('\r');
                                        str.removeFirstSpace();
                                        if (str.empty())
                                                continue;
                                        if (str[0] == '/')
                                                continue;

                                        // not in a section yet
                                        if (secCount <= 0)
                                        {
                                                section = str.token(L";", 1);
                                                secCount = str.token(L";", 2).toInt();
                                        }
                                        else
                                        {
                                                Utils::WString first = str.token(L";", 1);
                                                if (sceneModels.contains(first))
                                                {
                                                        this->addDummy(section, str);

                                                        if (add)
                                                        {
                                                                int pos = 4;
                                                                int scene = str.token(L";", 3).toInt();
                                                                for (int i = 0; i < scene; i++)
                                                                {
                                                                        sceneModels.pushBack(str.token(L";", 5 + (i * 2)));
                                                                        pos += 2;
                                                                }

                                                                int model = str.token(L";", pos).toInt();
                                                                for (int i = 0; i < model; i++)
                                                                        sceneModels.pushBack(str.token(L";", pos + (i * 2) + 1));
                                                        }
                                                }
                                                --secCount;

                                        }
                                }
                        }

                        File.remove();
                }
        }
}

void CXspFile::extractComponants(CVirtualFileSystem *pVfs, const Utils::WStringList& sceneModels)
{
        if (sceneModels.empty()) return;
        if ( !pVfs->extractGameFile(L"types/components.pck", CPackages::tempDirectory() + L"/tmp.dat").empty())
        {
                CFileIO File(CPackages::tempDirectory() + L"/tmp.dat");
                if ( File.exists() )
                {
                        Utils::WString file;
                        Utils::WString section;
                        int secCount = 0;
                        int secCount2 = 0;
                        std::vector<Utils::WString> lines;
                        if (File.readLines(lines))
                        {
                                for(auto itr = lines.begin(); itr != lines.end(); itr++)
                                {
                                        Utils::WString str = itr->remove(9).remove('\r');
                                        str.removeFirstSpace();
                                        if (str.empty())
                                                continue;
                                        if (str[0] == '/')
                                                continue;

                                        // not in a section yet
                                        if (secCount2)
                                        {
                                                if (sceneModels.contains(file))
                                                        this->addComponent(section, file, str);
                                                --secCount2;
                                        }
                                        else if (secCount <= 0)
                                        {
                                                section = str.token(L";", 1);
                                                secCount = str.token(L";", 2).toInt();
                                        }
                                        else
                                        {
                                                file = str.token(L";", 1);
                                                secCount2 = str.token(L";", 2).toInt();
                                                --secCount;
                                        }
                                }
                        }

                        File.remove();
                }
        }
}

bool CXspFile::getTextureList(Utils::WStringList &list, const unsigned char *olddata, size_t size) const
{
        if ( !olddata || !size )
                return false;

        size_t startLine = 0;
        Utils::WString 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 = Utils::WString::FromString((char *)(data + startLine));
                        line.removeChar(9);
                        line.removeChar('\r');
                        line.removeFirstSpace();

                        if ( !line.empty() && line[0] != '/' )
                        {
                                Utils::WString first = line.token(L":", 1);
                                if ( first.Compare(L"MATERIAL6") )
                                {
                                        Utils::WString material = line.tokens(L":", 2);
                                        std::vector<Utils::WString> strs;
                                        int num = material.token(L";", 5);
                                        if(material.tokens(L";", 6).tokenise(L"; ", strs))
                                        {
                                                for(size_t i = 0; i < strs.size(); i++) {
                                                        Utils::WString type = strs[i].token(L";", 1);
                                                        Utils::WString valtype = strs[i].token(L";", 2);                        
                                                        if ( valtype.Compare(L"SPTYPE_STRING") ) {
                                                                Utils::WString file = strs[i].token(L";", 3);
                                                                if(!list.contains(file))
                                                                        list.pushBack(file, L"");
                                                        }
                                                }
                                        }
                                }
                        }

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

        return true;
}

void CXspFile::extractTextures(CVirtualFileSystem *pVfs)
{
        Utils::WStringList 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(L"pbb") || f->checkFileExt(L"bob"))
                {
                        data = f->BobDecompile(&size);
                        if ( !data || !size ) {
                                // search for the pbd or bod files that match and use them instead
                                Utils::WString file;

                                file = CFileIO(f->getNameDirectory(NULL)).changeFileExtension(L"pbd");
                                file = pVfs->extractGameFile(file, CPackages::tempDirectory() + L"tmp.tmp");
        
                                if ( file.empty() ) {
                                        file = CFileIO(f->getNameDirectory(NULL)).changeFileExtension(L"bod");
                                        file = pVfs->extractGameFile(file, CPackages::tempDirectory() + L"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(L"pbb") || f->checkFileExt(L"pbd") )
                                data = f->UnPCKFile(&size);
                        else
                        {
                                data = f->GetData();
                                size = f->GetDataSize();
                        }
                }
                this->getTextureList(lTextures, data, size);

                if ( deleteData ) delete data;
        }

        for(auto itr = lTextures.begin(); itr != lTextures.end(); itr++)
        {
                CFileIO F((*itr)->str);
                if ( F.isFileExtension(L"fx") ) {
                        /*
                        if ( !pVfs->extractGameFileToPackage(this, "shader\\1_1\\" + Utils::WString(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::WString(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::WString(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::WString(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::WString(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::WString(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, L"dds\\" + (*itr)->str, FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"dds\\" + (*itr)->str.token(L".", -1), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"dds\\" + (*itr)->str + L".dds", FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"dds\\" + F.changeFileExtension(L"dds"), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"textures\\" + (*itr)->str, FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"textures\\" + (*itr)->str.token(L".", -1), FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"textures\\" + (*itr)->str + L".jpg", FILETYPE_SHIPOTHER) )
                                continue;

                        if ( pVfs->extractGameFileToPackage(this, L"textures\\" + F.changeFileExtension(L"jpg"), FILETYPE_SHIPOTHER) )
                                continue;
                }
        }
}

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

        bool remove = false;
        if ( CFileIO(file).isFileExtension(L"pck") )
        {
                C_File F;
                F.setFilename(file);
                F.UnPCKFile();
                if (F.writeToFile(CPackages::tempDirectory() + L"/tmp.dat"))
                {
                        remove = true;
                        file = CPackages::tempDirectory() + L"/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(const Utils::WStringList &sceneModels, const Utils::WString &filename)
{
        CFileIO File(filename);
        if ( File.exists() )
        {
                Utils::WString sSection;
                int section = 0;
                std::vector<Utils::WString> lines;
                if (File.readLines(lines))
                {
                        for(auto itr = lines.begin(); itr != lines.end(); itr++)
                        {
                                Utils::WString l = *itr;
                                l.removeChar(9);
                                l.removeChar('\r');
                                l.removeFirstSpace();
                                if (l.empty())
                                        continue;
                                if (l[0] == '/')
                                        continue;

                                // are we looking for a section
                                if (section <= 0)
                                {
                                        sSection = l.token(L";", 1);
                                        section = l.token(L";", 2).toInt();
                                }
                                else
                                {
                                        std::vector<Utils::WString> strs;
                                        if(l.tokenise(L";", strs))
                                        {
                                                for (size_t i = 0; i < strs.size(); i++)
                                                {
                                                        strs[i].removeEndSpace();
                                                        strs[i].removeFirstSpace();
                                                        if (strs[i].empty())
                                                                continue;
                                                        if (sceneModels.contains(strs[i]))
                                                                this->addBody(sSection, strs[i]);
                                                        --section;
                                                }
                                        }
                                }
                        }
                        return true;
                }
        }

        return false;
}

bool CXspFile::ImportCockpits(const Utils::WString &filename)
{
        CFileIO File(filename);
        if ( File.exists() )
        {
                std::vector<Utils::WString> lines;
                if (File.readLines(lines))
                {
                        int entries = 0;
                        auto itr = lines.begin();
                        while(itr != lines.end())
                        {
                                Utils::WString str = itr->remove(9).remove('\r');
                                str.removeFirstSpace();
                                if (str.empty())
                                        itr = lines.erase(itr);
                                else if (str[0] == '/')
                                        itr = lines.erase(itr);
                                else if (!entries)
                                {
                                        entries = str.token(L";", 2).toInt();
                                        itr = lines.erase(itr);
                                }
                                else
                                        itr++;
                        }

                        // now get all the entries from TShips
                        for (int i = 0; i < 6; i++)
                        {
                                int idx = m_sData.token(L";", 32 + (i * 2));
                                if (static_cast<size_t>(idx) < lines.size() && idx)
                                {
                                        Utils::WString turret = lines[idx];
                                        int pos = -1;
                                        Utils::WString id;
                                        while (id.empty() && pos > -100) id = turret.token(L";", pos--);
                                        m_sData = m_sData.replaceToken(L";", 32 + (i * 2), id + L"(" + (long)idx + L")");

                                        this->addCockpit(turret, 0);
                                }
                        }
                }
                return true;
        }
        return false;
}

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

                return ret;
        }

        return false;
}

bool CXspFile::extractBodies(CVirtualFileSystem *pVfs, const Utils::WStringList &sceneModels)
{
        if (sceneModels.empty()) return false;

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

                return ret;
        }

        return false;
}

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

        if ( textId <= 0 )
                return false;

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

        bool added = false;

        Utils::WString shipName;
        Utils::WString 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::WString line = F.readEndOfLine();
                line.removeChar(9);
                line.removeChar('\r');
                line.removeFirstSpace();
        
                if ( inpage )
                {
                        if ( line.left(6).Compare(L"</page") ) {
                                inpage = false;
                                continue;
                        }

                        // find matching id
                        if ( line.left(6).Compare(L"<t id=") )
                        {
                                int pos = line.findPos(L"id=\"", 0);
                                if ( pos != -1 )
                                {
                                        pos += 4;
                                        int endpos = line.findPos(L"\"", pos);
                                        if ( endpos != -1 )
                                        {
                                                int id = line.mid(pos, endpos);
                                                if ( id == textId || id == (textId + 1) )
                                                {
                                                        pos = line.findPos(L">", endpos);
                                                        if ( pos != -1 )
                                                        {
                                                                endpos = line.findPos(L"</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(L"<page id") )
                        {
                                int pos = line.findPos(L"id=\"");
                                if ( pos != -1 )
                                {
                                        pos += 4;
                                        int endpos = line.findPos(L"\"", pos);
                                        if ( endpos != -1 )
                                        {
                                                Utils::WString sId = line.mid(pos, endpos);
                                                int id = sId;
                                                if ( sId.length() > 4 ) {
                                                        id = sId.right(4);
                                                        currentGameID = sId.left(static_cast<long>(sId.length() - 4));
                                                }

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

                                Utils::WString s = line.right(-pos);
                                Utils::WString s2 = line.mid(pos, -1);
                                int endpos = line.findPos(L"\"", 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(L"&amp", L"&"));
                }
                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(L"t\\") && !f->sFile.left(2).Compare(L"t/") )
                        continue;

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

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

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

                case IMPORTSHIP_MODELS:
                        {
                                if (lModels.empty()) return false;
                                for(auto itr = lModels.begin(); itr != lModels.end(); itr++)
                                {
                                        if ((*itr)->str.isNumber() ) // count be componants or dummy
                                                continue;
                                        if ( pVfs->extractGameFileToPackage(this, L"objects\\" + (*itr)->str + L".pbb", FILETYPE_SHIPMODEL, L"objects\\" + (*itr)->str + L".bob") )
                                                continue;
                                        if ( pVfs->extractGameFileToPackage(this, L"objects\\" + (*itr)->str + ".pbd", FILETYPE_SHIPMODEL, L"objects\\" + (*itr)->str + L".bod") )
                                                continue;
                                }
                        }
                        break;

                case IMPORTSHIP_DUMMIES:
                        if (lModels.empty()) 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(L";", 7));
                        break;

                case IMPORTSHIP_BODIES:
                        // extract the bodies entries
                        if (lModels.empty()) 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);
        Utils::WStringList lModels;
        if(!readSceneModels(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;

        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(L"bob") )
                        {
                                if ( f->PCKFile() )
                                        f->changeFileExt(L"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::WString id = c->sCockpit.token(L";", 19);

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

Utils::WString CXspFile::formatShipData(const Utils::WStringList &cockpits, int *text, int game)
{
        Utils::WString data = (game == GAME_X3) ? this->getX3ShipData() : this->getTCShipData();
        // do turrets
        for ( int t = 0; t < 6; t++ )
        {
                int oldPos = 0;
                Utils::WString turret = data.token(L";", 32 + (t * 2));
                if ( !turret.isNumber() )
                {
                        if ( turret.contains(L"(") )
                        {
                                oldPos = turret.tokens(L"(", 2).token(L")", 1);
                                turret = turret.token(L"(", 1);
                        }
                        size_t pos = cockpits.findPos(turret);
                        if ( pos < 0 )  pos = oldPos;
                        if (static_cast<size_t>(pos) >= cockpits.size()) pos = 0;
                        data = data.replaceToken(L";", 32 + (t * 2), (long)pos);
                }
        }
        // adjust the weapons
        int mask = this->GetLaserMask(game - 1);
        if ( mask != -1 )
                data = data.replaceToken(L";", 19, (long)mask);
        mask = this->GetMissileMask(game - 1);
        if ( mask != -1 )
                data = data.replaceToken(L";", 25, (long)mask);

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

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

        return data;
}