Subversion Repositories spk

Rev

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

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

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

        m_iType = TYPE_XSP;
}

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

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

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

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

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

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

        m_iShipyard = SHIPYARD_NONE;
}

void CXspFile::AddText ( int id, CyString name, CyString 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;

        m_bChanged = true; 
}

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

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

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

        m_lDummy.push_back ( d );

        m_bChanged = true; 
}

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

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

        m_lComponent.push_back ( c );

        m_bChanged = true; 
}

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;
        m_bChanged = true; 
}
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;
        m_bChanged = true; 
}

bool CXspFile::IsValid ()
{
        if ( m_sName.Empty() )
                return false;
        if ( m_sAuthor.Empty() )
                return false;

        return true;
}

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

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

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

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

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

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

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

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

        return values;
}

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

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

        return true;
}

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

        if ( name.Empty() )
                name = GetLanguageName(lang);

        return name;
}

bool CXspFile::ConvertOld(CyString file)
{
        // open the file
        FILE *id = fopen(file.c_str(), "rb");
        if ( !id )
                return false;

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

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

        delete data;

        if ( !compr )
                return false;

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

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

                compr[pos - 1] = '\0';
                CyString line = (char *)(compr + start);
                start = pos;
                if ( line.Empty() )
                        continue;
                CyString first = line.GetToken(":", 1, 1);
                CyString rest = line.GetToken(":", 2);
                rest = rest.RemoveFirstSpace();

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

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

                        start += (size + 1);
                }
                else if ( first.Compare("SceneFile") )
                {
                        CyString file = rest.GetToken(" ", 3);
                        m_pSceneFile = this->AddFile(CFileIO(file).GetFilename(), CFileIO(file).GetDir(), FILETYPE_SHIPSCENE);
                        m_pSceneFile->SetCreationTime(rest.GetToken(" ", 1, 1).ToLong());
                        m_pSceneFile->ReadFromData((char *)(compr + pos), rest.GetToken(" ", 2, 2).ToInt());

                        start += m_pSceneFile->GetDataSize();
                }
                else if ( first.Compare("CockpitFile") )
                {
                        CyString file = rest.GetToken(" ", 3);
                        m_pCockpitFile = this->AddFile(CFileIO(file).GetFilename(), CFileIO(file).GetDir(), FILETYPE_COCKPITSCENE);
                        m_pCockpitFile->SetCreationTime(rest.GetToken(" ", 1, 1).ToLong());
                        m_pCockpitFile->ReadFromData((char *)(compr + pos), rest.GetToken(" ", 2, 2).ToInt());

                        start += m_pCockpitFile->GetDataSize();
                }
                else if ( first.Compare("Model") )
                {
                        CyString file = rest.GetToken(" ", 3);
                        C_File *newFile= this->AddFile(CFileIO(file).GetFilename(), CFileIO(file).GetDir(), FILETYPE_SHIPMODEL);
                        newFile->SetCreationTime(rest.GetToken(" ", 1, 1).ToLong());
                        newFile->ReadFromData((char *)(compr + pos), rest.GetToken(" ", 2, 2).ToInt());

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

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

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

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

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

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

                                                for ( SStringList *strNode = newLines.Head(); strNode; strNode = strNode->next )
                                                        this->AddCutData(strNode->str);
                                        }
                                        // bodies
                                        else if ( special == 2 )
                                        {
                                                entries = 0;
                                                CyString section;
                                                for ( int i = 0; i < iLines; i++ )
                                                {
                                                        CyString line = sLines[i];
                                                        line.RemoveChar(' ');
                                                        line.RemoveChar(9);
                                                        if ( line.Empty() || line[0] == '/' )
                                                                continue;
                                                        if ( entries <= 0)
                                                        {
                                                                section = line.GetToken(";", 1, 1);
                                                                entries = line.GetToken(";", 2, 2).ToInt();
                                                        }
                                                        else
                                                        {
                                                                if ( !line.IsIn(";") )
                                                                        continue;
                                                                if ( line.NumToken(";") <= 2 )
                                                                {
                                                                        this->AddBodies(section + ";" + line.GetToken(";", 1, 1) + ";");
                                                                        --entries;
                                                                }
                                                                else
                                                                {
                                                                        bool done = false;
                                                                        while (!done)
                                                                        {
                                                                                int num;
                                                                                CyString *strs = line.SplitToken(";", &num);
                                                                                done = true;
                                                                                for ( int j = 0; j < num; j++ )
                                                                                {
                                                                                        if ( !entries )
                                                                                        {
                                                                                                line = line.GetToken(";", j + 1);
                                                                                                if ( !line.Empty() )
                                                                                                        done = false;
                                                                                                break;
                                                                                        }

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

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

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

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

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

                pos = start;
        }

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

        return true;
}


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

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

        return "Unknown";
}

bool CXspFile::WriteHeader(FILE *id, int valueheader, int valueComprLen)
{
        fprintf ( id, "XSPCycrow;%.2f;%d;%d\n", FILEVERSION, valueheader, valueComprLen );
        if ( ferror(id) )
                return false;
        return true;
}

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

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

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

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

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

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

        return mask;
}

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

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

        return mask;
}

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

bool CXspFile::GeneratePackagerScript(bool wildcard, CyStringList *list, bool datafile)
{
        if ( !CBaseFile::GeneratePackagerScript(wildcard, list, datafile) )
                return false;

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

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

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

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

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

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

        return true;
}

bool CXspFile::LoadPackageData(const Utils::String &sFirst, const Utils::String &sRest)
{
        if ( sFirst.Compare("Shipyard") )
        {
                for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
                {
                        if ( sRest.Compare(GetShipyardName(i).ToString()) )
                        {
                                this->AddShipyard(i);
                                break;
                        }
                }
        }
        else if ( sFirst.Compare("OriginalDescription") )
                m_iOrgDesc = sRest;
        else if ( sFirst.Compare("ShipData") )
                m_sData = sRest;
        else if ( sFirst.Compare("ReadData") ) // read data from a tships file
        {
                CPackages p;
                m_sData = p.ReadShipData(sRest.tokens(" ", 2), sRest.token(" ", 1)).ToString();
        }
        else if ( sFirst.Compare("ExistingShip") )
                m_bExistingShip = true;
        else if ( sFirst.Compare("ShipID") )
                m_sID = sRest;
        else if ( !CBaseFile::LoadPackageData(sFirst, sRest) )
        {
                return false;
        }

        return true;
}


CyString CXspFile::GetX3ShipData()
{
        CyString data = m_sData;

        // change the ship subtype, Reunion uses number, TC uses a define
        CyString sSubType = data.GetToken(";", 6, 6);
        if ( !sSubType.ToInt() && sSubType != "0" )
                data = data.RepToken(";", 6, CyString::Number(CShipData::ConvertShipSubType(sSubType)));

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

                data = data.RepToken(";", TSHIPPOS_CLASS, CyString::Number(num));
        }

        return data;
}

