Subversion Repositories spk

Rev

Rev 35 | 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
 
6
CModDiff::CModDiff(CyString &dir, int maxPatch)
7
{
8
	m_bLoaded = LoadDirectory(dir);
9
	m_sTempDir = ".";
10
	m_iMaxPatch = maxPatch;
11
}
12
 
13
CModDiff::~CModDiff(void)
14
{
15
}
16
 
17
bool CModDiff::LoadDirectory(CyString &dir)
18
{
19
	m_sCurrentDir = dir;
35 cycrow 20
	return m_fileSystem.LoadFilesystem(dir.ToString(), m_iMaxPatch);
1 cycrow 21
}
22
 
23
bool CModDiff::CreateDiff(CyString &modfile)
24
{
25
	ClearError();
26
 
27
	//check for valid parameters
28
	if ( !CFileIO(modfile).Exists() ) { m_iError = MDERR_FILENOTFOUND; return false; }
29
 
30
	CyString addonDir = "";
31
	int addonSize = addonDir.Length() + 1;
32
 
33
	// try and open the mod file
34
	CCatFile cat;
35
	if ( cat.Open(modfile, addonDir, CATREAD_DAT, false) != CATERR_NONE ) { m_iError = MDERR_CANTOPENMOD; return false; }
36
 
37
	// we'll need to read in all the types/text files
38
	for ( int i = 0; i < cat.GetNumFiles(); i++ )
39
	{
40
		SInCatFile *f = cat.GetFile(i);
41
		CyString checkFile = f->sFile.FindReplace("\\", "/");
42
		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) )
43
		{
44
			// extract the file to the temp dir
45
			CyString toFile = CFileIO(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename()).GetFullFilename();
46
			if ( cat.ExtractFile(f, toFile ) )
47
			{
48
				// now extract the matching file from the game dir
35 cycrow 49
				if ( m_fileSystem.ExtractGameFile(f->sFile.ToString(), (toFile + ".compare").ToString()) )
1 cycrow 50
				{
51
					DiffFile(toFile + ".compare", toFile, f->sFile);
52
					CFileIO(toFile + ".compare").Remove();
53
				}
54
				// make sure we clear up afterwards
55
				CFileIO(toFile).Remove();
56
			}
57
		}
58
	}
59
 
60
	return true;
61
}
62
 
63
bool CModDiff::DiffFile(CyString &baseFile, CyString &modFile, CyString &fileType)
64
{
65
	int type = 0;
66
 
67
	SDiffFile *diffFile = new SDiffFile;
68
	diffFile->sFile = fileType;
69
 
70
	// read both files and compare them
71
	CFileIO Base(baseFile);
72
	CyStringList *baseLines = Base.ReadLinesStr();
73
	if ( baseLines )
74
	{
75
		CFileIO Mod(modFile);
76
		CyStringList *lines = Mod.ReadLinesStr();
77
		if ( lines )
78
		{
79
			int id = -1;
80
			SStringList *node[2];
81
			node[0] = baseLines->Head();
82
			node[1] = lines->Head();
83
 
84
			CyString prev[2];
85
 
86
			while ( node[0] || node[1] )
87
			{
88
				CyString str[2];
89
 
90
				for ( int i = 0; i < 2; i++ )
91
				{
92
					while (node[i])
93
					{
94
						CyString l = node[i]->str;
95
						node[i] = node[i]->next;
96
 
97
						l.RemoveFirstSpace();
98
						l.RemoveChar('\r');
99
						if ( !l.Empty() && l.Left(1) != "/") 
100
						{
101
							str[i] += l;
102
							if ( IsLineComplete(str[i], fileType, (id == -1) ? true : false) ) 
103
								break;
104
						}
105
					}
106
				}
107
 
108
				if ( id == -1 )
109
					id = 0;
110
				else
111
				{
112
					// first check for mismatch amount, one of the nodes will be empty
113
					if ( str[0].Empty() && !str[1].Empty() ) // mod file has more entries (these must be additions)
114
					{
115
						SDiffEntryAddition *entry = new SDiffEntryAddition;
116
						entry->iID = id;
117
						entry->sEntry = str[1];
118
						diffFile->m_lEntries.push_back(entry);
119
					}
120
					else if ( str[1].Empty() && !str[0].Empty() ) // mod file has less entries (must have removed some)
121
					{
122
						SDiffEntry *entry = new SDiffEntryRemoval;
123
						entry->iID = id;
124
						diffFile->m_lEntries.push_back(entry);
125
					}
126
					else // we have a line for both, we need to compare them
127
					{
128
						// we migth have multiple entries to add when changed
129
						if ( str[0].Empty() || str[1].Empty() ) continue;
130
						CompareLine(str[0], str[1], type, id, diffFile);						
131
					}
132
 
133
					++id;
134
				}
135
			}
136
 
137
			delete lines;
138
		}
139
		delete baseLines;
140
	}
141
 
142
	if ( diffFile->m_lEntries.empty() )
143
		delete diffFile;
144
	else
145
		m_lFiles.push_back(diffFile);
146
 
147
	return true;
148
}
149
 
