Subversion Repositories spk

Rev

Rev 124 | Rev 160 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 cycrow 1
 
2
#include "ModDiff.h"
3
#include "File_IO.h"
4
#include "CatFile.h"
5
 
58 cycrow 6
CModDiff::CModDiff(const Utils::String &dir, const Utils::String &sAddon, int maxPatch) : m_pCatFile(NULL), m_sAddon(sAddon), m_sTempDir("."), m_iMaxPatch(maxPatch)
1 cycrow 7
{
58 cycrow 8
	m_bLoaded = this->LoadDirectory(dir);
1 cycrow 9
}
10
 
11
CModDiff::~CModDiff(void)
12
{
58 cycrow 13
	delete m_pCatFile;
1 cycrow 14
}
15
 
58 cycrow 16
bool CModDiff::LoadDirectory(const Utils::String &dir)
1 cycrow 17
{
18
	m_sCurrentDir = dir;
101 cycrow 19
	m_fileSystem.setAddon(m_sAddon);
58 cycrow 20
	return m_fileSystem.LoadFilesystem(dir, m_iMaxPatch);
1 cycrow 21
}
22
 
58 cycrow 23
bool CModDiff::startDiff(const Utils::String &sModFile)
1 cycrow 24
{
25
	ClearError();
58 cycrow 26
	if ( !CFileIO::Exists(sModFile) ) { m_iError = MDERR_FILENOTFOUND; return false; }
1 cycrow 27
 
58 cycrow 28
	delete m_pCatFile;
29
	m_pCatFile = new CCatFile;
30
 
125 cycrow 31
	if ( m_pCatFile->open(sModFile, m_sAddon, CATREAD_CATDECRYPT, false) != CATERR_NONE ) { 
58 cycrow 32
		delete m_pCatFile;
33
		m_pCatFile = NULL;
34
		m_iError = MDERR_CANTOPENMOD; 
35
		return false; 
36
	}
37
 
38
	return true;
39
}
40
 
41
Utils::String CModDiff::_extractFile(const Utils::String &sFile, const Utils::String &sTo)
42
{
43
	SInCatFile *c = m_pCatFile->FindData(sFile);
44
	if ( !c ) c = m_pCatFile->FindData(m_sAddon + "/" + sFile);
45
	if ( !c ) return m_fileSystem.ExtractGameFile(sFile, sTo);
46
	if ( m_pCatFile->ExtractFile(c, sTo) ) return sTo;
47
	return "";
48
}
49
 
50
bool CModDiff::doDiff(const Utils::String &sModFile)
51
{
52
	// find the file in the loaded cat
53
	SInCatFile *c = m_pCatFile->FindData(sModFile);
54
	if ( !c ) c = m_pCatFile->FindData(m_sAddon + "/" + sModFile);
55
	if ( !c ) return false;
56
 
57
	// extract the matching file
58
	Utils::String sToFile = CFileIO(m_sTempDir + "/" + CFileIO(c->sFile).filename()).fullFilename();
59
	if ( !m_pCatFile->ExtractFile(sModFile, sToFile) ) return false;
60
 
61
	// create a diff
124 cycrow 62
	Utils::String to = m_fileSystem.ExtractGameFile(c->sFile, sToFile + ".compare");
58 cycrow 63
	if ( !to.empty() ) {
124 cycrow 64
		SDiffFile *diff = diffFile(to, sToFile, c->sFile);
58 cycrow 65
		if ( diff ) { 
66
			this->_adjustFile(sModFile, diff, false);
67
		}
68
		CFileIO::Remove(to);
69
	}
70
 
71
	CFileIO::Remove(sToFile);
72
 
73
	return true;
74
}
75
 
76
 