CyString CXspFile::GetTCShipData()
{
        CyString data = m_sData;

        CyString sSubType = data.GetToken(";", 6, 6);
        if ( sSubType.ToInt() || sSubType == "0" )
                data = data.RepToken(";", 6, CShipData::ConvertShipSubType(sSubType.ToInt()));

        CyString sClass = data.GetToken(";", TSHIPPOS_CLASS, TSHIPPOS_CLASS);
        if ( sClass.ToInt() || sClass == "0" )
                data = data.RepToken(";", TSHIPPOS_CLASS, CShipData::ConvertShipClass(sClass.ToInt()));

        return data;
}

bool CXspFile::RemoveCockpit(CyString cockpitid)
{
        // if its a whole line, just get the end
        if ( cockpitid.IsIn(";") )
        {
                cockpitid = cockpitid.GetToken(";", -2);
                while ( cockpitid.Right(1) == ";" )
                        cockpitid.Truncate((int)cockpitid.Length() - 1);
        }

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

        m_lCockpit.RemoveEmpty();

        return ret;
}

bool CXspFile::RemoveComponent(CyString section1, CyString section2, CyString 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(CyString section, CyString 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;
}

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

        return NullString;
}

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

        return NULL;
}

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

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

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

        m_lCutData.RemoveMarked();

        return ret;
}

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

        m_lBodies.RemoveMarked();

        return ret;
}

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

        m_lAnimations.RemoveMarked();

        return ret;
}