150
int CModDiff::GetAmountPosition(const CyString &fileType)
151
{
152
	return 2;
153
}
154
 
155
bool CModDiff::IsLineComplete(CyString &line, CyString &fileType, bool first)
156
{
157
	if ( first )
158
	{
159
		if ( line.NumToken(";") > GetAmountPosition(fileType) ) return true;
160
		return false;
161
	}
162
 
163
	return true;
164
}
165
 
166
void CModDiff::CompareLine(CyString &line1, CyString &line2, int type, int id, SDiffFile *diffFile)
167
{
168
	int max1, max2;
169
	CyString *str1 = line1.SplitToken(";", &max1);
170
	CyString *str2 = line2.SplitToken(";", &max2);
171
 
172
	if ( !str1 || !str2 ) return;
173
 
174
	int max = ((max1 > max2) ? max2 : max1);
175
	for ( int i = 0; i < max; i++ )
176
	{
177
		if ( str1[i] == str2[i] ) continue;
178
		if ( str1[i].Compare(str2[i]) ) continue;
179
		if ( str1[i].Empty() && str2[i].Empty() ) continue;
180
		if ( str1[i].Length() && str1[i][0] == '\0' && str2[i].Length() && str2[i][0] == '\0' ) continue;
181
		SDiffEntryChange *diff = new SDiffEntryChange;
182
		diff->iID = id;
183
		diff->iPos = i;
184
		diff->sEntry = str2[i];
185
		diff->sFrom = str1[i];
186
		diffFile->m_lEntries.push_back(diff);
187
	}
188
}
189
 
190
void CModDiff::Clean()
191
{
192
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
193
	{
194
		node->Data()->m_lEntries.clear();
195
		node->DeleteData();
196
	}
197
	m_lFiles.clear();
198
}
199
 
200
bool CModDiff::WriteDiff(CyString &file)
201
{
202
	if ( m_lFiles.empty() ) return false;
203
 
204
	CyStringList lines;
205
 
206
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
207
	{
208
		lines.PushBack(CyString("$$") + node->Data()->sFile);
209
		for ( CListNode<SDiffEntry> *node2 = node->Data()->m_lEntries.Front(); node2; node2 = node2->next() )
210
		{
211
			switch(node2->Data()->iType)
212
			{
213
				case DIFFTYPE_ADDITION:
214
					lines.PushBack(CyString("+++:") + (long)node2->Data()->iID + ":" + ((SDiffEntryAddition *)node2->Data())->sEntry);
215
					break;
216
				case DIFFTYPE_REMOVAL:
217
					lines.PushBack(CyString("---:") + (long)node2->Data()->iID);
218
					break;
219
				case DIFFTYPE_CHANGE:
220
					lines.PushBack(CyString("///:") + (long)node2->Data()->iID + ":" + (long)((SDiffEntryChange *)node2->Data())->iPos + ":" + ((SDiffEntryChange *)node2->Data())->sEntry);
221
					break;
222
			}
223
		}
224
	}
225
 
226
 
227
	CFileIO File(file);
228
	return File.WriteFile(&lines);
229
}
230
 
231
bool CModDiff::ReadDiff(CyString &file)
232
{
233
	Clean();
234
 
235
	CFileIO File(file);
236
	if ( !File.Exists() ) return false;
237
 
238
	CyStringList *lines = File.ReadLinesStr();
239
 
240
	if ( lines )
241
	{
242
		SDiffFile *diffFile = NULL;
243
		for ( SStringList *str = lines->Head(); str; str = str->next )
244
		{
245
			if ( str->str.Left(2).Compare("$$") )
246
			{
247
				diffFile = new SDiffFile;
248
				m_lFiles.push_back(diffFile);
249
				diffFile->sFile = str->str.Right(-2);
250
			}
251
			else if ( diffFile )
252
			{
253
				if ( str->str.Left(4).Compare("+++:") )
254
				{
255
					SDiffEntryAddition *addition = new SDiffEntryAddition;
256
					addition->iID = str->str.GetToken(":", 2, 2).ToInt();
257
					addition->sEntry = str->str.GetToken(":", 3);
258
					diffFile->m_lEntries.push_back(addition);
259
				}
260
				else if ( str->str.Left(4).Compare("---:") )
261
				{
262
					SDiffEntryRemoval *entry = new SDiffEntryRemoval;
263
					entry->iID = str->str.GetToken(":", 2, 2).ToInt();
264
					diffFile->m_lEntries.push_back(entry);
265
				}
266
				else if ( str->str.Left(4).Compare("///:") )
267
				{
268
					SDiffEntryChange *entry = new SDiffEntryChange;
269
					entry->iID = str->str.GetToken(":", 2, 2).ToInt();
270
					entry->iPos = str->str.GetToken(":", 3, 3).ToInt();
271
					entry->sEntry = str->str.GetToken(":", 4);
272
					diffFile->m_lEntries.push_back(entry);
273
				}
274
			}
275
		}
276
 
277
		delete lines;
278
 
279
		return true;
280
	}
281
 
282
	return false;
283
}
284
 