77
bool CModDiff::_adjustTShips(SDiffFile *pDiff, bool bReverse)
78
{
79
	// need to read TCockpits
80
	Utils::String sTo = this->_extractFile("types/TCockpits.pck", "TCockpits.xml");
81
	if ( sTo.empty() ) return false;
82
	CFileIO TCockpits("TCockpits.xml");
83
	if ( !TCockpits.exists() ) return false;
84
	CyStringList *pFiles = TCockpits.ReadLinesStr();
85
	if ( !pFiles ) return false;
86
 
87
	// remove all the comments
88
	for ( SStringList *str = pFiles->Head(); str; str = str->next ) {
89
		if ( str->str[0] == '/' ) str->remove = true;
90
	}
91
	pFiles->RemoveMarked();
92
	pFiles->PopFront();
93
 
84 cycrow 94
	std::map<Utils::String, int> fileSet;
58 cycrow 95
	int iPos = 0;
96
	for ( SStringList *str = pFiles->Head(); str; str = str->next ) fileSet[Utils::String(str->str.ToString()).token(";", -2)] = iPos++;
97
 
98
	// now cycle through ships and adjust the cockpits
99
	int iCount = -1;
100
	for ( SDiffEntry *pEntry = pDiff->m_lEntries.First(); pEntry; pEntry = pDiff->m_lEntries.Next() ) {
101
		switch (pEntry->iType) {
102
			case DIFFTYPE_ADDITION:
103
				{
104
					SDiffEntryAddition *pAddition = static_cast<SDiffEntryAddition *>(pEntry);
105
					if ( pAddition ) {
106
						for ( int t = 0; t < 6; t++ ) {
107
							Utils::String sE = pAddition->sEntry.token(";", 32 + (t * 2));
108
							if ( !bReverse && sE.isNumber() ) {
109
								int iTurret = sE;
110
								if ( iTurret && iTurret < pFiles->Count() ) {
111
									Utils::String sCockpit = pFiles->GetAt(iTurret)->str.ToString();
112
									pAddition->sEntry = pAddition->sEntry.replaceToken(";", 32 + (t * 2), sCockpit.token(";", -2) + ":" + static_cast<long>(iTurret));
113
								}
114
							}
115
							else if ( bReverse && !sE.isNumber() ) {
116
								int iUnfound = 0;
117
								if ( sE.isin(":") ) {
118
									iUnfound = sE.token(":", 2);
119
									sE = sE.token(":", 1);
120
								}
121
								int iEntry = (fileSet.find(sE) == fileSet.end()) ? iUnfound : fileSet[sE];
122
								pAddition->sEntry = pAddition->sEntry.replaceToken(";", 32 + (t * 2), static_cast<long>(iEntry));
123
							}
124
						}
125
					}
126
				}
127
				break;
128
			case DIFFTYPE_CHANGE:
129
				{
130
					SDiffEntryChange *pChange = static_cast<SDiffEntryChange *>(pEntry);
131
					if ( pChange->iPos >= 32 && pChange->iPos <= 42 && (pChange->iPos % 2) && pChange->sEntry.isNumber() ) {
132
						Utils::String sCockpit = pFiles->GetAt(pChange->sEntry)->str.ToString();
133
						pChange->sEntry = sCockpit.token(";", -2) + ":" + pChange->sEntry;
134
					}
135
				}
136
				break;
137
		}
138
	}
139
 
140
	delete pFiles;
141
 
142
	return true;
143
}
144
 
145
int CModDiff::_specialType(const Utils::String &sFile)
146
{
147
	if ( sFile.Compare("TShips") ) return MERGETYPE_TSHIPS;
148
	return MERGETYPE_NONE;
149
}
150
 
151
void CModDiff::_adjustFile(const Utils::String &sFile, SDiffFile *pDiff, bool bReverse)
152
{
153
	// check if we need to adjust the file
154
	CFileIO File(sFile);
155
	int iType = _specialType(File.baseName());
156
	if ( iType == MERGETYPE_NONE ) return;
157
 
158
	// read in the file data
159
	switch(iType) {
160
		case MERGETYPE_TSHIPS:
161
			this->_adjustTShips(pDiff, bReverse);
162
			break;
163
	}
164
}
165
 
