Rev 1 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "ModDiff.h"#include "File_IO.h"#include "CatFile.h"CModDiff::CModDiff(CyString &dir, int maxPatch){m_bLoaded = LoadDirectory(dir);m_sTempDir = ".";m_iMaxPatch = maxPatch;}CModDiff::~CModDiff(void){}bool CModDiff::LoadDirectory(CyString &dir){m_sCurrentDir = dir;return m_fileSystem.LoadFilesystem(dir, NullString, m_iMaxPatch);}bool CModDiff::CreateDiff(CyString &modfile){ClearError();//check for valid parametersif ( !CFileIO(modfile).Exists() ) { m_iError = MDERR_FILENOTFOUND; return false; }CyString addonDir = "";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; }// we'll need to read in all the types/text filesfor ( int i = 0; i < cat.GetNumFiles(); i++ ){SInCatFile *f = cat.GetFile(i);CyString checkFile = f->sFile.FindReplace("\\", "/");if ( (checkFile.Left(6).Compare("types/") || checkFile.Left(2).Compare("t/") || checkFile.Left(6 + addonSize).Compare(addonDir + "/types/") || checkFile.Left(2 + addonSize).Compare(addonDir + "/t/")) && ValidFile(checkFile) ){// extract the file to the temp dirCyString toFile = CFileIO(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename()).GetFullFilename();if ( cat.ExtractFile(f, toFile ) ){// now extract the matching file from the game dirif ( m_fileSystem.ExtractGameFile(f->sFile, toFile + ".compare") ){DiffFile(toFile + ".compare", toFile, f->sFile);CFileIO(toFile + ".compare").Remove();}// make sure we clear up afterwardsCFileIO(toFile).Remove();}}}return true;}bool CModDiff::DiffFile(CyString &baseFile, CyString &modFile, CyString &fileType){int type = 0;SDiffFile *diffFile = new SDiffFile;diffFile->sFile = fileType;// read both files and compare themCFileIO Base(baseFile);CyStringList *baseLines = Base.ReadLinesStr();if ( baseLines ){CFileIO Mod(modFile);CyStringList *lines = Mod.ReadLinesStr();if ( lines ){int id = -1;SStringList *node[2];node[0] = baseLines->Head();node[1] = lines->Head();CyString prev[2];while ( node[0] || node[1] ){CyString str[2];for ( int i = 0; i < 2; i++ ){while (node[i]){CyString l = node[i]->str;node[i] = node[i]->next;l.RemoveFirstSpace();l.RemoveChar('\r');if ( !l.Empty() && l.Left(1) != "/"){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;}}delete lines;}delete baseLines;}if ( diffFile->m_lEntries.empty() )delete diffFile;elsem_lFiles.push_back(diffFile);return true;}int CModDiff::GetAmountPosition(const CyString &fileType){return 2;}bool CModDiff::IsLineComplete(CyString &line, CyString &fileType, bool first){if ( first ){if ( line.NumToken(";") > GetAmountPosition(fileType) ) return true;return false;}return true;}void CModDiff::CompareLine(CyString &line1, CyString &line2, int type, int id, SDiffFile *diffFile){int max1, max2;CyString *str1 = line1.SplitToken(";", &max1);CyString *str2 = line2.SplitToken(";", &max2);if ( !str1 || !str2 ) return;int max = ((max1 > max2) ? max2 : max1);for ( int 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(CyString &file){if ( m_lFiles.empty() ) return false;CyStringList lines;for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() ){lines.PushBack(CyString("$$") + 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(CyString("+++:") + (long)node2->Data()->iID + ":" + ((SDiffEntryAddition *)node2->Data())->sEntry);break;case DIFFTYPE_REMOVAL:lines.PushBack(CyString("---:") + (long)node2->Data()->iID);break;case DIFFTYPE_CHANGE:lines.PushBack(CyString("///:") + (long)node2->Data()->iID + ":" + (long)((SDiffEntryChange *)node2->Data())->iPos + ":" + ((SDiffEntryChange *)node2->Data())->sEntry);break;}}}CFileIO File(file);return File.WriteFile(&lines);}bool CModDiff::ReadDiff(CyString &file){Clean();CFileIO File(file);if ( !File.Exists() ) return false;CyStringList *lines = File.ReadLinesStr();if ( lines ){SDiffFile *diffFile = NULL;for ( SStringList *str = lines->Head(); str; str = str->next ){if ( str->str.Left(2).Compare("$$") ){diffFile = new SDiffFile;m_lFiles.push_back(diffFile);diffFile->sFile = str->str.Right(-2);}else if ( diffFile ){if ( str->str.Left(4).Compare("+++:") ){SDiffEntryAddition *addition = new SDiffEntryAddition;addition->iID = str->str.GetToken(":", 2, 2).ToInt();addition->sEntry = str->str.GetToken(":", 3);diffFile->m_lEntries.push_back(addition);}else if ( str->str.Left(4).Compare("---:") ){SDiffEntryRemoval *entry = new SDiffEntryRemoval;entry->iID = str->str.GetToken(":", 2, 2).ToInt();diffFile->m_lEntries.push_back(entry);}else if ( str->str.Left(4).Compare("///:") ){SDiffEntryChange *entry = new SDiffEntryChange;entry->iID = str->str.GetToken(":", 2, 2).ToInt();entry->iPos = str->str.GetToken(":", 3, 3).ToInt();entry->sEntry = str->str.GetToken(":", 4);diffFile->m_lEntries.push_back(entry);}}}delete lines;return true;}return false;}bool CModDiff::ApplyMod(CyString &mod){return m_fileSystem.LoadMod(mod);}bool CModDiff::ApplyDiff(CyString &mod){if ( m_lFiles.empty() ) return false;bool ret = false;CyString addonDir = "";CCatFile cat;if ( !CCatFile::Opened(cat.Open(mod, addonDir, CATREAD_DAT, true)) )return false;for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() ){// extract the file from the gameSDiffFile *f = node->Data();CyStringList writeLines;int id;if ( ReadGameFile(f->sFile, &writeLines, &id) ){// now apply the difffor ( 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.GetAt(eNode->Data()->iID)->remove = true;break;case DIFFTYPE_CHANGE:{SStringList *strAt = writeLines.GetAt(eNode->Data()->iID);if ( strAt )strAt->str.RepToken(";", ((SDiffEntryChange *)eNode->Data())->iPos, ((SDiffEntryChange *)eNode->Data())->sEntry);}break;}}// add our comments and infowriteLines.PushFront(CyString((long)id) + ";" + (long)writeLines.Count() + ";");writeLines.PushFront(CyString("// Generated by ModDiff (SPK Version: ") + CyString::CreateFromFloat(GetLibraryVersion(), 2) + ")");// now write the fileCFileIO WriteFile(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename());if ( WriteFile.WriteFile(&writeLines) ){if ( cat.AppendFile(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename(), f->sFile) ){ret = true;}}}}if ( ret )cat.WriteCatFile();return ret;}bool CModDiff::ReadGameFile(CyString &file, CyStringList *writeLines, int *id){bool ret = false;if ( m_fileSystem.ExtractGameFile(file, m_sTempDir + "/" + CFileIO(file).GetFilename()) ){CFileIO File(m_sTempDir + "/" + CFileIO(file).GetFilename());CyStringList *lines = File.ReadLinesStr();if ( lines ){int entries = -1;for ( SStringList *str = lines->Head(); str; str = str->next ){CyString l = str->str;l.RemoveFirstSpace();if ( l.Empty() ) continue;if ( l.Left(1) == "/" ) continue;if ( entries == -1 ){entries = l.GetToken(":", 2, 2).ToInt();if ( id )(*id) = l.GetToken(":", 1, 1).ToInt();}elsewriteLines->PushBack(l);}delete lines;ret = true;}File.Remove();}return ret;}bool CModDiff::ValidFile(CyString &file){return CModDiff::CanBeDiffed(file);}bool CModDiff::CanBeDiffed(const CyString &file){CyString checkFile = file;checkFile = CFileIO(file).GetDir() + "/" + CFileIO(file).GetBaseName();// all t files are the same formatif ( checkFile.Left(7).Compare("types/T") )return true;return false;}