void CXspFile::AddCockpit(CyString cockpit, int game, int mask, int index)
{
        SCockpit *pCockpit = NULL;

        // first search for the cockpit entry
        CyString cid = cockpit.GetToken(";", 19, 19);
        for ( CListNode<SCockpit> *node = m_lCockpit.Front(); node; node = node->next() )
        {
                CyString id = node->Data()->sCockpit.GetToken(";", 19, 19);
                if ( id.Compare(cid) )
                {
                        pCockpit = node->Data();
                        break;
                }
        }

        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 )
        {
                // 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 = cockpit.GetToken(";", 9, 9).ToInt();
                else
                        wm->iMask = mask;
        }
        m_bChanged = true; 
}

void CXspFile::AddBody(CyString section, CyString data)
{
        this->AddBodies(section + ";" + data);
        m_bChanged = true; 
}
void CXspFile::AddBodies(CyString data)
{
        if ( !data.IsNumber() )
        {
                if ( data[(int)(data.Length() - 5)] == '.' )
                        data = data.Left(-5);
                else if ( data[(int)(data.Length() - 4)] == '.' )
                        data = data.Left(-4);
                if ( data.Right(1) != ";" )
                        data += ";";
        }
        m_lBodies.PushBack(data, true);
        m_bChanged = true; 
}

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

        return TAT_NONE;
}

bool CXspFile::ReadAnimations(CyStringList *lIn, CyStringList *lOut, int startRecord)
{
        CyString lastComment;
        CyString addEntry;
        int remaining = 0;
        CyString lastType;
        int newEntries = 0;
        int entries = -1;
        for ( SStringList *strNode = lIn->Head(); strNode; strNode = strNode->next )
        {
                CyString line = strNode->str;
                line.RemoveChar('\r');
                line.RemoveChar(9);
                if ( line.Empty() || line[0] == '/' )
                        continue;
                if ( entries == -1)
                {
                        entries = line.GetToken(";", 1, 1).ToInt();
                        newEntries = entries - startRecord;
                }
                else
                {
                        // remove comments, endspaces and last ;
                        CyString sStriped = line;
                        if ( sStriped.IsIn("//") )
                        {
                                if ( entries <= newEntries )
                                        lastComment = CyString("//") + sStriped.GetToken("//", 2);
                                sStriped = sStriped.GetToken("//", 1, 1);
                        }
                        sStriped.RemoveEndSpace();
                        if ( sStriped.Right(1) == ";" )
                                sStriped.Truncate(-1);

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

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

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

                                }
                        }

                        if ( !sRemainingLeft.Empty() )
                        {
                                int tatType = CXspFile::GetAnimationType(lastType);
                                if ( tatType == TAT_SINGLE )
                                {
                                        if ( sRemainingLeft.IsIn(";") )
                                                remaining -= sRemainingLeft.NumToken(";");
                                        else if ( !sRemainingLeft.Empty() )
                                                --remaining;

                                        if ( entries <= newEntries )
                                                addEntry += sRemainingLeft + ";";
                                }
                                else if ( tatType == TAT_3 )
                                {
                                        // last entry
                                        if ( remaining == 1 )
                                                --remaining;
                                        else if ( sRemainingLeft.IsIn(";") )
                                        {
                                                if ( !sRemainingLeft.IsIn("TATF_COORDS") )
                                                {
                                                        int amt = sRemainingLeft.NumToken(";") / 3;
                                                        remaining -= amt;
                                                        if ( remaining == 1 && (sRemainingLeft.NumToken(";") - (amt * 3)) == 1 )
                                                                --remaining;
                                                }
                                                else
                                                {
                                                        int iRem = sRemainingLeft.NumToken(";");
                                                        int iPos = 1;
                                                        while ( iPos < iRem )
                                                        {
                                                                CyString first = sRemainingLeft.GetToken(";", iPos, iPos);
                                                                if ( first.IsIn("TATF_COORDS") )
                                                                        iPos += 5;
                                                                else
                                                                        iPos += 3;
                                                                --remaining;
                                                        }

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

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

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

        return !lOut->Empty();
}

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

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

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

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

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

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

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

        m_sData = pVfs->getTShipsEntry(m_sID);

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

        return true;
}

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

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

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

        return true;
}

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

        return true;
}

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

        if ( !m_pSceneFile )
                return NULL;

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

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

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

        CyStringList *lModels = new CyStringList;

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

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

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

                        newline = false;

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

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

        if ( deleteData )
                delete data;

        return lModels;
}

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

        bool extracted = false;
        if ( pVfs->ExtractGameFile("types/dummies.pck", "tmp") ) {
                CFileIO File("tmp");
                if ( File.Exists() )
                {
                        CyString section;
                        int secCount = 0;
                        CyStringList *lines = File.ReadLinesStr();
                        for ( SStringList *node = lines->Head(); node; node = node->next )
                        {
                                node->str.RemoveFirstSpace();
                                node->str.RemoveChar(9);
                                node->str.RemoveChar('\r');
                                if ( node->str.Empty() )
                                        continue;
                                if ( node->str[0] == '/' )
                                        continue;

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

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

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

                                }
                        }

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

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

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

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

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

        size_t startLine = 0;
        CyString line;
        size_t pos = 0;

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

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

                        if ( !line.Empty() && line[0] != '/' )
                        {
                                CyString first = line.GetToken(":", 1, 1);
                                if ( first.Compare("MATERIAL6") )
                                {
                                        int max;
                                        CyString *strs = line.GetToken(":", 2).SplitToken("; ", &max);

                                        if ( strs && max >= 2)
                                        {
                                                for ( int i = 2; i < max; i++ )
                                                {
                                                        CyString s = strs[i];
                                                        if ( s.IsIn(";") )
                                                                s = s.GetToken(";", 1, 1);
                                                        if ( !s.IsNumber() && !s.Compare("NULL") && s.IsIn(".") )
                                                                list->PushBack(s, "", true);
                                                }
                                        }

                                        CLEANSPLIT(strs, max)
                                }
                        }

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

        return true;
}

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

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

                // cant do these yet
                if ( f->CheckFileExt("pbb") || f->CheckFileExt("bob") )
                {
                        if ( !f->BobDecompile() )
                                continue;
                }

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

                unsigned char *data = NULL;
                size_t size;

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

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

        for ( SStringList *node = lTextures.Head(); node; node = node->next )
        {
                if ( CFileIO(node->str).CheckFileExtension("dds") )
                        pVfs->extractGameFileToPackage(this, "dds\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER);
                else 
                        pVfs->extractGameFileToPackage(this, "textures\\" + Utils::String(node->str.ToString()), FILETYPE_SHIPOTHER);
        }
}