166
bool CModDiff::CreateDiff(const Utils::String &modfile)
167
{
168
	ClearError();
169
 
1 cycrow 170
	//check for valid parameters
52 cycrow 171
	if ( !CFileIO(modfile).ExistsOld() ) { m_iError = MDERR_FILENOTFOUND; return false; }
1 cycrow 172
 
58 cycrow 173
	Utils::String addonDir = "";
174
	int addonSize = addonDir.length() + 1;
1 cycrow 175
 
176
	// try and open the mod file
177
	CCatFile cat;
125 cycrow 178
	if ( cat.open(modfile, addonDir, CATREAD_DAT, false) != CATERR_NONE ) { m_iError = MDERR_CANTOPENMOD; return false; }
1 cycrow 179
 
180
	// we'll need to read in all the types/text files
125 cycrow 181
	for (unsigned int i = 0; i < cat.GetNumFiles(); i++)
1 cycrow 182
	{
183
		SInCatFile *f = cat.GetFile(i);
124 cycrow 184
		Utils::String checkFile = f->sFile.findReplace("\\", "/");
58 cycrow 185
		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) )
1 cycrow 186
		{
187
			// extract the file to the temp dir
58 cycrow 188
			Utils::String toFile = CFileIO(m_sTempDir + "/" + CFileIO(f->sFile).filename()).fullFilename();
1 cycrow 189
			if ( cat.ExtractFile(f, toFile ) )
190
			{
191
				// now extract the matching file from the game dir
124 cycrow 192
				if ( m_fileSystem.ExtractGameFile(f->sFile, toFile + ".compare") )
1 cycrow 193
				{
124 cycrow 194
					diffFile(toFile + ".compare", toFile, f->sFile);
58 cycrow 195
					CFileIO::Remove(toFile + ".compare");
1 cycrow 196
				}
197
				// make sure we clear up afterwards
58 cycrow 198
				CFileIO::Remove(toFile);
1 cycrow 199
			}
200
		}
201
	}
202
 
203
	return true;
204
}
205
 
58 cycrow 206
SDiffFile *CModDiff::diffFile(const Utils::String &baseFile, const Utils::String &modFile, const Utils::String &fileType)
1 cycrow 207
{
208
	int type = 0;
209
 
210
	SDiffFile *diffFile = new SDiffFile;
211
	diffFile->sFile = fileType;
212
 
213
	// read both files and compare them
214
	CFileIO Base(baseFile);
215
	CyStringList *baseLines = Base.ReadLinesStr();
216
	if ( baseLines )
217
	{
218
		CFileIO Mod(modFile);
219
		CyStringList *lines = Mod.ReadLinesStr();
220
		if ( lines )
221
		{
222
			int id = -1;
223
			SStringList *node[2];
224
			node[0] = baseLines->Head();
225
			node[1] = lines->Head();
226
 
58 cycrow 227
			Utils::String prev[2];
1 cycrow 228
 
229
			while ( node[0] || node[1] )
230
			{
58 cycrow 231
				Utils::String str[2];
1 cycrow 232
 
233
				for ( int i = 0; i < 2; i++ )
234
				{
235
					while (node[i])
236
					{
58 cycrow 237
						Utils::String l = node[i]->str.ToString();
1 cycrow 238
						node[i] = node[i]->next;
239
 
58 cycrow 240
						l.removeFirstSpace();
241
						l.removeChar('\r');
242
						if ( !l.empty() && l.left(1) != "/") {
1 cycrow 243
							str[i] += l;
58 cycrow 244
							if ( _isLineComplete(str[i], fileType, (id == -1) ? true : false) ) 
1 cycrow 245
								break;
246
						}
247
					}
248
				}
249
 
250
				if ( id == -1 )
251
					id = 0;
252
				else
253
				{
254
					// first check for mismatch amount, one of the nodes will be empty
58 cycrow 255
					if ( str[0].empty() && !str[1].empty() ) // mod file has more entries (these must be additions)
1 cycrow 256
					{
257
						SDiffEntryAddition *entry = new SDiffEntryAddition;
258
						entry->iID = id;
259
						entry->sEntry = str[1];
260
						diffFile->m_lEntries.push_back(entry);
261
					}
58 cycrow 262
					else if ( str[1].empty() && !str[0].empty() ) // mod file has less entries (must have removed some)
1 cycrow 263
					{
264
						SDiffEntry *entry = new SDiffEntryRemoval;
265
						entry->iID = id;
266
						diffFile->m_lEntries.push_back(entry);
267
					}
268
					else // we have a line for both, we need to compare them
269
					{
270
						// we migth have multiple entries to add when changed
58 cycrow 271
						if ( str[0].empty() || str[1].empty() ) continue;
272
						_compareLine(str[0], str[1], type, id, diffFile);						
1 cycrow 273
					}
274
 
275
					++id;
276
				}
277
			}
278
 
279
			delete lines;
280
		}
281
		delete baseLines;
282
	}
283
 
58 cycrow 284
	if ( diffFile->m_lEntries.empty() ) {
1 cycrow 285
		delete diffFile;
58 cycrow 286
		return NULL;
287
	}
288
	else {
1 cycrow 289
		m_lFiles.push_back(diffFile);
58 cycrow 290
		return diffFile;
291
	}
1 cycrow 292
}
293
 
