Rev 224 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "ModDiff.h"#include "File_IO.h"#include "CatFile.h"CModDiff::CModDiff(const Utils::WString &dir, const Utils::WString &sAddon, int maxPatch) : m_pCatFile(NULL), m_sAddon(sAddon), m_sTempDir(L"."), m_iMaxPatch(maxPatch){m_bLoaded = this->LoadDirectory(dir);}CModDiff::~CModDiff(void){delete m_pCatFile;}bool CModDiff::LoadDirectory(const Utils::WString &dir){m_sCurrentDir = dir;m_fileSystem.setAddon(m_sAddon);return m_fileSystem.LoadFilesystem(dir, m_iMaxPatch);}bool CModDiff::startDiff(const Utils::WString &sModFile){ClearError();if ( !CFileIO::Exists(sModFile) ) { m_iError = MDERR_FILENOTFOUND; return false; }delete m_pCatFile;m_pCatFile = new CCatFile;if ( m_pCatFile->open(sModFile, m_sAddon, CATREAD_CATDECRYPT, false) != CATERR_NONE ) {delete m_pCatFile;m_pCatFile = NULL;m_iError = MDERR_CANTOPENMOD;return false;}return true;}Utils::WString CModDiff::_extractFile(const Utils::WString &sFile, const Utils::WString &sTo){SInCatFile *c = m_pCatFile->findData(sFile);if ( !c ) c = m_pCatFile->findData(m_sAddon + L"/" + sFile);if ( !c ) return m_fileSystem.extractGameFile(sFile, sTo);if (m_pCatFile->extractFile(c, sTo)) return sTo;return L"";}bool CModDiff::doDiff(const Utils::WString &sModFile){// find the file in the loaded catSInCatFile *c = m_pCatFile->findData(sModFile);if ( !c ) c = m_pCatFile->findData(m_sAddon + L"/" + sModFile);if ( !c ) return false;Utils::WString temp = m_sTempDir;// extract the matching fileUtils::WString sToFile = CFileIO(temp + L"/" + CFileIO(c->sFile).filename()).fullFilename();if ( !m_pCatFile->extractFile(sModFile, sToFile) ) return false;// create a diffUtils::WString to = m_fileSystem.extractGameFile(c->sFile, sToFile + L".compare");if ( !to.empty() ) {SDiffFile *diff = diffFile(to, sToFile, c->sFile);if ( diff ) {this->_adjustFile(sModFile, diff, false);}CFileIO::Remove(to);}CFileIO::Remove(sToFile);return true;}bool CModDiff::_adjustTShips(SDiffFile *pDiff, bool bReverse){// need to read TCockpitsUtils::WString sTo = this->_extractFile(L"types/TCockpits.pck", L"TCockpits.xml");if ( sTo.empty() ) return false;CFileIO TCockpits(L"TCockpits.xml");if ( !TCockpits.exists() ) return false;std::vector<Utils::WString> lines;if (!TCockpits.readLines(lines))return false;// remove all the commentsauto itr = lines.begin();while(itr != lines.end()){if ((*itr)[0] == L'/')itr = lines.erase(itr);elseitr++;}lines.erase(lines.begin());std::map<std::wstring, int> fileSet;int iPos = 0;for(itr = lines.begin(); itr != lines.end(); itr++)fileSet[itr->token(L";", -2)] = iPos++;// now cycle through ships and adjust the cockpitsint iCount = -1;for ( SDiffEntry *pEntry = pDiff->m_lEntries.First(); pEntry; pEntry = pDiff->m_lEntries.Next() ) {switch (pEntry->iType) {case DIFFTYPE_ADDITION:{SDiffEntryAddition *pAddition = static_cast<SDiffEntryAddition *>(pEntry);if ( pAddition ) {for ( int t = 0; t < 6; t++ ) {Utils::WString sE = pAddition->sEntry.token(L";", 32 + (t * 2));if ( !bReverse && sE.isNumber() ){int iTurret = sE;if ( iTurret && static_cast<size_t>(iTurret) < lines.size()){Utils::WString sCockpit = lines[iTurret];pAddition->sEntry = pAddition->sEntry.replaceToken(L";", 32 + (t * 2), sCockpit.token(L";", -2) + ":" + static_cast<long>(iTurret));}}else if ( bReverse && !sE.isNumber() ) {int iUnfound = 0;if ( sE.contains(L":") ) {iUnfound = sE.token(L":", 2);sE = sE.token(L":", 1);}int iEntry = (fileSet.find(sE) == fileSet.end()) ? iUnfound : fileSet[sE];pAddition->sEntry = pAddition->sEntry.replaceToken(L";", 32 + (t * 2), static_cast<long>(iEntry));}}}}break;case DIFFTYPE_CHANGE:{SDiffEntryChange *pChange = static_cast<SDiffEntryChange *>(pEntry);if ( pChange->iPos >= 32 && pChange->iPos <= 42 && (pChange->iPos % 2) && pChange->sEntry.isNumber() ) {Utils::WString sCockpit = lines[pChange->sEntry.toInt()];pChange->sEntry = sCockpit.token(L";", -2) + L":" + pChange->sEntry;}}break;}}return true;}int CModDiff::_specialType(const Utils::WString &sFile) const{if ( sFile.Compare(L"TShips") ) return MERGETYPE_TSHIPS;return MERGETYPE_NONE;}void CModDiff::_adjustFile(const Utils::WString &sFile, SDiffFile *pDiff, bool bReverse){// check if we need to adjust the fileCFileIO File(sFile);int iType = _specialType(File.baseName());if ( iType == MERGETYPE_NONE ) return;// read in the file dataswitch(iType) {case MERGETYPE_TSHIPS:this->_adjustTShips(pDiff, bReverse);break;}}bool CModDiff::CreateDiff(const Utils::WString &modfile){ClearError();//check for valid parametersif ( !CFileIO::Exists(modfile) ) { m_iError = MDERR_FILENOTFOUND; return false; }Utils::WString addonDir = L"";int addonSize = addonDir.length() + 1;// try and open the mod fileCCatFile cat;if ( cat.open(modfile, addonDir, CATREAD_DAT, false) != CATERR_NONE ) { m_iError = MDERR_CANTOPENMOD; return false; }Utils::WString temp = m_sTempDir;// we'll need to read in all the types/text filesfor (unsigned int i = 0; i < cat.GetNumFiles(); i++){SInCatFile *f = cat.GetFile(i);Utils::WString checkFile = f->sFile.findReplace(L"\\", L"/");if ( (checkFile.left(6).Compare(L"types/") || checkFile.left(2).Compare(L"t/") || checkFile.left(6 + addonSize).Compare(addonDir + L"/types/") || checkFile.left(2 + addonSize).Compare(addonDir + L"/t/")) && _validFile(checkFile) ){// extract the file to the temp dirUtils::WString toFile = CFileIO(temp + L"/" + CFileIO(f->sFile).filename()).fullFilename();if (cat.extractFile(f, toFile )){// now extract the matching file from the game dirif (!m_fileSystem.extractGameFile(f->sFile, toFile + L".compare").empty() ){diffFile(toFile + L".compare", toFile, f->sFile);CFileIO::Remove(toFile + L".compare");}// make sure we clear up afterwardsCFileIO::Remove(toFile);}}}return true;}SDiffFile *CModDiff::diffFile(const Utils::WString &baseFile, const Utils::WString &modFile, const Utils::WString &fileType){int type = 0;SDiffFile *diffFile = new SDiffFile;diffFile->sFile = fileType;// read both files and compare themCFileIO Base(baseFile);std::vector<Utils::WString> baseLines;if(Base.readLines(baseLines)){CFileIO Mod(modFile);std::vector<Utils::WString> lines;if(Mod.readLines(lines)){int id = -1;std::vector<Utils::WString>::iterator node[2];node[0] = baseLines.begin();node[1] = lines.begin();std::vector<Utils::WString>::iterator endNode[2];endNode[0] = baseLines.end();endNode[1] = lines.end();Utils::WString prev[2];while (node[0] != endNode[0] || node[1] != endNode[1]){Utils::WString str[2];for ( int i = 0; i < 2; i++ ){while (node[i] != endNode[i]){Utils::WString l = *node[i];node[i]++;l.removeFirstSpace();l.removeChar('\r');if ( !l.empty() && l.left(1) != L"/") {str[i] += l;if ( _isLineComplete(str[i], fileType, (id == -1) ? true : false) )break;}}}if ( id == -1 )id = 0;else{// first check for mismatch amount, one of the nodes will be emptyif ( str[0].empty() && !str[1].empty() ) // mod file has more entries (these must be additions){SDiffEntryAddition *entry = new SDiffEntryAddition;entry->iID = id;entry->sEntry = str[1];diffFile->m_lEntries.push_back(entry);}else if ( str[1].empty() && !str[0].empty() ) // mod file has less entries (must have removed some){SDiffEntry *entry = new SDiffEntryRemoval;entry->iID = id;diffFile->m_lEntries.push_back(entry);}else // we have a line for both, we need to compare them{// we migth have multiple entries to add when changedif ( str[0].empty() || str[1].empty() ) continue;_compareLine(str[0], str[1], type, id, diffFile);}++id;}}}}if ( diffFile->m_lEntries.empty() ) {delete diffFile;return NULL;}else {m_lFiles.push_back(diffFile);return diffFile;}}int CModDiff::_amountPosition(const Utils::WString &fileType){return 2;}bool CModDiff::_isLineComplete(const Utils::WString &line, const Utils::WString &fileType, bool first){if ( first ){if ( line.countToken(L";") > _amountPosition(fileType) ) return true;return false;}return true;}void CModDiff::_compareLine(const Utils::WString &line1, const Utils::WString &line2, int type, int id, SDiffFile *diffFile){std::vector<Utils::WString> str1;std::vector<Utils::WString> str2;if (!line1.tokenise(L";", str1))return;if (!line2.tokenise(L";", str2))return;size_t max = ((str1.size() > str2.size()) ? str2.size() : str1.size());for (size_t i = 0; i < max; i++ ){if ( str1[i] == str2[i] ) continue;if ( str1[i].Compare(str2[i]) ) continue;if ( str1[i].empty() && str2[i].empty() ) continue;if ( str1[i].length() && str1[i][0] == '\0' && str2[i].length() && str2[i][0] == '\0' ) continue;SDiffEntryChange *diff = new SDiffEntryChange;diff->iID = id;diff->iPos = i;diff->sEntry = str2[i];diff->sFrom = str1[i];diffFile->m_lEntries.push_back(diff);}}void CModDiff::Clean(){for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() ){node->Data()->m_lEntries.clear();node->DeleteData();}m_lFiles.clear();}bool CModDiff::WriteDiff(const Utils::WString &file){if ( m_lFiles.empty() ) return false;Utils::WStringList lines;for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() ){lines.pushBack(L"$$" + node->Data()->sFile);for ( CListNode<SDiffEntry> *node2 = node->Data()->m_lEntries.Front(); node2; node2 = node2->next() ){switch(node2->Data()->iType){case DIFFTYPE_ADDITION:lines.pushBack(L"+++:" + Utils::WString::Number((long)node2->Data()->iID) + L":" + ((SDiffEntryAddition *)node2->Data())->sEntry);break;case DIFFTYPE_REMOVAL:lines.pushBack(L"---:" + Utils::WString::Number((long)node2->Data()->iID));break;case DIFFTYPE_CHANGE:lines.pushBack(L"///:" + Utils::WString::Number((long)node2->Data()->iID) + L":" + Utils::WString::Number((long)((SDiffEntryChange *)node2->Data())->iPos) + L":" + ((SDiffEntryChange *)node2->Data())->sEntry);break;}}}CFileIO File(file);return File.writeFile(&lines);}bool CModDiff::ReadDiff(const Utils::WString &file){Clean();CFileIO File(file);if ( !File.exists() ) return false;std::vector<Utils::WString> lines;if(File.readLines(lines)){SDiffFile *diffFile = NULL;for(auto itr = lines.begin(); itr != lines.end(); itr++){if (itr->left(2).Compare(L"$$") ){diffFile = new SDiffFile;m_lFiles.push_back(diffFile);diffFile->sFile = itr->right(-2);}else if ( diffFile ){if ( itr->left(4).Compare(L"+++:") ){SDiffEntryAddition *addition = new SDiffEntryAddition;addition->iID = itr->token(L":", 2).toInt();addition->sEntry = itr->tokens(L":", 3);diffFile->m_lEntries.push_back(addition);}else if ( itr->left(4).Compare(L"---:") ){SDiffEntryRemoval *entry = new SDiffEntryRemoval;entry->iID = itr->token(L":", 2).toInt();diffFile->m_lEntries.push_back(entry);}else if (itr->left(4).Compare(L"///:") ){SDiffEntryChange *entry = new SDiffEntryChange;entry->iID = itr->token(L":", 2).toInt();entry->iPos = itr->token(L":", 3).toInt();entry->sEntry = itr->tokens(L":", 4);diffFile->m_lEntries.push_back(entry);}}}return true;}return false;}bool CModDiff::ApplyMod(const Utils::WString &mod){return m_fileSystem.addMod(mod);}bool CModDiff::ApplyDiff(const Utils::WString &mod){if ( m_lFiles.empty() ) return false;this->ApplyMod(mod);bool ret = false;Utils::WString temp = m_sTempDir;Utils::WString addonDir = L"";CCatFile cat;if ( !CCatFile::Opened(cat.open(mod, addonDir, CATREAD_CATDECRYPT, true)) )return false;for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() ){// extract the file from the gameSDiffFile *f = node->Data();Utils::WStringList writeLines;int id;if ( _readGameFile(f->sFile, writeLines, &id)){// now apply the diffthis->_adjustFile(f->sFile, f, true);for ( CListNode<SDiffEntry> *eNode = f->m_lEntries.Front(); eNode; eNode = eNode->next() ){switch ( eNode->Data()->iType ){case DIFFTYPE_ADDITION:writeLines.pushBack(((SDiffEntryAddition *)eNode->Data())->sEntry);break;case DIFFTYPE_REMOVAL:writeLines.removeAt(eNode->Data()->iID);break;case DIFFTYPE_CHANGE:{auto strAt = writeLines.get(eNode->Data()->iID);if ( strAt )strAt->str = strAt->str.replaceToken(L";", ((SDiffEntryChange *)eNode->Data())->iPos, ((SDiffEntryChange *)eNode->Data())->sEntry);}break;}}// add our comments and infowriteLines.pushFront(Utils::WString((long)id) + L";" + (long)writeLines.size() + L";");writeLines.pushFront(L"// Generated by ModDiff (SPK Version: " + Utils::WString::FromFloat(GetLibraryVersion(), 2) + L")");// now write the fileCFileIO WriteFile(temp + L"/" + CFileIO(f->sFile).filename());if ( WriteFile.writeFile(&writeLines) ){if ( cat.appendFile(m_sTempDir + L"/" + CFileIO(f->sFile).filename(), f->sFile) )ret = true;WriteFile.remove();}}}if ( ret )cat.WriteCatFile();return ret;}bool CModDiff::_readGameFile(const Utils::WString &file, Utils::WStringList &writeLines, int *id){bool ret = false;Utils::WString sTo = m_fileSystem.extractGameFile(file, m_sTempDir + L"/" + CFileIO(file).filename());if ( !sTo.empty() ) {CFileIO File(sTo);std::vector<Utils::WString> lines;if(File.readLines(lines)){int entries = -1;for(auto itr = lines.begin(); itr != lines.end(); itr++){Utils::WString l = *itr;l.removeFirstSpace();if ( l.empty() ) continue;if ( l.left(1) == L"/" ) continue;if ( entries == -1 ){entries = l.token(L":", 2).toInt();if ( id )(*id) = l.token(L":", 1).toInt();}elsewriteLines.pushBack(l);}ret = true;}File.close();File.remove();}return ret;}bool CModDiff::_validFile(const Utils::WString &file){return CModDiff::CanBeDiffed(file);}bool CModDiff::CanBeDiffed(const Utils::WString &file){Utils::WString checkFile = file;checkFile = CFileIO(file).dir() + L"/" + CFileIO(file).baseName();// all t files are the same formatif ( checkFile.left(7).Compare(L"types/T") )return true;return false;}