285
bool CModDiff::ApplyMod(CyString &mod)
286
{
35 cycrow 287
	return m_fileSystem.addMod(mod.ToString());
1 cycrow 288
}
289
 
290
bool CModDiff::ApplyDiff(CyString &mod)
291
{
292
	if ( m_lFiles.empty() ) return false;
293
 
294
	bool ret = false;
295
 
296
	CyString addonDir = "";
297
 
298
	CCatFile cat;
299
	if ( !CCatFile::Opened(cat.Open(mod, addonDir, CATREAD_DAT, true)) )
300
		return false;
301
 
302
	for ( CListNode<SDiffFile> *node = m_lFiles.Front(); node; node = node->next() )
303
	{
304
		// extract the file from the game
305
		SDiffFile *f = node->Data();
306
 
307
		CyStringList writeLines;
308
		int id;
309
		if ( ReadGameFile(f->sFile, &writeLines, &id) )
310
		{
311
			// now apply the diff
312
			for ( CListNode<SDiffEntry> *eNode = f->m_lEntries.Front(); eNode; eNode = eNode->next() )
313
			{
314
				switch ( eNode->Data()->iType )
315
				{
316
					case DIFFTYPE_ADDITION:
317
						writeLines.PushBack(((SDiffEntryAddition *)eNode->Data())->sEntry);
318
						break;
319
					case DIFFTYPE_REMOVAL:
320
						writeLines.GetAt(eNode->Data()->iID)->remove = true;
321
						break;
322
					case DIFFTYPE_CHANGE:
323
						{
324
							SStringList *strAt = writeLines.GetAt(eNode->Data()->iID);
325
							if ( strAt )
326
								strAt->str.RepToken(";", ((SDiffEntryChange *)eNode->Data())->iPos, ((SDiffEntryChange *)eNode->Data())->sEntry);
327
						}
328
						break;
329
				}
330
			}
331
 
332
			// add our comments and info
333
			writeLines.PushFront(CyString((long)id) + ";" + (long)writeLines.Count() + ";");
334
			writeLines.PushFront(CyString("// Generated by ModDiff (SPK Version: ") + CyString::CreateFromFloat(GetLibraryVersion(), 2) + ")");
335
 
336
			// now write the file
337
			CFileIO WriteFile(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename());
338
			if ( WriteFile.WriteFile(&writeLines) )
339
			{
340
				if ( cat.AppendFile(m_sTempDir + "/" + CFileIO(f->sFile).GetFilename(), f->sFile) )
341
				{
342
					ret = true;
343
				}
344
			}
345
		}
346
	}
347
 
348
	if ( ret )
349
		cat.WriteCatFile();
350
 
351
	return ret;
352
}
353
 
354
bool CModDiff::ReadGameFile(CyString &file, CyStringList *writeLines, int *id)
355
{
356
	bool ret = false;
357
 
35 cycrow 358
	if ( m_fileSystem.ExtractGameFile(file.ToString(), (m_sTempDir + "/" + CFileIO(file).GetFilename()).ToString()) )
1 cycrow 359
	{
360
		CFileIO File(m_sTempDir + "/" + CFileIO(file).GetFilename());
361
 
362
		CyStringList *lines = File.ReadLinesStr();
363
		if ( lines )
364
		{
365
			int entries = -1;
366
			for ( SStringList *str = lines->Head(); str; str = str->next )
367
			{
368
				CyString l = str->str;
369
				l.RemoveFirstSpace();
370
				if ( l.Empty() ) continue;
371
				if ( l.Left(1) == "/" ) continue;
372
 
373
				if ( entries == -1 )
374
				{
375
					entries = l.GetToken(":", 2, 2).ToInt();
376
					if ( id )
377
						(*id) = l.GetToken(":", 1, 1).ToInt();
378
				}
379
				else
380
					writeLines->PushBack(l);
381
			}
382
			delete lines;
383
			ret = true;
384
		}
385
		File.Remove();
386
	}
387
 
388
	return ret;
389
}
390
 
391
 
392
bool CModDiff::ValidFile(CyString &file)
393
{
394
	return CModDiff::CanBeDiffed(file);
395
}
396
 
397
bool CModDiff::CanBeDiffed(const CyString &file)
398
{
399
	CyString checkFile = file;
400
	checkFile = CFileIO(file).GetDir() + "/" + CFileIO(file).GetBaseName();
401
 
402
	// all t files are the same format
403
	if ( checkFile.Left(7).Compare("types/T") )
404
		return true;
405
 
406
	return false;
35 cycrow 407
}