58 cycrow 294
int CModDiff::_amountPosition(const Utils::String &fileType)
1 cycrow 295
{
296
	return 2;
297
}
298
 
58 cycrow 299
bool CModDiff::_isLineComplete(const Utils::String &line, const Utils::String &fileType, bool first)
1 cycrow 300
{
301
	if ( first )
302
	{
58 cycrow 303
		if ( line.countToken(";") > _amountPosition(fileType) ) return true;
1 cycrow 304
		return false;
305
	}
306
 
307
	return true;
308
}
309
 
58 cycrow 310
void CModDiff::_compareLine(const Utils::String &line1, const Utils::String &line2, int type, int id, SDiffFile *diffFile)
1 cycrow 311
{
312
	int max1, max2;
58 cycrow 313
	Utils::String *str1 = line1.tokenise(";", &max1);
314
	Utils::String *str2 = line2.tokenise(";", &max2);
1 cycrow 315
 
316
	if ( !str1 || !str2 ) return;
317
 
318
	int max = ((max1 > max2) ? max2 : max1);
319
	for ( int i = 0; i < max; i++ )
320
	{
321
		if ( str1[i] == str2[i] ) continue;
322
		if ( str1[i].Compare(str2[i]) ) continue;
58 cycrow 323
		if ( str1[i].empty() && str2[i].empty() ) continue;
324
		if ( str1[i].length() && str1[i][0] == '\0' && str2[i].length() && str2[i][0] == '\0' ) continue;
1 cycrow 325
		SDiffEntryChange *diff = new SDiffEntryChange;
326
		diff->iID = id;
327
		diff->iPos = i;
328
		diff->sEntry = str2[i];
329
		diff->sFrom = str1[i];
330
		diffFile->m_lEntries.push_back(diff);
331
	}
332
}
333
 
334
void CModDiff::Clean()
335
{
336
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
337
	{
338
		node->Data()->m_lEntries.clear();
339
		node->DeleteData();
340
	}
341
	m_lFiles.clear();
342
}
343
 
58 cycrow 344
bool CModDiff::WriteDiff(const Utils::String &file)
1 cycrow 345
{
346
	if ( m_lFiles.empty() ) return false;
347
 
348
	CyStringList lines;
349
 
350
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
351
	{
352
		lines.PushBack(CyString("$$") + node->Data()->sFile);
353
		for ( CListNode<SDiffEntry> *node2 = node->Data()->m_lEntries.Front(); node2; node2 = node2->next() )
354
		{
355
			switch(node2->Data()->iType)
356
			{
357
				case DIFFTYPE_ADDITION:
358
					lines.PushBack(CyString("+++:") + (long)node2->Data()->iID + ":" + ((SDiffEntryAddition *)node2->Data())->sEntry);
359
					break;
360
				case DIFFTYPE_REMOVAL:
361
					lines.PushBack(CyString("---:") + (long)node2->Data()->iID);
362
					break;
363
				case DIFFTYPE_CHANGE:
364
					lines.PushBack(CyString("///:") + (long)node2->Data()->iID + ":" + (long)((SDiffEntryChange *)node2->Data())->iPos + ":" + ((SDiffEntryChange *)node2->Data())->sEntry);
365
					break;
366
			}
367
		}
368
	}
369
 
370
 
371
	CFileIO File(file);
372
	return File.WriteFile(&lines);
373
}
374
 