bool CXspFile::AddTextFromFile(CyString file, int textId)
{
        bool remove = false;
        if ( CFileIO(file).CheckFileExtension("pck") )
        {
                C_File F(file);
                F.UnPCKFile();
                if ( F.WriteToFile("tmp") )
                {
                        remove = true;
                        file = "tmp";
                }
        }

        FILE *fid = fopen(file.c_str(), "r");
        if ( fid )
        {
                bool ret = this->AddTextFromFile(fid, textId);
                fclose(fid);

                if ( remove )
                        CFileIO(file).Remove();
                m_bChanged = true; 
                return ret;
        }

        return false;
}

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

        return false;
}

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

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

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

                delete lines;
                return true;
        }
        return false;
}

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

                return ret;
        }

        return false;
}

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

        if ( pVfs->ExtractGameFile("types/Bodies.pck", "tmp") ) {
                bool ret = this->ImportBodies(sceneModels, "tmp");
                CFileIO("tmp").Remove();

                return ret;
        }

        return false;
}

bool CXspFile::AddTextFromFile(FILE *id, int textId)
{
        if ( textId == -1 && !m_sData.empty() )
                textId = m_sData.token(";", 7);

        if ( textId <= 0 )
                return false;

        CyString line;
        if ( !id )
                return false;

        bool added = false;

        CyString shipName;
        CyString shipDesc;

        int lang = 0;
        bool inpage = false;
        while ( !feof(id) )
        {
                if ( !shipName.Empty() && !shipDesc.Empty() )
                {
                        added = true;
                        break;
                }

                line.GetEndOfLine(id, 0, false);
                line.RemoveChar(9);
                line.RemoveChar('\r');
                line.RemoveFirstSpace();

                if ( inpage )
                {
                        if ( line.Left(6).Compare("</page") )
                                break;

                        // find matching id
                        if ( line.Left(6).Compare("<t id=") )
                        {
                                int pos = line.FindPos("id=\"");
                                if ( pos != -1 )
                                {
                                        int endpos = line.FindPos("\"", pos + 5);
                                        if ( endpos != -1 )
                                        {
                                                int id = line.Mid(pos + 5, endpos).ToInt();
                                                if ( id == textId || id == (textId + 1) )
                                                {
                                                        pos = line.FindPos(">", endpos);
                                                        if ( pos != -1 )
                                                        {
                                                                endpos = line.FindPos("</t>", pos);
                                                                if ( endpos != -1 )
                                                                {
                                                                        if ( id == textId )
                                                                                shipName = line.Mid(pos + 2, endpos - (pos + 1));
                                                                        else
                                                                                shipDesc = line.Mid(pos + 2, endpos - (pos + 1));
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
                else if ( lang ) // search for page 17
                {
                        if ( line.Left(8).Compare("<page id") )
                        {
                                int pos = line.FindPos("id=\"");
                                if ( pos != -1 )
                                {
                                        int endpos = line.FindPos("\"", pos + 5);
                                        if ( endpos != -1 )
                                        {
                                                CyString sId = line.Mid(pos + 5, endpos - (pos + 4));
                                                int id = sId.ToInt();
                                                if ( id == 17 || sId.Right(4).ToInt() == 17 )
                                                        inpage = true;
                                        }
                                }
                        }
                }
                else if ( line.Left(12).Compare("<language id") )
                {
                        int pos = line.FindPos("id=\"");
                        if ( pos != -1 )
                        {
                                int endpos = line.FindPos("\"", pos + 5);
                                if ( endpos != -1 )
                                        lang = line.Mid(pos + 5, endpos - (pos + 4)).ToInt();
                        }
                }
        }

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

        if ( added )
        {
                if ( lang == 44 || m_sName.Empty())
                {
                        m_sName = shipName;
                        m_sDescription= shipDesc;
                        m_sDescription = m_sDescription.FindReplace("&amp", "&");
                }
                this->AddText(lang, shipName, shipDesc);
                return true;    
        }

        return false;
}

void CXspFile::ExtractTexts(CCatFile *catFile, CCatFile *secondCatFile, int textId)
{
        for ( CListNode<SInCatFile> *node = catFile->GetFiles()->Front(); node; node = node->next() )
        {
                SInCatFile *f = node->Data();
                if ( !f->sFile.Left(2).Compare("t\\") && !f->sFile.Left(2).Compare("t/") )
                        continue;

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

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

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

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

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

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

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

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

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

        return true;
}

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

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

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

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

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

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

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

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

        delete lModels;

        return true;
}


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

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

                CyString id = c->sCockpit.GetToken(";", 19, 19);

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

CyString CXspFile::FormatShipData(CyStringList *cockpits, int *text, int game)
{
        CyString data = (game == GAME_X3) ? this->GetX3ShipData() : this->GetTCShipData();
        // do turrets
        for ( int t = 0; t < 6; t++ )
        {
                int oldPos = 0;
                CyString turret = data.GetToken(";", 32 + (t * 2), 32 + (t * 2));
                if ( !turret.IsNumber() )
                {
                        if ( turret.IsIn("(") )
                        {
                                oldPos = turret.GetToken("(", 2).GetToken(")", 1, 1).ToInt();
                                turret = turret.GetToken("(", 1, 1);
                        }
                        int pos = cockpits->FindStringPos(turret);
                        if ( pos < 0 )  pos = oldPos;
                        data = data.RepToken(";", 32 + (t * 2), CyString::Number(pos));
                }
        }
        // adjust the weapons
        int mask = this->GetLaserMask(game - 1);
        if ( mask != -1 )
                data = data.RepToken(";", 19, CyString::Number(mask));
        mask = this->GetMissileMask(game - 1);
        if ( mask != -1 )
                data = data.RepToken(";", 25, CyString::Number(mask));

        // fix the ship text
        if ( m_iOrgDesc > 0 )
                (*text) = m_iOrgDesc;
        data = data.RepToken(";", 7, CyString::Number(*text));

        // add the ware id
        data.RemoveChar(9);
        data.RemoveEndSpace();
        // remove the end ;
        while ( data.Right(1) == ";" ) data.Truncate((int)data.Length() - 1);
        data = data.RepToken(";", data.NumToken(";"), this->GetShipID());
        if ( data.Right(1) != ";" )
                data += ";";

        return data;
}