Rev 248 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "XspFile.h"#include "File_IO.h"#include "Packages.h"#include "CatFile.h"#define ADDSECTIONLIST(T, L) for(CListNode<T> *node = L.Front(); node; node = node->next()) \_addSection(list, node->Data()->sSection, node->Data()->sData);#define GENERATESECTION(F, T, L) if ( !L.empty() ) { \type = F; \Utils::CList<STypesSection> list; \ADDSECTIONLIST(T, L); \data = CXspFile::TypesListToString(list, true); \}#define GENERATESTRINGSECTION(F, L, FIRST) if ( !L.empty() ) { \type = F; \Utils::CList<STypesSection> list; \_addDataSection(L, list, FIRST); \data = CXspFile::TypesListToString(list, true); \}CXspFile::CXspFile () : CBaseFile(){SetDefaults ();}void CXspFile::Delete (){CBaseFile::Delete();m_pSceneFile = m_pCockpitFile = NULL;// delete listsm_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 idSText *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 §ion, 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 §ion, const Utils::WString §ion2, 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 gameSWeaponMask *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 oneif ( !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 gameSWeaponMask *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 oneif ( !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>"));elsereturn 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 upbool CXspFile::convertOld(const Utils::WString &file){// open the fileFILE *id = _wfopen(file.c_str(), L"rb");if ( !id )return false;// read to memoryfseek(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 filesize_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 linesize_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 lineif ( 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 linesUtils::WString data = Utils::WString::FromString((const char *)newFile->GetData());data.removeChar('\r');std::vector<Utils::WString> sLines;if(data.tokenise(L"\n", sLines)){// cut datasize_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);}// bodieselse 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";", 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 sectionif ( !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;}}}}}}// animationselse 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 X3if ( !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 itthis->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 itthis->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 §ion, 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 fileswsprintf(buf, L"%hs", fname.c_str());if ( tmpFile.startRead() ) {*/ZipAdd(hz, fname.c_str(), (void *)fileData.c_str(), 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 defineUtils::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 endif ( 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 §ion1, const Utils::WString §ion2, 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 §ion, 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 existsSWeaponMask *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);elsewm->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 maskif ( game > 0 )addCockpitWeapon(cid, game, mask);_changed();}void CXspFile::addBody(const Utils::WString §ion, 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 lineslastType = 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 entryif ( 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;elseiPos += 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 languageif ( 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 thatif ( english )return english;// otherwise try a germanif ( 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 textif ( !t->sName.empty() )return t->sName;// we have found a text, but there is no ship name ??else if ( lang != 44 ){// reget the english onet = FindShipText(44);if ( t && !t->sName.empty() )return t->sName;}}// still not found one, return the ships namereturn this->shipName(lang);}Utils::WString CXspFile::textDescription(int lang){SText *t = FindShipText(lang);if ( t ){// return the correct language textif ( !t->sDesc.empty() )return t->sDesc;// we have found a text, but there is no ship name ??else if ( lang != 44 ){// reget the english onet = FindShipText(44);if ( t && !t->sDesc.empty() )return t->sDesc;}}// still not found one, return the ships nameif ( !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 filesif ( 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 listif ( !this->processSceneFiles(pVfs, progress) )return false;// pack all the ship filesthis->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 fileif (!m_pSceneFile)m_pSceneFile = this->GetFirstFile(FILETYPE_SHIPSCENE);if (!m_pSceneFile)return false;// check if its packedsize_t datasize;unsigned char* data = m_pSceneFile->UncompressData((long*)&datasize, 0);// if data wasn't compressed, then copy it itself as we are editing itbool 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 lineif (!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 modelelse 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 yetif (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 yetif (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 yetif (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 insteadUtils::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 sectionif (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);}elseitr++;}// now get all the entries from TShipsfor (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 idif ( 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);elseshipDesc = 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(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 shipnameif ( !shipName.empty() )added = true;if ( added ){if ( lang == 44 || this->name().empty()){this->setName(shipName);this->setDescription(shipDesc.findReplace(L"&", 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 databool 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 dummycontinue;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 texturescase IMPORTSHIP_TEXTURES:this->extractTextures(pVfs);break;case IMPORTSHIP_TEXTS:// extract the text file entriespVfs->extractTexts(this, m_sData.token(L";", 7));break;case IMPORTSHIP_BODIES:// extract the bodies entriesif (lModels.empty()) return false;this->extractBodies(pVfs, lModels);break;case IMPORTSHIP_COCKPITS:// extract the cockpit entriesthis->extractCockpits(pVfs);break;}return true;}bool CXspFile::processSceneFiles(CVirtualFileSystem *pVfs, CProgressInfo *progress){// now lets parse our filesif ( progress ) progress->UpdateStatus(IMPORTSHIP_EXTRACTSCENE);Utils::WStringList lModels;if(!readSceneModels(lModels))return false;// extract componants, and add extra items to listif ( !this->processSceneFileSection(IMPORTSHIP_COMPONANT, pVfs, lModels, progress) )return false;//lets first find any model filesif ( !this->processSceneFileSection(IMPORTSHIP_MODELS, pVfs, lModels, progress) )return false;// extract the dummiesif ( !this->processSceneFileSection(IMPORTSHIP_DUMMIES, pVfs, lModels, progress) )return false;// extract the texturesif ( !this->processSceneFileSection(IMPORTSHIP_TEXTURES, pVfs, lModels, progress) )return false;// extract the text file entriesif ( !this->processSceneFileSection(IMPORTSHIP_TEXTS, pVfs, lModels, progress) )return false;// extract the bodies entriesif ( !this->processSceneFileSection(IMPORTSHIP_BODIES, pVfs, lModels, progress) )return false;// extract the cockpit entriesif ( !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 turretsfor ( 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);}int 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 weaponsint 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 textif ( m_iOrgDesc > 0 )(*text) = m_iOrgDesc;data = data.replaceToken(L";", 7, (long)*text);// add the ware iddata.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;}