58 cycrow 375
bool CModDiff::ReadDiff(const Utils::String &file)
1 cycrow 376
{
377
	Clean();
378
 
379
	CFileIO File(file);
52 cycrow 380
	if ( !File.exists() ) return false;
1 cycrow 381
 
382
	CyStringList *lines = File.ReadLinesStr();
383
 
384
	if ( lines )
385
	{
386
		SDiffFile *diffFile = NULL;
387
		for ( SStringList *str = lines->Head(); str; str = str->next )
388
		{
389
			if ( str->str.Left(2).Compare("$$") )
390
			{
391
				diffFile = new SDiffFile;
392
				m_lFiles.push_back(diffFile);
58 cycrow 393
				diffFile->sFile = str->str.Right(-2).ToString();
1 cycrow 394
			}
395
			else if ( diffFile )
396
			{
397
				if ( str->str.Left(4).Compare("+++:") )
398
				{
399
					SDiffEntryAddition *addition = new SDiffEntryAddition;
400
					addition->iID = str->str.GetToken(":", 2, 2).ToInt();
58 cycrow 401
					addition->sEntry = str->str.GetToken(":", 3).ToString();
1 cycrow 402
					diffFile->m_lEntries.push_back(addition);
403
				}
404
				else if ( str->str.Left(4).Compare("---:") )
405
				{
406
					SDiffEntryRemoval *entry = new SDiffEntryRemoval;
407
					entry->iID = str->str.GetToken(":", 2, 2).ToInt();
408
					diffFile->m_lEntries.push_back(entry);
409
				}
410
				else if ( str->str.Left(4).Compare("///:") )
411
				{
412
					SDiffEntryChange *entry = new SDiffEntryChange;
413
					entry->iID = str->str.GetToken(":", 2, 2).ToInt();
414
					entry->iPos = str->str.GetToken(":", 3, 3).ToInt();
58 cycrow 415
					entry->sEntry = str->str.GetToken(":", 4).ToString();
1 cycrow 416
					diffFile->m_lEntries.push_back(entry);
417
				}
418
			}
419
		}
420
 
421
		delete lines;
422
 
423
		return true;
424
	}
425
 
426
	return false;
427
}
428
 
58 cycrow 429
bool CModDiff::ApplyMod(const Utils::String &mod)
1 cycrow 430
{
58 cycrow 431
	return m_fileSystem.addMod(mod);
1 cycrow 432
}
433
 
58 cycrow 434
bool CModDiff::ApplyDiff(const Utils::String &mod)
1 cycrow 435
{
436
	if ( m_lFiles.empty() ) return false;
437
 
58 cycrow 438
	this->ApplyMod(mod);
439
 
1 cycrow 440
	bool ret = false;
441
 
125 cycrow 442
	Utils::String addonDir = "";
1 cycrow 443
 
444
	CCatFile cat;
125 cycrow 445
	if ( !CCatFile::Opened(cat.open(mod, addonDir, CATREAD_CATDECRYPT, true)) )
1 cycrow 446
		return false;
447
 
448
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
449
	{
450
		// extract the file from the game
451
		SDiffFile *f = node->Data();
452
 
453
		CyStringList writeLines;
454
		int id;
58 cycrow 455
		if ( _readGameFile(f->sFile, &writeLines, &id) )
1 cycrow 456
		{
457
			// now apply the diff
58 cycrow 458
			this->_adjustFile(f->sFile, f, true);
1 cycrow 459
			for ( CListNode<SDiffEntry> *eNode = f->m_lEntries.Front(); eNode; eNode = eNode->next() )
460
			{
461
				switch ( eNode->Data()->iType )
462
				{
463
					case DIFFTYPE_ADDITION:
58 cycrow 464
						writeLines.PushBack(CyString(((SDiffEntryAddition *)eNode->Data())->sEntry));
1 cycrow 465
						break;
466
					case DIFFTYPE_REMOVAL:
467
						writeLines.GetAt(eNode->Data()->iID)->remove = true;
468
						break;
469
					case DIFFTYPE_CHANGE:
470
						{
471
							SStringList *strAt = writeLines.GetAt(eNode->Data()->iID);
472
							if ( strAt )
473
								strAt->str.RepToken(";", ((SDiffEntryChange *)eNode->Data())->iPos, ((SDiffEntryChange *)eNode->Data())->sEntry);
474
						}
475
						break;
476
				}
477
			}
478
 
479
			// add our comments and info
480
			writeLines.PushFront(CyString((long)id) + ";" + (long)writeLines.Count() + ";");
481
			writeLines.PushFront(CyString("// Generated by ModDiff (SPK Version: ") + CyString::CreateFromFloat(GetLibraryVersion(), 2) + ")");
482
 
483
			// now write the file
58 cycrow 484
			CFileIO WriteFile(m_sTempDir + "/" + CFileIO(f->sFile).filename());
1 cycrow 485
			if ( WriteFile.WriteFile(&writeLines) )
486
			{
58 cycrow 487
				if ( cat.AppendFile(m_sTempDir + "/" + CFileIO(f->sFile).filename(), f->sFile) )
1 cycrow 488
				{
489
					ret = true;
490
				}
58 cycrow 491
				WriteFile.remove();
1 cycrow 492
			}
493
		}
494
	}
495
 
496
	if ( ret )
497
		cat.WriteCatFile();
498
 
499
	return ret;
500
}
501
 
58 cycrow 502
bool CModDiff::_readGameFile(const Utils::String &file, CyStringList *writeLines, int *id)
1 cycrow 503
{
504
	bool ret = false;
505
 
58 cycrow 506
	Utils::String sTo = m_fileSystem.ExtractGameFile(file, m_sTempDir + "/" + CFileIO(file).filename());
507
	if ( !sTo.empty() ) {
508
		CFileIO File(sTo);
1 cycrow 509
 
510
		CyStringList *lines = File.ReadLinesStr();
511
		if ( lines )
512
		{
513
			int entries = -1;
514
			for ( SStringList *str = lines->Head(); str; str = str->next )
515
			{
516
				CyString l = str->str;
517
				l.RemoveFirstSpace();
518
				if ( l.Empty() ) continue;
519
				if ( l.Left(1) == "/" ) continue;
520
 
521
				if ( entries == -1 )
522
				{
523
					entries = l.GetToken(":", 2, 2).ToInt();
524
					if ( id )
525
						(*id) = l.GetToken(":", 1, 1).ToInt();
526
				}
527
				else
528
					writeLines->PushBack(l);
529
			}
530
			delete lines;
531
			ret = true;
532
		}
58 cycrow 533
		File.close();
52 cycrow 534
		File.remove();
1 cycrow 535
	}
536
 
537
	return ret;
538
}
539
 
540
 
58 cycrow 541
bool CModDiff::_validFile(const Utils::String &file)
1 cycrow 542
{
543
	return CModDiff::CanBeDiffed(file);
544
}
545
 
58 cycrow 546
bool CModDiff::CanBeDiffed(const Utils::String &file)
1 cycrow 547
{
58 cycrow 548
	Utils::String checkFile = file;
102 cycrow 549
	checkFile = CFileIO(file).dir() + "/" + CFileIO(file).baseName();
1 cycrow 550
 
551
	// all t files are the same format
58 cycrow 552
	if ( checkFile.left(7).Compare("types/T") )
1 cycrow 553
		return true;
554
 
555
	return false;
35 cycrow 556
}