Subversion Repositories spk

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 cycrow 1
#include "spk.h"
2
#include "emp.h"
3
 
4
#include <time.h>
5
#include <vector>
6
 
7
#ifdef _RAR
8
#include <unrar.h>
9
#endif
10
 
11
enum {READ_START, READ_GLOBAL, READ_SCRIPT, READ_SCRIPTFILE, READ_WARES};
12
enum { EXTRACT, TEST, PRINT, LIST };
13
 
14
typedef struct SDummyEntry {
15
	CyString		sSection;
16
	CyStringList	lEntries;
17
} SDummyEntry;
18
 
19
typedef struct SComponantEntry2 {
20
	CyString		sSection;
21
	CyStringList	lEntries;
22
} SComponantEntry2;
23
 
24
typedef struct SComponantEntry {
25
	CyString		sSection;
26
	CLinkList<SComponantEntry2>	lEntries;
27
} SComponantEntry;
28
 
29
CPackages::CPackages()
30
{
31
	m_bRenameText = false;
32
	m_iLanguage = 0;
33
	m_iGame = -1;
34
	m_iGameVersion = -1;
35
	m_iGameFlags = 0;
36
	m_bSurpressProtectedWarning = false;
37
	m_iMaxPatch = 1;
38
	m_iLastUpdated = 0;
39
	m_iFakePatch = -1;
40
	m_bVanilla = true;
41
	m_pEnabledMod = NULL;
42
	m_bForceModInstall = false;
43
	m_bLoaded = false;
44
	m_bRedo = false;
45
	m_bOldPlugin = false;
46
	m_bUsedWare = false;
47
	m_pPackageNode = NULL;
48
	m_bRemoveDir = false;
49
	m_bDisableVanilla = false;
50
	m_bLoadVFS = true;
51
	m_iSaveGame = -1;
52
	m_iSaveGameManager = -1;
53
	m_bForceEMP = false;
54
 
55
	for ( int i = 0; i < WAREBUFFERS; i++ )
56
		m_iWareBuffer[i] = 0;
57
	m_iShipBuffer = 0;
58
}
59
 
60
CPackages::~CPackages ()
61
{
62
	m_lDisableList.clear();
63
	m_lEnableList.clear();
64
	this->Reset();
65
}
66
 
67
void CPackages::SetVanilla(bool b)
68
{
69
	if ( m_bVanilla != b && b ) {
70
		for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() ) {
71
			pNode->Data()->SetModifiedEnabled(pNode->Data()->IsEnabled());
72
		}
73
	}
74
	m_bVanilla = b;
75
}
76
 
77
void CPackages::Reset()
78
{
79
	m_lFiles.MemoryClear();
80
	m_lUninstallFiles.MemoryClear();
81
	for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
82
		pNode->Data()->GetFileList()->clear();
83
	m_lPackages.MemoryClear();
84
	m_lOriginalFiles.MemoryClear();
85
	m_iFakePatch = -1;
86
	m_bVanilla = false;
87
	m_pEnabledMod = NULL;
88
	m_bLoaded = false;
89
	m_iLanguage = 0;
90
	m_bOldPlugin = false;
91
	m_bRemoveDir = false;
92
	m_bDisableVanilla = false;
93
	m_bSurpressProtectedWarning = false;
94
	m_iSaveGame = -1;
95
	m_iSaveGameManager = -1;
96
	m_sSetMod = "";
97
 
98
	for ( int i = 0; i < WAREBUFFERS; i++ )
99
	{
100
		m_iWareBuffer[i] = 0;
101
		m_lGameWares[i].MemoryClear();
102
	}
103
	m_iShipBuffer = 0;
104
 
105
	m_lGameShips.MemoryClear();
106
	m_lAvailablePackages.MemoryClear();
107
	m_lInstallList.MemoryClear();
108
	m_lEnableList.MemoryClear();
109
	m_lDisableList.MemoryClear();
110
 
111
	m_lCreatedFiles.Clear();
112
	m_lNonRemovedFiles.Clear();
113
	m_lGlobals.Clear();
114
	m_lFakePatchOrder.Clear();
115
}
116
 
117
void CPackages::LoadVirtualFileSystem()
118
{
119
	if ( !m_bLoadVFS )
120
		return;
121
 
122
	if ( m_pEnabledMod )
123
	{
124
		C_File *f;
125
		for ( f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); f; f = m_pEnabledMod->GetNextFile(f) )
126
		{
127
			if ( f->IsFakePatch() ) continue;
128
			if ( f->CheckFileExt("cat") ) break;
129
		}
130
 
131
		if ( f )
132
			m_pGameVFS.LoadFilesystem(m_sCurrentDir, f->GetFilePointer(), 0);
133
		else
134
			m_pGameVFS.LoadFilesystem(m_sCurrentDir, NullString, 0);
135
	}
136
	else
137
		m_pGameVFS.LoadFilesystem(m_sCurrentDir, NullString, 0);
138
}
139
 
140
bool CPackages::IsOldDir(CyString dir)
141
{
142
	bool oldPlugin = false;
143
 
144
	dir = dir.FindReplace("\\", "/");
145
	CFileIO datFile(dir + "/PluginManager/pluginmanager.dat");
146
	if ( datFile.Exists() )
147
	{
148
		std::vector<CyString> *readFile = datFile.ReadLines();
149
		if ( readFile )
150
		{
151
			for ( int i = 0; i < (int)readFile->size(); i++ )
152
			{
153
				CyString line(readFile->at(i));
154
				CyString cmd = line.GetToken(":", 1, 1).lower();
155
				if ( cmd == "<script>" || cmd == "</scripts>" )
156
					break;
157
				else if ( cmd == "spkinstaller" || cmd == "globalfiles" )
158
					break;
159
				else if ( cmd == "pluginmanager" )
160
				{
161
					oldPlugin = true;
162
					break;
163
				}
164
			}
165
 
166
			delete readFile;
167
		}
168
	}
169
 
170
	return oldPlugin;
171
}
172
 
173
bool CPackages::Read ( CyString dir, CProgressInfo *progress )
174
{
175
	m_sCurrentDir = dir;
176
	m_sCurrentDir = m_sCurrentDir.FindReplace("\\", "/");
177
	m_bOldPlugin = false;
178
	m_lCreatedFiles.Clear();
179
	m_bRemoveDir = false;
180
	m_bSurpressProtectedWarning = false;
181
	m_iSaveGame = -1;
182
	m_iSaveGameManager = -1;
183
	m_lNonRemovedFiles.Clear();
184
	m_lGlobals.Clear();
185
	m_lFakePatchOrder.Clear();
186
 
187
	m_bVanilla = true;
188
 
189
	// check the pluginmanager data file exists
190
	CFileIO datFile(m_sCurrentDir + "/PluginManager/pluginmanager.dat");
191
	if ( datFile.Exists() )
192
	{
193
		std::vector<CyString> *readFile = datFile.ReadLines();
194
		if ( readFile )
195
		{
196
			float fVersion = 0;
197
			bool spkinstaller = false;
198
			float fBeta = 0;
199
 
200
			int iStatus = READ_START;
201
			int iCount = 0;
202
			int iWare = -1;
203
			int iShip = -1;
204
 
205
			CBaseFile *packageFile = 0;
206
 
207
			for ( int i = 0; i < (int)readFile->size(); i++ )
208
			{
209
				CyString line(readFile->at(i));
210
 
211
				CyString cmd = line.GetToken(":", 1, 1).lower();
212
				CyString rest = line.GetToken(":", 2).RemoveFirstSpace();
213
 
214
				if ( iStatus == READ_GLOBAL )
215
				{
216
					if ( cmd == "<script>" )
217
					{
218
						packageFile = new CSpkFile();
219
						iStatus = READ_SCRIPT;
220
					}
221
					else if ( cmd == "<ship>" )
222
					{
223
						packageFile = new CXspFile();
224
						iStatus = READ_SCRIPT;
225
					}
226
					else if ( cmd == "<base>" )
227
					{
228
						packageFile = new CBaseFile();
229
						iStatus = READ_SCRIPT;
230
					}
231
					else if ( cmd == "<archive>" )
232
					{
233
						packageFile = new CArchiveFile();
234
						iStatus = READ_SCRIPT;
235
					}
236
					else if ( cmd != "</scripts>" )
237
					{
238
						int game = 0;
239
						bool disabled = false;
240
						CyString fileName = line.GetToken(":", 5);
241
						CyString origName;
242
						if ( fileName.Left(2) == "D#" )
243
						{
244
							fileName.Erase(0, 2);
245
							disabled = true;
246
						}
247
						if ( fileName.Left(2) == "G#" ) {
248
							fileName.Erase(0, 2);
249
							game = fileName.GetToken("#", 1, 1).ToInt();
250
							fileName = fileName.GetToken("#", 2);
251
						}
252
						if ( fileName.IsIn("O#") )
253
						{
254
							origName = fileName.GetToken("O#", 2, 2);
255
							fileName = fileName.GetToken("O#", 1, 1);
256
						}
257
 
258
						C_File *newFile = new C_File( m_sCurrentDir + fileName );
259
						newFile->SetGame(game);
260
						newFile->SetOriginalName(origName);
261
						newFile->SetDisabled(disabled);
262
						newFile->SetFileType(line.GetToken(":", 1, 1).ToInt());
263
						newFile->SetCreationTime((time_t)line.GetToken(":", 2, 2).ToInt());
264
						newFile->SetDir(line.GetToken(":", 3, 3));
265
						if ( line.GetToken(":", 4, 4).ToInt() )
266
							newFile->SetShared(true);
267
						newFile->UpdateSigned();
268
 
269
						m_lFiles.push_back(newFile);
270
					}
271
				}
272
				else if ( iStatus == READ_SCRIPT )
273
				{
274
					if ( cmd == "installspk" )
275
						packageFile->SetFilename(rest);
276
					else if ( cmd == "files" )
277
					{
278
						iStatus = READ_SCRIPTFILE;
279
						if ( spkinstaller )
280
						{
281
							int max;
282
							CyString *files = rest.SplitToken(' ', &max);
283
 
284
							for ( int i = 0; i < max; i++ )
285
							{
286
								int fileNum = files[i].ToInt();
287
								if ( fileNum >= 0 && fileNum < m_lFiles.size() )
288
									packageFile->AddFile(m_lFiles[fileNum]);
289
							}
290
 
291
							CLEANSPLIT(files, max);
292
						}
293
					}
294
					else if ( cmd == "disabled" )
295
						packageFile->SetEnabled(false);
296
					else if ( cmd == "modifieddisabled" )
297
						packageFile->SetModifiedEnabled(false);
298
					else if ( cmd == "icon" )
299
					{
300
						C_File *icon = new C_File(rest.GetToken(" ", 2));
301
						packageFile->SetIcon(icon, rest.GetToken(" ", 1, 1));
302
					}
303
					else
304
						packageFile->ParseValueLine(line);
305
				}
306
				else if ( iStatus == READ_SCRIPTFILE )
307
				{
308
					if ( cmd == "<script>" )
309
					{
310
						if ( packageFile->IsMod() && packageFile->IsEnabled() )
311
							m_pEnabledMod = packageFile;
312
 
313
						m_lPackages.push_back(packageFile);
314
						this->ConvertOldPackage(packageFile);
315
						packageFile = new CSpkFile();
316
						iStatus = READ_SCRIPT;
317
 
318
					}
319
					else if ( cmd == "<ship>" )
320
					{
321
						m_lPackages.push_back(packageFile);
322
						this->ConvertOldPackage(packageFile);
323
						packageFile = new CXspFile();
324
						iStatus = READ_SCRIPT;
325
					}
326
					else if ( cmd == "<base>" )
327
					{
328
						m_lPackages.push_back(packageFile);
329
						this->ConvertOldPackage(packageFile);
330
						packageFile = new CBaseFile();
331
						iStatus = READ_SCRIPT;
332
					}
333
					else if ( cmd == "<archive>" )
334
					{
335
						m_lPackages.push_back(packageFile);
336
						this->ConvertOldPackage(packageFile);
337
						packageFile = new CArchiveFile();
338
						iStatus = READ_SCRIPT;
339
					}
340
					else if ( cmd == "</scripts>" )
341
					{
342
						if ( packageFile->IsMod() && packageFile->IsEnabled() )
343
							m_pEnabledMod = packageFile;
344
 
345
						m_lPackages.push_back(packageFile);
346
						this->ConvertOldPackage(packageFile);
347
					}
348
					else
349
					{
350
						int fileNum = line.GetToken("::", 1, 1).ToInt();
351
						if ( fileNum >= 0 && fileNum < m_lFiles.size() )
352
							packageFile->AddFile(m_lFiles[fileNum]);
353
					}
354
				}
355
				else if ( iWare != -1 )
356
				{
357
					--iCount;
358
 
359
					SGameWare *gm = new SGameWare;
360
					gm->iPos = line.GetToken(" ", 1, 1).ToInt();
361
					gm->iType = line.GetToken(" ", 2, 2).ToInt();
362
					gm->cType = line.GetToken(" ", 3, 3)[0];
363
					gm->pWare = NULL;
364
					gm->sWareName = line.GetToken(" ", 4);
365
					m_lGameWares[iWare].push_back(gm);
366
					if ( iCount <= 0 )
367
						iWare = -1;
368
				}
369
				else if ( iShip != -1 )
370
				{
371
					--iCount;
372
 
373
					SGameShip *gm = new SGameShip;
374
					gm->iType = line.GetToken(" ", 1, 1).ToInt();
375
					if ( line.GetToken(" ", 2, 2).Left(4) == "$#C:" )
376
					{
377
						gm->sShipClass = line.GetToken(" ", 2, 2).Right(-4);
378
						gm->sShipID = line.GetToken(" ", 3);
379
					}
380
					else
381
					{
382
						gm->sShipID = line.GetToken(" ", 2);
383
						gm->sShipClass = "OBJ_SHIP_M5";
384
					}
385
					gm->pPackage = NULL;
386
					m_lGameShips.push_back(gm);
387
					if ( iCount <= 0 )
388
						iShip = -1;
389
				}
390
				else if ( cmd.Compare("savegamemanager") )
391
					m_iSaveGameManager = rest.ToInt();
392
				else if ( cmd.Compare("savegame") )
393
					m_iSaveGame = rest.ToInt();
394
				else if ( cmd == "fakepatch" )
395
					m_iFakePatch = rest.ToInt();
396
				else if ( cmd == "updatetime" )
397
					m_iLastUpdated = rest.ToInt();
398
				else if ( cmd == "surpressprotectedwarning" )
399
					m_bSurpressProtectedWarning = true;
400
				else if ( cmd == "pluginmanager" )
401
				{
402
					fVersion = rest.ToFloat();
403
					m_bOldPlugin = true;
404
				}
405
				else if ( cmd == "setmod" )
406
					m_sSetMod = rest;
407
				else if ( cmd == "shipbuffer" )
408
					m_iShipBuffer = rest.ToInt();
409
				else if ( cmd == "warebuffers" )
410
				{
411
					int max = rest.NumToken(" ");
412
					for ( int i = 0; i < WAREBUFFERS; i++ )
413
					{
414
						if ( i > max )
415
							m_iWareBuffer[i] = 0;
416
						else
417
							m_iWareBuffer[i] = rest.GetToken(" ", i + 1, i + 1).ToInt();
418
					}
419
				}
420
				else if ( cmd == "createdfile" )
421
					m_lCreatedFiles.PushBack(rest);
422
				else if ( cmd == "wares" )
423
				{
424
					iWare = rest.GetToken(" ", 1, 1).ToInt();
425
					iCount = rest.GetToken(" ", 2, 2).ToInt();
426
				}
427
				else if ( cmd == "ships" )
428
				{
429
					iShip = 1;
430
					iCount = rest.GetToken(" ", 1, 1).ToInt();
431
				}
432
				else if ( cmd == "modified" )
433
					m_bVanilla = false;
434
				else if ( cmd == "spkinstaller" )
435
				{
436
					fVersion = rest.ToFloat();
437
					spkinstaller = true;
438
				}
439
				else if ( cmd == "BetaVersion" )
440
					fBeta = rest.ToFloat();
441
				else if ( cmd == "Uninstall" )
442
				{
443
					C_File *uf = new C_File();
444
					uf->SetFileType(FILETYPE_SCRIPT);
445
					uf->SetCreationTime(rest.GetToken(" ", 1, 1).ToInt());
446
					uf->SetFilename(m_sCurrentDir + "/Scripts/" + rest.GetToken(" ", 2));
447
					m_lUninstallFiles.push_back(uf);
448
				}
449
				else if ( cmd == "NonRemovedFile" )
450
				{
451
					if ( !CFileIO(rest).Remove() )
452
						m_lNonRemovedFiles.PushBack(rest);
453
				}
454
				else if ( cmd == "Original" )
455
				{
456
					C_File *uf = new C_File();
457
					uf->SetFileType(rest.GetToken(" ", 1, 1).ToInt());
458
					if ( uf->GetFileType() == FILETYPE_EXTRA )
459
					{
460
						uf->SetDir(rest.GetToken(" ", 2).GetToken(":", 2));
461
						uf->SetFilename(rest.GetToken(" ", 2).GetToken(":", 1, 1));
462
					}
463
					else
464
						uf->SetFilename(rest.GetToken(" ", 2));
465
					uf->SetFilename(m_sCurrentDir + "/" + uf->GetNameDirectory(NULL));
466
					m_lOriginalFiles.push_back(uf);
467
				}
468
				else if ( cmd.Compare("GlobalSetting") )
469
					m_lGlobals.PushBack(rest.GetToken(":", 1, 1), rest.GetToken(":", 2));
470
				else if ( cmd.Compare("FakePatchOrder") )
471
					m_lFakePatchOrder.PushBack(rest.GetToken(":", 1, 1), rest.GetToken(":", 2));
472
				else if ( cmd == "globalfiles" )
473
					iStatus = READ_GLOBAL;
474
 
475
				if ( progress )
476
				{
477
					progress->UpdateProgress((long)i, (long)readFile->size());
478
				}
479
			}
480
			readFile->clear();
481
 
482
			if ( !spkinstaller )
483
				m_bRedo = true;
484
 
485
			delete readFile;
486
		}
487
	}
488
 
489
	if ( m_pEnabledMod && !m_pEnabledMod->IsEnabled() )
490
		m_pEnabledMod = NULL;
491
 
492
	m_iGame = m_gameExe.GetGameType(m_sCurrentDir) + 1;
493
	m_iGameVersion = m_gameExe.GetGameVersion(m_sCurrentDir, &m_sGameVersion) + 1;
494
	m_iGameFlags = m_gameExe.GetGameFlags(m_iGame - 1);
495
	m_iMaxPatch = m_gameExe.GetMaxPatch(m_iGame - 1);
496
 
497
	// find the fake patch
498
	if ( !ReadyFakePatch() )
499
		return false;
500
 
501
	this->RemoveCreatedFiles();
502
 
503
	// match up wares
504
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
505
	{
506
		if ( node->Data()->GetType() == TYPE_SPK )
507
		{
508
			CSpkFile *p = (CSpkFile *)node->Data();
509
			for ( CListNode<SWares> *wNode = p->GetWaresList()->Front(); wNode; wNode = wNode->next() )
510
			{
511
				SWares *w = wNode->Data();
512
				for ( CListNode<SGameWare> *gNode = m_lGameWares[CPackages::ConvertWareType(w->cType)].Front(); gNode; gNode = gNode->next() )
513
				{
10 cycrow 514
					if ( w->sID == gNode->Data()->sWareName.ToString() )
1 cycrow 515
					{
516
						gNode->Data()->pWare = w;
517
						break;
518
					}
519
				}
520
			}
521
		}
522
		else if ( node->Data()->GetType() == TYPE_XSP )
523
		{
524
			CXspFile *p = (CXspFile *)node->Data();
525
			for ( CListNode<SGameShip> *gNode = m_lGameShips.Front(); gNode; gNode = gNode->next() )
526
			{
527
				if ( p->GetShipID().Compare(gNode->Data()->sShipID) )
528
				{
529
					gNode->Data()->pPackage = p;
530
					break;
531
				}
532
			}
533
		}
534
	}
535
 
536
	// check the purged time
537
	this->PurgeGameObjects();
538
 
539
	m_bLoaded = true;
540
 
541
	return true;
542
}
543
 
544
void CPackages::RemoveCreatedFiles()
545
{
546
	for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
547
	{
548
		if ( CFileIO(node->str).Exists() )
549
			CFileIO(node->str).Remove();
550
		else if ( CFileIO(m_sCurrentDir + "/" + node->str).Exists() )
551
			CFileIO(m_sCurrentDir + "/" + node->str).Remove();
552
	}
553
 
554
	m_lCreatedFiles.Clear();
555
}
556
 
557
void CPackages::PurgeGameObjects()
558
{
559
	// check for the log file
560
	CyString logDir = GetLogDirectory();
561
	CFileIO LogFile(logDir + "/log0" + CyString::Number(PMTEXTFILE).PadNumber(4) + ".txt");
562
	if ( LogFile.Exists() )
563
	{
564
		// read the log file to memory
565
		std::vector<CyString> *lines = LogFile.ReadLines();
566
		if ( lines )
567
		{
568
			for ( int i = 0; i < (int)lines->size(); i++ )
569
			{
570
				CyString line(lines->at(i));
571
				CyString start = line.GetToken(":", 1, 1).ToLower();
572
				CyString rest = line.GetToken(":", 2, 2).RemoveFirstSpace();
573
				if ( start.Compare("purged") )
574
				{
575
					long time = rest.ToLong();
576
					if ( time == m_iLastUpdated )
577
					{
578
						this->PurgeWares();
579
						this->PurgeShips();
580
						this->RemoveUninstallScripts();
581
					}
582
				}
583
			}
584
 
585
			delete lines;
586
		}
587
		// remove the log file
588
		LogFile.Remove();
589
	}
590
}
591
 
592
void CPackages::PurgeWares()
593
{
594
	// mark all delete wares as available
595
	for ( int i = 0; i < WAREBUFFERS; i++ )
596
	{
597
		for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
598
		{
599
			SGameWare *w = node->Data();
600
			if ( !w ) continue;
601
			if ( w->iType == WARETYPE_DELETED )
602
				w->iType = WARETYPE_NONE;
603
		}
604
		m_lGameWares[i].RemoveEmpty();
605
	}
606
}
607
 
608
void CPackages::PurgeShips()
609
{
610
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
611
	{
612
		if ( !node->Data() )
613
			continue;
614
		if ( node->Data()->iType == WARETYPE_DELETED )
615
			node->Data()->iType = WARETYPE_NONE;
616
	}
617
	m_lGameShips.RemoveEmpty();
618
}
619
 
620
CyString CPackages::GetLogDirectory()
621
{
622
	CyString logDir = m_sCurrentDir;
623
	if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
624
	{
625
		SGameExe *exe = m_gameExe.GetGame(m_iGame - 1);
626
		if ( exe )
627
		{
628
			if ( !exe->sMyDoc.Empty() )
629
				logDir = m_sMyDoc + "/" + exe->sMyDoc;
630
		}
631
	}
632
 
633
	return logDir;
634
}
635
 
636
CyString CPackages::GetLogDirectory(CyString gameExe)
637
{
638
	CyString logDir = m_sCurrentDir;
639
	if ( m_iGameFlags & EXEFLAG_MYDOCLOG )
640
	{
641
		SGameExe *exe = m_gameExe.GetGameExe(CFileIO(gameExe).GetFilename());
642
		if ( exe )
643
		{
644
			if ( !exe->sMyDoc.Empty() )
645
				logDir = m_sMyDoc + "/" + exe->sMyDoc;
646
		}
647
	}
648
 
649
	return CFileIO(logDir).GetFullFilename();
650
}
651
 
652
CyString CPackages::GetSaveDirectory()
653
{
654
	CyString logDir = this->GetLogDirectory();
655
	if ( m_iGameFlags & EXEFLAG_NOSAVESUBDIR )
656
		return logDir;
657
 
658
	return logDir + "/save";
659
}
660
 
661
bool CPackages::ReadyFakePatch()
662
{
663
	// already exists, lets skip it
664
	if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Exists() && CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Exists() )
665
		return true;
666
 
667
	// if only one of them exists, lets remove them
668
	if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Exists() )
669
		CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat").Remove();
670
	if ( CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Exists() )
671
		CFileIO(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat").Remove();
672
 
673
	// now lets find the fake patch
674
	CyString useFile;
675
	if ( m_iFakePatch > 0 )
676
	{
677
		CyString file = CyString::Number(m_iFakePatch).PadNumber(2);
678
		if ( CheckValidPluginManagerFile(file) )
679
			useFile = file;
680
		else if ( CheckIfPluginManagerFile(file) )
681
			useFile = file;
682
	}
683
 
684
	if ( useFile.Empty() )
685
	{
686
		int nextfree = this->FindNextFakePatch();
687
		--nextfree; // gets the end fake patch
688
 
689
		// work backwards till we find a valid one
690
		while ( nextfree > m_iMaxPatch )
691
		{
692
			CyString file = CyString::Number(nextfree).PadNumber(2);
693
			if ( CheckValidPluginManagerFile(file) )
694
			{
695
				useFile = file;
696
				break;
697
			}
698
			--nextfree;
699
		}
700
	}
701
 
702
	CyString addonDir = this->GetAddonDir();
703
 
704
	// couldn't find the correct file, lets search for it
705
	if ( useFile.Empty() ) {
706
		int nextfree = this->FindNextFakePatch();
707
		--nextfree; // gets the end fake patch
708
 
709
		// work backwards till we find a valid one
710
		while ( nextfree > m_iMaxPatch )
711
		{
712
			CyString file = CyString::Number(nextfree).PadNumber(2);
713
			if ( CheckIfPluginManagerFile(file) )
714
			{
715
				useFile = file;
716
				break;
717
			}
718
			--nextfree;
719
		}		
720
	}
721
 
722
	if ( !useFile.Empty() )
723
	{
724
		// lets check whats in the file first
725
		CCatFile openCat;
726
		if ( openCat.Open(m_sCurrentDir + "/" + useFile + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
727
		{
728
			CLinkList<SInCatFile> *files = openCat.GetFiles();
729
			bool found = false;
730
			if ( files )
731
			{
732
				CyString useAddonDir = addonDir;
733
				if ( !useAddonDir.Empty() ) useAddonDir += "\\";
734
				for ( SInCatFile *inCat = files->First(); inCat; inCat = files->Next() )
735
				{
736
					if ( inCat->sFile.Compare("PlugMan\\TFake.pck") )
737
						continue;
738
					if ( inCat->sFile.Compare(useAddonDir + "t\\44" + CyString::Number(PMTEXTFILE).PadNumber(4) + ".pck") )
739
						continue;
740
					if ( inCat->sFile.Compare(useAddonDir + "t\\" + CyString::Number(PMTEXTFILE).PadNumber(4) + "-L044.pck") )
741
						continue;
742
 
743
					found = true;
744
					break;
745
				}
746
			}
747
 
748
			// no files, jsut delete them
749
			CFileIO catFile(m_sCurrentDir + "/" + useFile + ".cat");
750
			if ( !files || !found )
751
			{
752
				if ( catFile.Remove() )
753
				{
754
					CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");
755
					if ( datFile.Remove() )
756
						return true;
757
				}
758
			}
759
			else
760
			{
761
				if ( catFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat") )
762
				{
763
					CFileIO datFile(m_sCurrentDir + "/" + useFile + ".dat");
764
 
765
					if ( datFile.Rename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat") )
766
						return true;
767
 
768
					// TODO: it failed, restore cat file and do error
769
				}
770
			}
771
		}
772
 
773
		// if we're here, we tryed, and failed
774
		return false;
775
	}
776
 
777
	// otherwise we didn't need to (hopefully)
778
	return true;
779
}
780
 
781
 
782
bool CPackages::CheckIfPluginManagerFile(CyString filename)
783
{
784
	bool found = false;
785
 
786
	CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
787
	if ( catFile.Exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").Exists() )
788
	{
789
		CCatFile openFile;
790
		if ( openFile.Open(catFile.GetFullFilename(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
791
		{
792
			int count = 0;
793
			int noncount = 0;
794
 
795
			// check for some of the files
796
			for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() ) {
797
				if ( node->Data()->GetFullDir().IsIn("::") ) {
798
					if (CFileIO(node->Data()->GetFullDir().GetToken("::", 1, 1)).GetFilename().Compare("PlugMan_Fake.cat") ) {
799
						CyString filename = node->Data()->GetFilePointer().GetToken("::", 2, 2);
800
						filename = filename.findreplace("/", "\\");
801
						if ( openFile.FindData(filename) )
802
							++count;
803
						else
804
							++noncount;
805
					}
806
				}
807
			}
808
 
809
			if ( (count && !noncount) || (count > noncount) )
810
				found = true;
811
		}
812
	}
813
 
814
	return found;
815
}
816
 
817
bool CPackages::CheckValidPluginManagerFile(CyString filename)
818
{
819
	bool found = false;
820
 
821
	// both the cat file and dat file exists, lets open it
822
	CFileIO catFile(m_sCurrentDir + "/" + filename + ".cat");
823
	if ( catFile.Exists() && CFileIO(m_sCurrentDir + "/" + filename + ".dat").Exists() )
824
	{
825
		CCatFile openFile;
826
		if ( openFile.Open(catFile.GetFullFilename(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
827
		{
828
			if ( openFile.FindData("PlugMan\\TFake.pck") )
829
				return true;
830
			if ( openFile.FindData("pluginmanagerfake.pck") )
831
				return true;
832
		}
833
	}
834
 
835
	return found;
836
}
837
 
838
/**
839
 * Converts a package in old format
840
 *
841
 * Some entries in older packages need to be changed for the new installer
842
 * These Include:
843
 *		- Game Version, old format is an id of position in the Data/exe file, new is 2 values, one for game, one for the version
844
 */
845
void CPackages::ConvertOldPackage(CBaseFile *package)
846
{
847
	for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
848
		if ( gNode->Data()->iGame == -1 ) {
849
			// all versions
850
			if ( gNode->Data()->iVersion == 0 )
851
				gNode->Data()->iGame = 0;
852
			else
853
			{
854
				int version = 0;
855
				gNode->Data()->iGame = m_gameExe.ConvertGameType(gNode->Data()->iVersion, &version);
856
				gNode->Data()->iVersion = version;
857
			}
858
		}
859
	}
860
 
861
	if ( package->GetForumLink().Empty() && package->GetWebSite().IsIn("forum.egosoft"))
862
	{
863
		package->SetForumLink(package->GetWebSite());
864
		package->SetWebSite("");
865
	}
866
 
867
	// convert the version
868
	if ( package->GetType() == TYPE_SPK )
869
	{
870
		CSpkFile *spk = (CSpkFile *)package;
6 cycrow 871
		if ( spk->GetScriptType() == CSpkFile::SCRIPTTYPE_CUSTOM )
1 cycrow 872
		{
873
			CyString type = spk->GetScriptTypeString(44);
874
			if ( type.Compare("Ship Upgrade") )
6 cycrow 875
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPUPGRADE);
1 cycrow 876
			else if ( type.Compare("Trade Script") )
6 cycrow 877
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_TRADE);
1 cycrow 878
			else if ( type.Compare("Fleet Management") )
6 cycrow 879
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_FLEET);
1 cycrow 880
			else if ( type.Compare("Navigation Script") )
6 cycrow 881
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_NAVIGATION);
1 cycrow 882
			else if ( type.Compare("Piracy") )
6 cycrow 883
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_PIRACY);
1 cycrow 884
			else if ( type.Compare("Other") )
6 cycrow 885
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_OTHER);
1 cycrow 886
			else if ( type.Compare("Ship Command") )
6 cycrow 887
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_SHIPCOMMAND);
1 cycrow 888
			else if ( type.Compare("Station Command") )
6 cycrow 889
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_STATIONCOMMAND);
1 cycrow 890
			else if ( type.Compare("al plugin") )
6 cycrow 891
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_ALPLUGIN);
1 cycrow 892
			else if ( type.Compare("combat script") )
6 cycrow 893
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_COMBAT);
1 cycrow 894
			else if ( type.Compare("bbs and missions") )
6 cycrow 895
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_MISSION);
1 cycrow 896
			else if ( type.Compare("extension mod") )
6 cycrow 897
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_EXTENSION);
1 cycrow 898
			else if ( type.Compare("rebalance mod") )
6 cycrow 899
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_REBALANCE);
1 cycrow 900
			else if ( type.Compare("general mod") )
6 cycrow 901
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_GENERALMOD);
1 cycrow 902
			else if ( type.Compare("total conversion") )
6 cycrow 903
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_TOTAL);
1 cycrow 904
			else if ( type.Compare("cheat script") )
6 cycrow 905
				spk->SetScriptType(CSpkFile::SCRIPTTYPE_CHEAT);
1 cycrow 906
			else if ( type == "Library Script" )
907
			{
908
				spk->SetScriptType("");
909
				spk->SetLibrary();
910
			}
911
 
6 cycrow 912
			if ( spk->GetScriptType() != CSpkFile::SCRIPTTYPE_CUSTOM )
1 cycrow 913
				spk->SetScriptType("");
914
		}
915
	}
916
	else if ( package->GetType() == TYPE_XSP )
917
	{
918
		CXspFile *xsp = (CXspFile *)package;
919
		CyString data = xsp->GetShipData();
920
 
921
		for ( int i = 17; i <= 18; i++ )
922
		{
923
			CyString model = data.GetToken(";", i, i);
924
			CyString modelExt = model.Right(4);
925
			// check file extension
926
			if ( modelExt.Compare(".bod") || modelExt.Compare(".pbd") )
927
				data = data.RepToken(";", i, model.Left(-4));
928
		}
929
 
930
		xsp->SetShipData(data);
931
	}
932
 
933
	// any extra files that are in director folder
934
	if ( package->AnyFileType(FILETYPE_EXTRA) )
935
	{
936
		for ( C_File *f = package->GetFirstFile(FILETYPE_EXTRA); f; f = package->GetNextFile(f) )
937
		{
938
			if ( !f->GetDir().Compare("director") )
939
				continue;
940
			if ( f->CheckFileExt("xml") || f->CheckFileExt("pck") )
941
			{
942
				f->SetDir("");
943
				f->SetFileType(FILETYPE_MISSION);
944
			}
945
		}
946
	}
947
}
948
 
949
void CPackages::UpdatePackage(CBaseFile *p)
950
{
951
	if ( p->GetType() != TYPE_SPK )
952
		return;
953
 
954
	CSpkFile *package = (CSpkFile *)p;
955
 
956
	// update the signed status
957
	package->UpdateSigned(true);
958
 
959
	// check for another mod
960
	if ( !package->IsAnotherMod() )
961
		return;
962
 
963
	package->SetParent((CSpkFile *)FindSpkPackage(package->GetOtherName(), package->GetOtherAuthor()));
964
}
965
 
966
/**
967
 * Updates the package list once data has been read
968
 *
969
 * Finds any original Files
970
 * Updates Signed status of all packages
971
 */
972
bool CPackages::UpdatePackages(int doStatus, bool individual)
973
{
974
	// if theres no original files, then set the current structure as the base
975
	if ( doStatus == 0 || doStatus == -1 )
976
	{
977
		if ( m_lOriginalFiles.empty() || m_bRedo )
978
		{
979
			StoreOriginalFiles(FILETYPE_SCRIPT, "scripts");
980
			StoreOriginalFiles(FILETYPE_TEXT, "t");
981
			StoreOriginalFiles(FILETYPE_SOUND, "soundtrack");
982
			StoreOriginalFiles(FILETYPE_EXTRA, "mov");
983
		}
984
	}
985
 
986
	// update each package
987
	// parent/child, signed status
988
	if ( doStatus == -1 || doStatus == 1 )
989
	{
990
		if ( individual )
991
		{
992
			// no package, most likly none installed
993
			if ( !m_pPackageNode )
994
				return false;
995
 
996
			this->UpdatePackage(m_pPackageNode->Data());
997
 
998
			// move to the next package
999
			m_pPackageNode = m_pPackageNode->next();
1000
			if ( !m_pPackageNode )
1001
				return false;
1002
		}
1003
		else
1004
		{
1005
			for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
1006
				this->UpdatePackage(node->Data());
1007
		}
1008
	}
1009
 
1010
	if ( doStatus == -1 || doStatus == 2 )
1011
		this->LoadVirtualFileSystem();
1012
 
1013
	// adjust save games
1014
	if ( doStatus == -1 || doStatus == 3 )
1015
	{
1016
		/*
1017
		// load our save games to the active mydocs
1018
		if ( m_iSaveGame != -1 )
1019
		{
1020
			// check we have a valid save game directory
1021
			CDirIO Dir(this->GetSaveDirectory());
1022
			if ( Dir.Exists() )
1023
			{
1024
				// now check our directory already exists
1025
				// if we are vanilla mode, then make sure we use the vanilla saves if exists
1026
				CyString d = CyString("game_") + (long)m_iSaveGame + "/";
1027
				if ( m_bVanilla )
1028
					d += "Vanilla";
1029
				else
1030
					d += "Modified";
1031
 
1032
				// copy the files back over
1033
				if ( Dir.Exists(d) )
1034
				{
1035
					// if vanilla, remove files first
1036
					if ( m_bVanilla )
1037
					{
1038
						CyStringList *lDirs = Dir.DirList();
1039
						if ( lDirs )
1040
						{
1041
							for ( SStringList *node = lDirs->Head(); node; node = node->next )
1042
							{
1043
								if ( !Dir.IsFile(node->str) || !CFileIO(node->str).CheckFileExtension("sav") )
1044
									continue;
1045
								CFileIO(Dir.File(node->str)).Remove();
1046
							}
1047
 
1048
							delete lDirs;
1049
						}
1050
					}
1051
 
1052
					// copy files to normal save game directory
1053
					CDirIO FromDir(Dir.Dir(d));
1054
					CyStringList *lDirs = FromDir.DirList();
1055
					if ( lDirs )
1056
					{
1057
						for ( SStringList *node = lDirs->Head(); node; node = node->next )
1058
						{
1059
							if ( !FromDir.IsFile(node->str) || !CFileIO(node->str).CheckFileExtension("sav") )
1060
								continue;
1061
							CFileIO F(FromDir.File(node->str));
1062
							F.Copy(Dir.File(F.GetFilename()));
1063
						}
1064
 
1065
						delete lDirs;
1066
					}
1067
				}
1068
			}
1069
		}*/
1070
	}
1071
 
1072
	m_bRedo = false;
1073
	return true;
1074
}
1075
 
1076
int CPackages::CheckOpenPackage(CyString file, int *error)
1077
{
1078
	// first check if it exists
1079
	if ( !CFileIO(file).Exists() )
1080
	{
1081
		*error = INSTALLERR_NOEXIST;
1082
		return -1;
1083
	}
1084
 
1085
	// open the spk file
1086
	float fVersion = 0.0f;
1087
	int check = CBaseFile::CheckFile(file, &fVersion);
1088
 
1089
	// wrong version
1090
	if ( fVersion > (float)FILEVERSION )
1091
	{
1092
		*error = INSTALLERR_VERSION;
1093
		return -1;
1094
	}
1095
 
1096
	return check;
1097
}
1098
 
1099
CMultiSpkFile *CPackages::OpenMultiPackage(CyString file, int *error, CProgressInfo *progress)
1100
{
1101
	int check = CheckOpenPackage(file, error);
1102
	if ( *error == -1 )
1103
		return false;
1104
 
1105
	if ( check != SPKFILE_MULTI )
1106
	{
1107
		*error = INSTALLERR_NOMULTI;
1108
		return false;
1109
	}
1110
 
1111
	*error = INSTALLERR_NONE;
1112
 
1113
	CMultiSpkFile *package = new CMultiSpkFile;
1114
 
1115
	bool ret = false;
1116
	if ( package->ReadFile(file, true) )
1117
	{
1118
		if ( package->ReadAllPackages(SPKREAD_NODATA) )
1119
		{
1120
			for ( CListNode<SMultiSpkFile> *node = package->GetFileList()->Front(); node; node = node->next() )
1121
				this->ConvertOldPackage(node->Data()->pFile);
1122
 
1123
			ret = true;
1124
		}
1125
	}
1126
 
1127
	if ( ret )
1128
		return package;
1129
 
1130
	delete package;
1131
	return NULL;
1132
}
1133
 
1134
bool CPackages::OpenMultiPackage ( CyString file, CLinkList<CBaseFile> *packageList, int *error, CProgressInfo *progress )
1135
{
1136
	int check = CheckOpenPackage(file, error);
1137
	if ( *error == -1 )
1138
		return false;
1139
 
1140
	if ( check != SPKFILE_MULTI )
1141
	{
1142
		*error = INSTALLERR_NOMULTI;
1143
		return false;
1144
	}
1145
 
1146
	*error = INSTALLERR_NONE;
1147
 
1148
	CMultiSpkFile *package = new CMultiSpkFile;
1149
 
1150
	bool ret = false;
1151
	if ( package->ReadFile(file, true) )
1152
	{
1153
		if ( package->ReadAllPackages(SPKREAD_ALL, packageList) )
1154
		{
1155
			for ( CListNode<CBaseFile> *node = packageList->Front(); node; node = node->next() )
1156
				this->ConvertOldPackage(node->Data());
1157
 
1158
 
1159
			ret = true;
1160
		}
1161
	}
1162
 
1163
	delete package;
1164
 
1165
	return ret;
1166
}
1167
 
1168
int CPackages::PrepareMultiPackage ( CyString file, CLinkList<CBaseFile> *errorPackageList, int *error, CProgressInfo *progress )
1169
{
1170
	int check = CheckOpenPackage(file, error);
1171
	if ( *error == -1 )
1172
		return 0;
1173
 
1174
	if ( check != SPKFILE_MULTI )
1175
	{
1176
		*error = INSTALLERR_NOMULTI;
1177
		return 0;
1178
	}
1179
 
1180
	*error = INSTALLERR_NONE;
1181
 
1182
	CMultiSpkFile *package = new CMultiSpkFile;
1183
 
1184
	int count = 0;
1185
	if ( package->ReadFile(file, true) )
1186
	{
1187
		CLinkList<CBaseFile> packageList;
1188
		if ( package->ReadAllPackages(SPKREAD_ALL, &packageList) )
1189
		{
1190
			for ( CListNode<CBaseFile> *node = packageList.Front(); node; node = node->next() )
1191
			{
1192
				CBaseFile *p = node->Data();
1193
				this->ConvertOldPackage(p);
1194
				int error = this->PrepareInstallPackage(p);
1195
				if ( error )
1196
				{
1197
					node->Data()->SetLoadError(error);
1198
					errorPackageList->push_back(p);
1199
				}
1200
				else
1201
					++count;
1202
			}
1203
		}
1204
		packageList.clear();
1205
	}
1206
 
1207
	for ( SMultiSpkFile *ms = package->GetFileList()->First(); ms; ms = package->GetFileList()->Next() )
1208
		ms->pFile = NULL;
1209
	delete package;
1210
 
1211
	return count;
1212
}
1213
 
1214
CBaseFile *CPackages::OpenPackage(CyString file, int *error, CProgressInfo *progress, int readtype, int flags)
1215
{
1216
	int check = CheckOpenPackage(file, error);
1217
	if ( *error == -1 )
1218
		return NULL;
1219
 
1220
	CBaseFile *installFile = 0;
1221
 
1222
	if ( progress )
1223
		progress->DoHalf();
1224
 
1225
	switch (check)
1226
	{
1227
		case SPKFILE_OLD:
1228
			*error = INSTALLERR_OLD;
1229
			return NULL;
1230
		case SPKFILE_INVALID:
1231
			// convert xsp
1232
			if ( CFileIO(file).CheckFileExtension("xsp") )
1233
			{
1234
				installFile = new CXspFile();
1235
				if ( !((CXspFile *)installFile)->ConvertOld(file) )
1236
				{
1237
					delete installFile;
1238
					return NULL;
1239
				}
1240
				break;
1241
			}
1242
			*error = INSTALLERR_INVALID;
1243
			return NULL;
1244
		case SPKFILE_BASE:
1245
			installFile = new CBaseFile();
1246
			if ( !installFile->ReadFile(file, readtype, progress) )
1247
			{
1248
				delete installFile;
1249
				return NULL;
1250
			}
1251
			break;
1252
		case SPKFILE_SINGLE:
1253
			installFile = new CSpkFile();
1254
			if ( !((CSpkFile *)installFile)->ReadFile(file, readtype, progress) )
1255
			{
1256
				delete installFile;
1257
				return NULL;
1258
			}
1259
			break;
1260
		case SPKFILE_MULTI:
1261
			*error = INSTALLERR_NOMULTI;
1262
			return NULL;
1263
		case SPKFILE_SINGLESHIP:
1264
			installFile = new CXspFile();
1265
			if ( !((CXspFile *)installFile)->ReadFile(file, readtype, progress) )
1266
			{
1267
				delete installFile;
1268
				return NULL;
1269
			}
1270
			break;
1271
 
1272
		default:
1273
			*error = INSTALLERR_UNKNOWN;
1274
			return NULL;
1275
	}
1276
 
1277
	if ( progress )
1278
		progress->SecondHalf();
1279
 
1280
	// now uncomress all files
1281
	if ( !(flags & READFLAG_NOUNCOMPRESS) )
1282
		installFile->UncompressAllFiles(progress);
1283
 
1284
	this->ConvertOldPackage (installFile);
1285
 
1286
	return installFile;
1287
}
1288
 
1289
void CPackages::PurgeUninstallScripts(CBaseFile *package, CyStringList *errors)
1290
{
1291
	for ( CListNode<C_File> *fNode = m_lUninstallFiles.Front(); fNode; fNode = fNode->next() )
1292
	{
1293
		C_File *uf = fNode->Data();
1294
		// check against any script files
1295
		for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
1296
		{
1297
			C_File *checkFile = checkNode->Data();
1298
			if ( !checkFile ) continue;
1299
 
1300
			if ( checkFile->GetFileType() != FILETYPE_UNINSTALL && checkFile->GetFileType() != FILETYPE_SCRIPT )
1301
				continue;
1302
 
1303
			if ( uf->GetFilename().Compare(checkFile->GetFilename()) )
1304
			{
1305
				if ( RemoveUninstallFile(uf, errors) )
1306
					fNode->DeleteData();
1307
				break;
1308
			}
1309
		}
1310
	}
1311
 
1312
	m_lUninstallFiles.RemoveEmpty();
1313
 
1314
	this->WriteData();
1315
}
1316
 
1317
int CPackages::InstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *errored, CLinkList<CBaseFile> *installedList)
1318
{
1319
	if ( m_lInstallList.empty() ) return false;
1320
 
1321
	int installed = 0;
1322
	for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
1323
	{
1324
		CBaseFile *p = node->Data();
1325
		if ( this->InstallPackage(p, errors, progress, !p->IsEnabled()) )
1326
		{
1327
			++installed;
1328
			if ( installedList )
1329
				installedList->push_back(p);
1330
		}
1331
		else if ( errored )
1332
		{
1333
			m_lPackages.remove(p);
1334
			errored->push_back(p);
1335
		}
1336
	}
1337
 
1338
	m_lInstallList.clear();
1339
 
1340
	this->WriteData();
1341
 
1342
	return installed;
1343
}
1344
 
1345
bool CPackages::InstallPackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress, bool disabled )
1346
{
1347
	// first check if we are installed a mod
1348
	bool prevDisabled = disabled;
1349
	if ( package->IsMod() && m_pEnabledMod && !m_bForceModInstall )
1350
		disabled = true;
1351
	// if vanilla nad package aint signed
1352
	if ( m_bVanilla && !package->IsSigned() )
1353
		disabled = true;
1354
 
1355
	// check any depancies
1356
	if ( !disabled && !this->CheckEnabledDependacy(package) )
1357
		disabled = true;
1358
 
1359
	// search for an old version
1360
	CBaseFile *oldPackage = FindPackage(package);
1361
	if ( oldPackage && oldPackage == m_pEnabledMod && disabled )
1362
		disabled = prevDisabled;
1363
 
1364
	// update packages must have an old package installed already (should have been checked for already)
1365
	if ( package->GetType() == TYPE_SPK )
1366
	{
1367
		if ( ((CSpkFile *)package)->IsPackageUpdate() )
1368
		{
1369
			if ( !oldPackage )
1370
				return false;
1371
			// change any mods to temp ones
1372
			for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
1373
			{
1374
				if ( f->Data()->GetFileType() != FILETYPE_MOD )
1375
					continue;
1376
 
1377
				f->Data()->SetDir("temp");
1378
				if ( f->Data()->IsFakePatch() )
1379
					f->Data()->SetName(CyString("Fake_") + f->Data()->GetName());
1380
			}
1381
		}
1382
	}
1383
 
1384
	// no need to backup if we're disabling them
1385
	if ( !disabled )
1386
	{
1387
		// find any uninstall files and remove them
1388
		this->PurgeUninstallScripts(package, errors);
1389
 
1390
		// backup any original files before installing
1391
		CDirIO oDir(m_sCurrentDir + "/PluginManager/Original");
1392
		for ( CListNode<C_File> *oNode = m_lOriginalFiles.Front(); oNode; oNode = oNode->next() )
1393
		{
1394
			C_File *of = oNode->Data();
1395
			for ( CListNode<C_File> *checkNode = package->GetFileList()->Front(); checkNode; checkNode = checkNode->next() )
1396
			{
1397
				C_File *f = checkNode->Data();
1398
 
1399
				// match the same filetype
1400
				if ( of->GetFileType() != f->GetFileType() )
1401
					continue;
1402
 
1403
				// same file
1404
				if ( of->GetFilename().Compare(f->GetFilename()) )
1405
				{
1406
					// check if original file already exists (assume already backed up)
1407
					if ( !BackupOriginalFile(of, errors) )
1408
						continue;
1409
					break;
1410
				}
1411
			}
1412
		}
1413
	}
1414
 
1415
	// install all the files
1416
	if ( oldPackage )
1417
	{
1418
		CLinkList<CBaseFile> excludeList;
1419
		excludeList.push_back(oldPackage);
1420
		this->UpdateUsedFiles(&excludeList, true);
1421
	}
1422
	else
1423
		this->UpdateUsedFiles(0, true);
1424
 
1425
	package->ReadAllFilesToMemory();
1426
	if ( !package->InstallFiles (m_sCurrentDir, progress, &m_lFiles, errors, !disabled, this) )
1427
	{
1428
		// TODO: clear up installed files
1429
		return false;
1430
	}
1431
 
1432
	// if we're installing an addon, lets use the fake patch method for object files
1433
	if ( m_iGameFlags & EXEFLAG_ADDON ) {
1434
		CCatFile cat;
1435
		if ( CCatFile::Opened(cat.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT)) ) {
1436
			for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() ) {
1437
				if ( f->Data()->GetFileType() != FILETYPE_SHIPSCENE && f->Data()->GetFileType() != FILETYPE_COCKPITSCENE && f->Data()->GetFileType() != FILETYPE_SHIPMODEL && f->Data()->GetFileType() != FILETYPE_SHIPOTHER )
1438
					continue;
1439
				if ( CCatFile::IsAddonDir(f->Data()->GetNameDirectory(package)) )
1440
					continue;
1441
				if ( cat.AppendFile(f->Data()->GetFilePointer(), f->Data()->GetNameDirectory(package), true, (m_iGameFlags & EXEFLAG_NOXOR) ? false : true) ) {
1442
					CFileIO(f->Data()->GetFilePointer()).Remove();
1443
					f->Data()->SetFilename(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat::" + f->Data()->GetNameDirectory(package));
1444
				}
1445
			}
1446
		}
1447
	}
1448
 
1449
	bool shuffle = false;
1450
 
1451
	// merge the update into the old package
1452
	bool dontAdd = false;
1453
	if ( package->GetType() == TYPE_SPK )
1454
	{
1455
		if ( ((CSpkFile *)package)->IsPackageUpdate() )
1456
		{
1457
			// now copy any files from a mod
1458
			for ( CListNode<C_File> *f = package->GetFileList()->Front(); f; f = f->next() )
1459
			{
1460
				if ( !f->Data() )
1461
					continue;
1462
				if ( f->Data()->GetFileType() != FILETYPE_MOD )
1463
					continue;
1464
				// we only need the cat file
1465
				if ( !f->Data()->CheckFileExt("cat") )
1466
					continue;
1467
 
1468
				// if fake patch, find first fake patch in package
1469
				C_File *findMatching = NULL;
1470
				if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
1471
				{
1472
					for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
1473
					{
1474
						if ( !node->Data() )
1475
							continue;
1476
						if ( node->Data()->GetFileType() != FILETYPE_MOD )
1477
							continue;
1478
						// we only need the cat file
1479
						if ( !node->Data()->CheckFileExt("cat") )
1480
							continue;
1481
						if ( !node->Data()->IsFakePatch() )
1482
							continue;
1483
 
1484
						findMatching = node->Data();
1485
						break;
1486
					}
1487
				}
1488
				// otherwise, just add to the mod of the same name
1489
				else
1490
				{
1491
					for ( CListNode<C_File> *node = oldPackage->GetFileList()->Front(); node; node = node->next() )
1492
					{
1493
						if ( !node->Data() )
1494
							continue;
1495
						if ( node->Data()->GetFileType() != FILETYPE_MOD )
1496
							continue;
1497
						// we only need the cat file
1498
						if ( !node->Data()->CheckFileExt("cat") )
1499
							continue;
1500
 
1501
						if ( node->Data()->GetName().Compare(f->Data()->GetName()) )
1502
						{
1503
							findMatching = node->Data();
1504
							break;
1505
						}
1506
					}
1507
				}
1508
 
1509
				if ( findMatching )
1510
				{
1511
					// copy accross all mods
1512
					CCatFile catTo, catFrom;
1513
					if ( catFrom.Open(f->Data()->GetFilePointer(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
1514
					{
1515
						if ( catTo.Open(findMatching->GetFilePointer(), this->GetAddonDir(), CATREAD_CATDECRYPT, false) == CATERR_NONE )
1516
						{
1517
							for ( int i = 0; i < catFrom.GetNumFiles(); i++ )
1518
							{
1519
								SInCatFile *c = catFrom.GetFile(i);								 
1520
								catTo.AppendFile(f->Data()->GetFilePointer() + "::" + c->sFile, c->sFile);
1521
							}
1522
						}
1523
					}
1524
 
1525
					// now remove the files
1526
					C_File *m = package->FindMatchingMod(f->Data());
1527
					RemoveFile(f->Data(), errors);
1528
					m_lFiles.remove(f->Data());
1529
					f->ChangeData(NULL);
1530
					if ( m )
1531
					{
1532
						int pos = package->GetFileList()->FindPos(m);
1533
						RemoveFile(m, errors);
1534
						m_lFiles.remove(m);
1535
						if ( pos != -1 )
1536
							package->GetFileList()->GetNode(pos)->ChangeData(NULL);
1537
					}
1538
				}
1539
				// no matching file, then we shall just renaming back
1540
				else
1541
				{
1542
					if ( f->Data()->GetBaseName().Left(5).Compare("fake_") )
1543
					{
1544
						shuffle = true;
1545
						C_File *match = package->FindMatchingMod(f->Data());
1546
						CyString next = CyString::Number(this->FindNextFakePatch()).PadNumber(2);
1547
 
1548
						CyString oldFilePointer = f->Data()->GetFilePointer();
1549
						f->Data()->SetDir("");
1550
						f->Data()->ChangeBaseName(next);
1551
						if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
1552
							f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));
1553
 
1554
						if ( match )
1555
						{
1556
							CyString oldFilePointer = match->GetFilePointer();
1557
							match->SetDir("");
1558
							match->ChangeBaseName(next);
1559
							if ( CFileIO(oldFilePointer).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
1560
								match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
1561
						}
1562
					}
1563
					else
1564
					{
1565
						C_File *match = package->FindMatchingMod(f->Data());
1566
 
1567
						f->Data()->SetDir("");
1568
						if ( CFileIO(f->Data()->GetFilePointer()).Rename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package)) )
1569
							f->Data()->SetFilename(m_sCurrentDir + "/" + f->Data()->GetNameDirectory(package));
1570
						if ( match )
1571
						{
1572
							match->SetDir("");
1573
							if ( CFileIO(match->GetFilePointer()).Rename(m_sCurrentDir + "/" + match->GetNameDirectory(package)) )
1574
								match->SetFilename(m_sCurrentDir + "/" + match->GetNameDirectory(package));
1575
						}
1576
					}
1577
				}
1578
			}
1579
 
1580
			package->GetFileList()->RemoveEmpty();
1581
			((CSpkFile *)oldPackage)->MergePackage(package);
1582
			delete package;
1583
			package = oldPackage;
1584
			oldPackage = false;
1585
			dontAdd = true;
1586
 
1587
			CDirIO(m_sCurrentDir).RemoveDir("temp");
1588
			CDirIO(m_sCurrentDir).RemoveDir("Mods/temp");
1589
		}
1590
	}
1591
 
1592
	// if theres an icon, write it
1593
	if ( package->GetIcon() )
1594
	{
1595
		C_File *icon = package->GetIcon();
1596
		if ( !icon->GetData() || !icon->GetDataSize() )
1597
			package->SetIcon(NULL, "");
1598
		else
1599
		{
1600
			CDirIO Dir(m_sCurrentDir);
1601
			bool ready = true;
1602
			if ( !Dir.Exists("PluginManager") )
1603
			{
1604
				if ( !Dir.Create("PluginManager") )
1605
					ready = false;
1606
			}
1607
			if ( ready && !Dir.Exists("PluginManager/Icons") )
1608
			{
1609
				if ( !Dir.Create("PluginManager/Icons") )
1610
					ready = false;
1611
			}
1612
 
1613
			if ( ready )
1614
			{
1615
				if ( !icon->UncompressData() )
1616
					package->SetIcon(NULL, "");
1617
				else
1618
				{
1619
					CFileIO iconFile(m_sCurrentDir + "/PluginManager/Icons/" + package->GetAuthor() + "_" + package->GetName() + "." + package->GetIconExt());
1620
					if ( iconFile.WriteData((const char *)icon->GetData(), icon->GetDataSize()) )
1621
					{
1622
						icon->SetFilename(package->GetAuthor() + "_" + package->GetName() + "." + package->GetIconExt());
1623
						icon->SetFullDir(m_sCurrentDir + "/PluginManager/Icons");
1624
					}
1625
					else
1626
						package->SetIcon(NULL, "");
1627
				}
1628
			}
1629
 
1630
			if ( package->GetIcon() )
1631
				package->GetIcon()->DeleteData();
1632
		}
1633
	}
1634
 
1635
	// remove all data
1636
	package->ClearFileData();
1637
 
1638
	// add to list
1639
	if ( !dontAdd )
1640
	{
1641
		if ( oldPackage )
1642
		{
1643
			m_lPackages.insert(oldPackage, package);
1644
			m_lPackages.remove(oldPackage, false);
1645
		}
1646
		else
1647
			m_lPackages.push_back (package);
1648
	}
1649
 
1650
	UpdateUsedFiles();
1651
 
1652
	if ( disabled )
1653
		package->SetEnabled(false);
1654
 
1655
	// remove any files no longer used by old package
1656
	if ( oldPackage )
1657
	{
1658
		CListNode<C_File> *fnode = oldPackage->GetFileList()->Front();
1659
		while ( fnode )
1660
		{
1661
			C_File *f = fnode->Data();
1662
 
1663
			// no longer used
1664
			if ( !f->GetUsed() )
1665
			{
1666
				// remove from file list
1667
				m_lFiles.remove(f, false);
1668
 
1669
				// remove from hard drive
1670
				if ( RemoveFile(f, errors) )
1671
				{
1672
					// if a fake patch, we need to shufle
1673
					if ( f->IsFakePatch() )
1674
						shuffle = true;
1675
					else if ( f->IsAutoTextFile() )
1676
						shuffle = true;
1677
				}
1678
			}
1679
 
1680
			fnode = fnode->next();
1681
		}
1682
 
1683
	}
1684
 
1685
	if ( shuffle )
1686
	{
1687
		ShuffleFakePatches(errors);
1688
		ShuffleTextFiles(errors);
1689
	}
1690
 
1691
	// now we need to link any child/parent packages
1692
	if ( package->GetType() == TYPE_SPK )
1693
	{
1694
		CSpkFile *spk = (CSpkFile *)package;
1695
		if ( spk->IsAnotherMod() )
1696
			spk->SetParent((CSpkFile *)FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()));
1697
	}
1698
 
1699
	// store enabled mod
1700
	if ( package->IsMod() && !disabled )
1701
		m_pEnabledMod = package;
1702
 
1703
	m_bRemoveDir = true;
1704
 
1705
	this->WriteData();
1706
 
1707
	return true;
1708
}
1709
 
1710
bool CPackages::UninstallPreparedPackages(CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *uninstalledPackages, CLinkList<CBaseFile> *disabledPackages)
1711
{
1712
	if ( m_lInstallList.empty() ) return false;
1713
 
1714
	// update the used status, excluding all packages we are about to remove
1715
	UpdateUsedFiles(&m_lInstallList);
1716
 
1717
	CyStringList removeDirs;
1718
	CLinkList<C_File> uninstallFiles;
1719
	CLinkList<C_File> fileList;
1720
	bool readme = false, original = false, shuffle = false;
1721
 
1722
	// find all files that need to be removed
1723
	int maxFiles = 0;
1724
	for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
1725
	{
1726
		CBaseFile *p = node->Data();
1727
		maxFiles += this->GetAllPackageFiles(p, &fileList, true);
1728
 
1729
		// disable any dependants
1730
		if ( p->GetType() == TYPE_SPK && ((CSpkFile *)p)->IsLibrary() )
1731
		{
1732
			CLinkList<CBaseFile> depList;
1733
			if ( this->GetDependacyList(p, &depList) )
1734
			{
1735
				for ( CBaseFile *depP = depList.First(); depP; depP = depList.Next() )
1736
				{
1737
					if ( depP->IsEnabled() )
1738
						this->PrepareDisablePackage(depP);
1739
				}
1740
 
1741
				if ( m_lDisableList.size() )
1742
					this->DisablePreparedPackages(errors, progress, disabledPackages);
1743
			}
1744
		}
1745
	}
1746
 
1747
	// interate through all the files in the package
1748
	int fileCount = 0;
1749
	for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
1750
	{
1751
		C_File *f = node->Data();
1752
 
1753
		// display progress if needed
1754
		if ( progress )
1755
		{
1756
			progress->UpdateProgress(fileCount++, maxFiles);
1757
			progress->UpdateFile(f);
1758
		}
1759
 
1760
		// skip uninstall files
1761
		if ( f->GetFileType() == FILETYPE_UNINSTALL )
1762
		{
1763
			uninstallFiles.push_back(f);
1764
			continue;
1765
		}
1766
 
1767
		// only delete files that are not used
1768
		// if its a shared file, we skip it
1769
		if ( f->GetUsed() || f->IsShared() )
1770
			continue;
1771
 
1772
		if ( f->GetFileType() == FILETYPE_README )
1773
			readme = true;
1774
		else if ( f->GetFileType() == FILETYPE_UNINSTALL || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND || f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_SHIPSCENE || f->GetFileType() == FILETYPE_COCKPITSCENE || f->GetFileType() == FILETYPE_SHIPOTHER || f->GetFileType() == FILETYPE_SHIPMODEL || f->GetFileType() == FILETYPE_ADVERT )
1775
		{
1776
			CyString dir = f->GetDirectory(NULL);
1777
			removeDirs.PushBack(dir, true);
1778
			dir = dir.FindReplace("\\", "/");
1779
			if ( dir.IsIn("/") )
1780
			{
1781
				for ( int i = dir.NumToken("/"); i; i-- )
1782
				{
1783
					CyString remDir = dir.GetToken("/", 1, i);
1784
					removeDirs.PushBack(remDir, true);
1785
				}
1786
			}
1787
		}
1788
 
1789
		if ( f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(6).lower() == "extras" )
1790
			removeDirs.PushBack("Extras", true);
1791
 
1792
		if ( RemoveFile(f, errors) )
1793
		{
1794
			// check if its an original file and restore
1795
			if ( IsOriginalFile(f) )
1796
			{
1797
				CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(NULL));
1798
				if ( of.Exists() )
1799
				{
1800
					original = true;
1801
					if ( of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(NULL)) )
1802
						this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE, f->GetNameDirectory(NULL), errors);
1803
					else
1804
						this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE_FAIL, f->GetNameDirectory(NULL), errors);
1805
				}
1806
			}
1807
		}
1808
		else // problem removeing (try when the program closes)
1809
			m_lNonRemovedFiles.PushBack(f->GetFilePointer());
1810
 
1811
		// check for fake patchs
1812
		if ( f->IsFakePatch() )
1813
			shuffle = true;
1814
		else if ( f->IsAutoTextFile() )
1815
			shuffle = true;
1816
 
1817
		// remove the file from the main list as swell
1818
		m_lFiles.remove(f, false);
1819
		delete f;
1820
	}
1821
 
1822
	// remove all the packages from memory
1823
	for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
1824
	{
1825
		CBaseFile *p = node->Data();
1826
		p->GetFileList()->clear();
1827
		m_lPackages.remove(p);
1828
 
1829
		if ( p == m_pEnabledMod )
1830
			m_pEnabledMod = NULL;
1831
 
1832
		if ( uninstalledPackages )
1833
			uninstalledPackages->push_back(p);
1834
		else
1835
			delete p;
1836
	}
1837
 
1838
	m_lInstallList.clear();
1839
 
1840
	// check unistall files
1841
	if ( !uninstallFiles.empty() )
1842
	{
1843
		removeDirs.PushBack(CyString("PluginManager/Uninstall"));
1844
 
1845
		// make sure the scripts directory is created, even thou it should always be there anyways
1846
		CDirIO scriptDir(m_sCurrentDir);
1847
		if ( !scriptDir.Exists("scripts") )
1848
		{
1849
			if ( scriptDir.Create("Scripts") )
1850
				this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, "Scripts", errors);
1851
			else
1852
				this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, "Scripts", errors);
1853
		}
1854
 
1855
		for ( C_File *uf = uninstallFiles.First(); uf; uf = uninstallFiles.Next() )
1856
		{
1857
			C_File *newFile = new C_File();
1858
			newFile->SetFileType(FILETYPE_SCRIPT);
1859
			newFile->SetFilename(uf->GetFilename());
1860
			newFile->SetCreationTime(uf->GetCreationTime());
1861
 
1862
			// other installed packages use this file as well, copy it
1863
			CyString newFilename = m_sCurrentDir + "/" + newFile->GetNameDirectory(NULL);
1864
			CFileIO file(uf->GetFilePointer());
1865
 
1866
			if ( uf->GetUsed() )
1867
			{
1868
				if ( file.Copy(newFilename) )
1869
					this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY, newFile->GetNameDirectory(NULL), errors);
1870
				else
1871
				{
1872
					this->AddLogEntry(SPKINSTALL_UNINSTALL_COPY_FAIL, newFile->GetNameDirectory(NULL), errors);
1873
					delete newFile;
1874
					newFile = NULL;
1875
				}
1876
			}
1877
			// otherwise just move it
1878
			else
1879
			{
1880
				if ( file.Rename(newFilename) )
1881
					this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE, newFile->GetNameDirectory(NULL), errors);
1882
				else
1883
				{
1884
					this->AddLogEntry(SPKINSTALL_UNINSTALL_MOVE_FAIL, newFile->GetNameDirectory(NULL), errors);
1885
					delete newFile;
1886
					newFile = NULL;
1887
				}
1888
 
1889
				m_lFiles.remove(uf, false);
1890
				delete uf;
1891
			}
1892
 
1893
			// add to the list
1894
			if ( newFile )
1895
			{
1896
				// first check if theres a matching one
1897
				bool found = false;
1898
				for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
1899
				{
1900
					C_File *checkFile = node->Data();
1901
					if ( checkFile->GetFilename().Compare(newFile->GetFilename()) )
1902
					{
1903
						found = true;
1904
						break;
1905
					}
1906
				}
1907
 
1908
				// not found, so add it
1909
				if ( !found )
1910
					m_lUninstallFiles.push_back(newFile);
1911
				else
1912
					delete newFile;
1913
			}
1914
		}
1915
	}
1916
	uninstallFiles.clear();
1917
 
1918
	// remove all directies that we're not using
1919
	if ( readme )
1920
	{
1921
		removeDirs.PushBack(CyString("PluginManager/Readme"));
1922
		removeDirs.PushBack(CyString("Readme"));
1923
	}
1924
	if ( original )
1925
		removeDirs.PushBack(CyString("PluginManager/Original"));
1926
	removeDirs.PushBack(CyString("PluginManager/Disabled"));
1927
	RemoveUnusedDirectories(removeDirs, errors);
1928
 
1929
	// finally lets shuffle any fake patchs to fill in gaps
1930
	if ( shuffle )
1931
	{
1932
		ShuffleFakePatches(errors);
1933
		ShuffleTextFiles(errors);
1934
	}
1935
 
1936
	this->WriteData();
1937
 
1938
	return true;
1939
}
1940
 
1941
/**
1942
 * Prepares a package to be uninstalled
1943
 *
1944
 * Adds the package, and all its children onto the uninstall list to be used by UninstallPreparePackages
1945
 */
1946
void CPackages::PrepareUninstallPackage(CBaseFile *package)
1947
{
1948
	// add package to list
1949
	if ( !m_lInstallList.FindData(package) )
1950
		m_lInstallList.push_back(package);
1951
 
1952
	// add all children
1953
	CLinkList<CBaseFile> children;
1954
	if ( this->GetChildPackages(package, &children, true) )
1955
	{
1956
		for ( CBaseFile *p = children.First(); p; p = children.Next() )
1957
		{
1958
			if ( !m_lInstallList.FindData(p) )
1959
				m_lInstallList.push_back(p);
1960
		}
1961
	}
1962
}
1963
 
1964
bool CPackages::PrepareEnablePackage(CBaseFile *package)
1965
{
1966
	ClearError();
1967
 
1968
	if ( package->GetParent() && !package->GetParent()->IsEnabled() )
1969
	{
1970
		m_iError = PKERR_NOPARENT;
1971
		return false;
1972
	}
1973
 
1974
	if ( m_bVanilla && !package->IsSigned() )
1975
	{
1976
		m_iError = PKERR_MODIFIED;
1977
		return false;
1978
	}
1979
 
1980
	// check if it needs depancies
1981
	if ( package->AnyDependacies() )
1982
	{
1983
		if ( this->GetMissingDependacies(package, NULL, true) )
1984
		{
1985
			m_iError = PKERR_MISSINGDEP;
1986
			return false;
1987
		}
1988
	}
1989
 
1990
	if ( !m_lEnableList.FindData(package) )
1991
	{
1992
		if ( !package->IsEnabled() )
1993
			m_lEnableList.push_back(package);
1994
 
1995
		// do all the children as well
1996
		if ( m_bAutoEnableChild )
1997
		{
1998
			CLinkList<CBaseFile> childList;
1999
			this->GetChildPackages(package, &childList, true);
2000
 
2001
			// add all disabled packages to list
2002
			for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
2003
			{
2004
				if ( !p->IsEnabled() )
2005
					m_lEnableList.push_back(p);
2006
			}
2007
		}
2008
	}
2009
 
2010
	m_bRemoveDir = true;
2011
 
2012
	return true;
2013
}
2014
 
2015
bool CPackages::PrepareDisableForVanilla()
2016
{
2017
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
2018
	{
2019
		CBaseFile *p = node->Data();
2020
		if ( !p->IsSigned() && p->IsEnabled() )
2021
		{
2022
			this->PrepareDisablePackage(p);
2023
			m_bDisableVanilla = true;
2024
		}
2025
	}
2026
 
2027
	return true;
2028
}
2029
 
2030
bool CPackages::PrepareEnableLibrarys()
2031
{
2032
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
2033
	{
2034
		CBaseFile *p = node->Data();
2035
		if ( p->GetType() != TYPE_SPK )
2036
			continue;
2037
 
2038
		if ( !p->IsEnabled() && ((CSpkFile *)p)->IsLibrary() )
2039
			this->PrepareEnablePackage(p);
2040
	}
2041
 
2042
	return true;
2043
}
2044
 
2045
bool CPackages::PrepareEnableFromVanilla()
2046
{
2047
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
2048
	{
2049
		CBaseFile *p = node->Data();
2050
		if ( !p->IsEnabled() && p->IsModifiedEnabled() ) {
2051
			this->PrepareEnablePackage(p);
2052
		}
2053
	}
2054
 
2055
	return true;
2056
}
2057
 
2058
int CPackages::GetDependacyList(CBaseFile *package, CLinkList<CBaseFile> *list)
2059
{
2060
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
2061
	{
2062
		CBaseFile *p = node->Data();
2063
		if ( p->IsPackageNeeded(package->GetName(), package->GetAuthor()) )
2064
			list->push_back(p);
2065
	}
2066
 
2067
	return list->size();
2068
}
2069
 
2070
bool CPackages::PrepareDisablePackage(CBaseFile *package)
2071
{
2072
	if ( !m_lDisableList.FindData(package) )
2073
	{
2074
		if ( package->IsEnabled() )
2075
			m_lDisableList.push_back(package);
2076
 
2077
		CLinkList<CBaseFile> childList;
2078
		this->GetChildPackages(package, &childList, true);
2079
 
2080
		// add all disabled packages to list
2081
		for ( CBaseFile *p = childList.First(); p; p = childList.Next() )
2082
		{
2083
			if ( p->IsEnabled() && !m_lDisableList.FindData(p) )
2084
				m_lDisableList.push_back(p);
2085
		}
2086
 
2087
		// if its a library, check for any dependacies
2088
		if ( package->GetType() == TYPE_SPK && ((CSpkFile *)package)->IsLibrary() )
2089
		{
2090
			CLinkList<CBaseFile> depList;
2091
			if ( this->GetDependacyList(package, &depList) )
2092
			{
2093
				for ( CBaseFile *p = depList.First(); p; p = depList.Next() )
2094
				{
2095
					if ( !m_lDisableList.FindData(p) )
2096
						this->PrepareDisablePackage(p);
2097
				}
2098
			}
2099
		}
2100
	}
2101
	return true;
2102
}
2103
 
2104
/**
2105
 * Prepares a package to be installed
2106
 */
2107
int CPackages::PrepareInstallPackage(CBaseFile *package, bool disabled, bool force, int check)
2108
{
2109
	// add package to list
2110
	if ( !m_lInstallList.FindData(package) )
2111
	{
2112
		int error = this->CheckInstallPackage(package, check);
2113
		if ( error == INSTALLCHECK_OK || force )
2114
		{
2115
			if ( disabled )
2116
				package->SetEnabled(false);
2117
 
2118
			bool added = false;
2119
			if ( package->GetType() == TYPE_SPK )
2120
			{
2121
				// find other mods in the currently added list
2122
				CSpkFile *spk = (CSpkFile *)package;
2123
				if ( spk->IsAnotherMod() )
2124
				{
2125
					for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
2126
					{
2127
						CBaseFile *p = node->Data();
10 cycrow 2128
						if ( spk->GetOtherName().Compare(p->GetName().ToString()) && spk->GetOtherAuthor().Compare(p->GetAuthor().ToString()) )
1 cycrow 2129
						{
2130
							m_lInstallList.insert(m_lInstallList.FindPos(p) + 2, package);
2131
							added = true;
2132
							break;
2133
						}
2134
					}
2135
				}
2136
			}
2137
 
2138
			if ( !added )
2139
			{
2140
				// check if we are a parent
2141
				for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
2142
				{
2143
					if ( node->Data()->GetType() != TYPE_SPK )
2144
						continue;
2145
 
2146
					CSpkFile *spk = (CSpkFile *)node->Data();
2147
					if ( !spk->IsAnotherMod() )
2148
						continue;
2149
 
10 cycrow 2150
					if ( spk->GetOtherName().Compare(package->GetName().ToString()) && spk->GetOtherAuthor().Compare(package->GetAuthor().ToString()) )
1 cycrow 2151
					{
2152
						added = true;
2153
						m_lInstallList.insert(node->Data(), package);
2154
						break;
2155
					}
2156
				}
2157
 
2158
				if ( !added )
2159
					m_lInstallList.push_back(package);
2160
			}
2161
 
2162
			return INSTALLCHECK_OK;
2163
		}
2164
 
2165
		package->SetLoadError(error);
2166
		return error;
2167
	}
2168
 
2169
	return INSTALLCHECK_ALREADYQUEUED;
2170
}
2171
 
2172
void CPackages::RemovePreparedInstall(CBaseFile *package)
2173
{
2174
	if ( !package )
2175
	{
2176
		m_lInstallList.MemoryClear();
2177
	}
2178
	else
2179
	{
2180
		if ( m_lInstallList.FindData(package) )
2181
		{
2182
			m_lInstallList.remove(package, true);
2183
		}
2184
	}
2185
}
2186
 
2187
char CPackages::ConvertWareTypeBack(int w)
2188
{
2189
	switch ( w )
2190
	{
2191
		case WARES_BIO:
2192
			return 'b';
2193
		case WARES_ENERGY:
2194
			return 'e';
2195
		case WARES_FOOD:
2196
			return 'f';
2197
		case WARES_MINERAL:
2198
			return 'm';
2199
		case WARES_TECH:
2200
			return 't';
2201
		case WARES_NATURAL:
2202
			return 'n';
2203
	}
2204
	return 't';
2205
}
2206
int CPackages::ConvertWareType(char w)
2207
{
2208
	switch ( LOWER(w) )
2209
	{
2210
		case 'b':
2211
			return WARES_BIO;
2212
		case 'e':
2213
			return WARES_ENERGY;
2214
		case 'f':
2215
			return WARES_FOOD;
2216
		case 'm':
2217
			return WARES_MINERAL;
2218
		case 't':
2219
			return WARES_TECH;
2220
		case 'n':
2221
			return WARES_NATURAL;
2222
	}
2223
	return WARES_TECH;
2224
}
2225
 
2226
void CPackages::SetupShips()
2227
{
2228
	for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
2229
	{
2230
		if ( wNode->Data()->iType != WARETYPE_NONE )
2231
			wNode->Data()->iType = WARETYPE_DELETED;
2232
	}
2233
 
2234
	// find any new ships to add to the list
2235
	for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
2236
	{
2237
		if ( pNode->Data()->GetType() != TYPE_XSP )
2238
			continue;
2239
		CXspFile *p = (CXspFile *)pNode->Data();
2240
 
2241
		bool found = false;
2242
		for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
2243
		{
2244
			if ( wNode->Data()->sShipID.Compare(p->GetShipID()) )
2245
			{
2246
				if ( !p->IsEnabled() )
2247
					wNode->Data()->iType = WARETYPE_DISABLED;
2248
				else
2249
					wNode->Data()->iType = WARETYPE_ADDED;
2250
				found = true;
2251
				wNode->Data()->pPackage = p;
2252
				wNode->Data()->sShipClass = p->GetShipClass();
2253
				break;
2254
			}
2255
		}
2256
 
2257
		if ( found || !p->IsEnabled() )
2258
			continue;
2259
 
2260
		// first find any free
2261
		SGameShip *gw = NULL;
2262
		for ( CListNode<SGameShip> *wNode = m_lGameShips.Front(); wNode; wNode = wNode->next() )
2263
		{
2264
			if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
2265
				gw = wNode->Data();
2266
			// find an old entry for the ware and add it to the same place
2267
			if ( wNode->Data()->sShipID.Compare(p->GetShipID()) )
2268
			{
2269
				gw = wNode->Data();
2270
				break;
2271
			}
2272
		}
2273
 
2274
		// none found, create one
2275
		if ( !gw )
2276
		{
2277
			gw = new SGameShip;
2278
			gw->sShipID = p->GetShipID();
2279
			gw->sShipClass = p->GetShipClass();
2280
			gw->pPackage = p;
2281
			m_lGameShips.push_back(gw);
2282
		}
2283
		gw->iType = WARETYPE_ADDED;
2284
	}
2285
}
2286
 
2287
void CPackages::SetupWares()
2288
{
2289
	for ( int i = 0; i < WAREBUFFERS; i++ )
2290
	{
2291
		for ( CListNode<SGameWare> *wNode = m_lGameWares[i].Front(); wNode; wNode = wNode->next() )
2292
		{
2293
			if ( wNode->Data()->iType != WARETYPE_NONE )
2294
				wNode->Data()->iType = WARETYPE_DELETED;
2295
		}
2296
	}
2297
 
2298
	// find any new wares to add to the list
2299
	for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
2300
	{
2301
		if ( pNode->Data()->GetType() != TYPE_SPK )
2302
			continue;
2303
		CSpkFile *p = (CSpkFile *)pNode->Data();
2304
 
2305
		for ( CListNode<SWares> *node = p->GetWaresList()->Front(); node; node = node->next() )
2306
		{
2307
			SWares *w = node->Data();
2308
			int wareType = CPackages::ConvertWareType(w->cType);
2309
 
2310
			// check if its on the list
2311
			bool found = false;
2312
			for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
2313
			{
2314
				if ( wNode->Data()->sWareName == w->sID )
2315
				{
2316
					if ( !p->IsEnabled() )
2317
						wNode->Data()->iType = WARETYPE_DISABLED;
2318
					else
2319
						wNode->Data()->iType = WARETYPE_ADDED;
2320
					wNode->Data()->pWare = w;
2321
					found = true;
2322
					break;
2323
				}
2324
			}
2325
 
2326
			if ( found || !p->IsEnabled() )
2327
				continue;
2328
 
2329
			// first find any free
2330
			SGameWare *gw = NULL;
2331
			for ( CListNode<SGameWare> *wNode = m_lGameWares[wareType].Front(); wNode; wNode = wNode->next() )
2332
			{
2333
				if ( (!gw) && (wNode->Data()->iType == WARETYPE_NONE) )
2334
					gw = wNode->Data();
2335
				// find an old entry for the ware and add it to the same place
2336
				if ( wNode->Data()->sWareName == w->sID )
2337
				{
2338
					gw = wNode->Data();
2339
					break;
2340
				}
2341
			}
2342
 
2343
			// none found, create one
2344
			if ( !gw )
2345
			{
2346
				gw = new SGameWare;
2347
				gw->sWareName = w->sID;
2348
				gw->iPos = m_lGameWares[wareType].size();
2349
				gw->pWare = w;
2350
				gw->cType = w->cType;
2351
				m_lGameWares[wareType].push_back(gw);
2352
			}
2353
			gw->iType = WARETYPE_ADDED;
2354
		}
2355
	}
2356
}
2357
 
2358
/**
2359
 * Closing the current directory
2360
 *
2361
 * When existing, program needs to close the directory
2362
 */
2363
bool CPackages::CloseDir ( CyStringList *errors, CProgressInfo *progress, bool removedir )
2364
{
2365
	if ( m_sCurrentDir.Empty() )
2366
		return true;
2367
	if ( !m_bLoaded )
2368
		return true;
2369
 
2370
	m_sActiveDir = m_sCurrentDir;
2371
 
2372
	if ( m_bRenameText )
2373
		CreateLanguageTextFiles(errors);
2374
 
2375
	this->BackupSaves();
2376
 
2377
	if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.dat").Exists() )
2378
		CFileIO(m_sCurrentDir + "/mods/PluginManager.dat").Remove();
2379
	if ( CFileIO(m_sCurrentDir + "/mods/PluginManager.cat").Exists() )
2380
		CFileIO(m_sCurrentDir + "/mods/PluginManager.cat").Remove();
2381
 
2382
	if ( !m_bVanilla )
2383
	{
2384
		// base mode for Reunion
2385
		if ( m_iGame == GAME_X3 && m_pEnabledMod )
2386
		{
2387
			C_File *fDat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
2388
			while ( fDat && !fDat->IsFakePatch() && !fDat->CheckFileExt("dat") )
2389
				fDat = m_pEnabledMod->GetNextFile(fDat);
2390
 
2391
			if ( fDat )
2392
			{
2393
				C_File *fCat = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
2394
				while ( fCat && !fCat->IsFakePatch() && !fCat->CheckFileExt("cat") && !fCat->GetBaseName().Compare(fDat->GetBaseName()) )
2395
					fCat = m_pEnabledMod->GetNextFile(fCat);
2396
 
2397
				if ( fCat )
2398
				{
2399
					CFileIO(fDat->GetFilePointer()).Copy(m_sCurrentDir + "/mods/PluginManager.dat");
2400
					CFileIO(fCat->GetFilePointer()).Copy(m_sCurrentDir + "/mods/PluginManager.cat");
2401
				}
2402
			}
2403
		}
2404
		else if ( m_iGame == GAME_X3 && !m_sSetMod.Empty() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Exists() && CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").Exists() )
2405
		{
2406
			CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".dat").Copy(m_sCurrentDir + "/mods/PluginManager.dat");
2407
			CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Copy(m_sCurrentDir + "/mods/PluginManager.cat");
2408
		}
2409
 
2410
		if ( !CDirIO(m_sCurrentDir).Exists("mods") )
2411
			CDirIO(m_sCurrentDir).Create("mods");
2412
 
2413
		SetupWares();
2414
		SetupShips();
2415
		CreateEMPFile();
2416
		CreateWareFiles();
2417
		CreateDummies();
2418
		CreateComponants();
2419
		CreateTShips();
2420
		CreateCutData();
2421
		CreateBodies();
2422
		CreateAnimations();
2423
		CreateCustomStarts();
2424
		CreateGlobals();
2425
		CreatePluginManagerText();
2426
		RestoreFakePatch();
2427
	}
2428
 
2429
	RemoveFailedFiles();
2430
 
2431
	WriteData();
2432
	if ( removedir && m_bRemoveDir )
2433
	{
2434
		m_bRemoveDir = false;
2435
		CyStringList removeDirs;
2436
		removeDirs.PushBack(".");
2437
		RemoveUnusedDirectories(removeDirs, errors);
2438
	}
2439
 
2440
	m_bLoaded = false;
2441
	return true;
2442
}
2443
 
2444
CyString CPackages::GetModKey()
2445
{
2446
	return m_gameExe.GetModKey(m_iGame - 1);
2447
}
2448
CyString CPackages::GetSelectedModName()
2449
{
2450
	if ( !m_pEnabledMod )
2451
	{
2452
		if ( m_sSetMod.Empty() && m_iGame == GAME_X3 )
2453
			return "PluginManager";
2454
		return m_sSetMod;
2455
	}
2456
 
2457
	if ( !m_pEnabledMod->IsEnabled() )
2458
		return m_sSetMod;
2459
 
2460
	C_File *f = m_pEnabledMod->GetFirstFile(FILETYPE_MOD);
2461
	if ( !f )
2462
		return m_sSetMod;
2463
 
2464
	CyString name = f->GetFilename();
2465
	name = name.Left(-4);
2466
	return name;
2467
 
2468
}
2469
 
2470
bool CPackages::RestoreFakePatch()
2471
{
2472
	m_iFakePatch = -1;
2473
 
2474
	CFileIO catFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat");
2475
	CFileIO datFile(m_sCurrentDir + "/PluginManager/PlugMan_Fake.dat");
2476
 
2477
	// if only 1 exists, remove it
2478
	if ( catFile.Exists() && !datFile.Exists() )
2479
	{
2480
		if ( !catFile.Remove() )
2481
			return false;
2482
	}
2483
	else if ( !catFile.Exists() && datFile.Exists() )
2484
	{
2485
		if ( !datFile.Remove() )
2486
			return false;
2487
	}
2488
 
2489
	// if both exists, lets rename them
2490
	if ( catFile.Exists() && datFile.Exists() )
2491
	{
2492
		// we need to add the plugin manager file in
2493
		CyString file = m_sTempDir;
2494
		if ( !file.Empty() )
2495
			file += "/";
2496
		file += "pluginmanagerfake.txt";
2497
		file = file.FindReplace("\\", "/");
2498
		CFileIO fakeFile(file);
2499
		std::vector<CyString> lines;
2500
		lines.push_back("//pluginmanager fake patch");
2501
		fakeFile.WriteFile(&lines);
2502
 
2503
		CCatFile fakePatch;
2504
		if ( fakePatch.Open(m_sCurrentDir + "/PluginManager/PlugMan_Fake.cat", this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE )
2505
		{
2506
			if ( fakePatch.AppendFile(file, "PlugMan\\TFake.pck") )
2507
			{
2508
				// find next available fake patch
2509
				m_iFakePatch = this->FindNextFakePatch();
2510
 
2511
				CyString filename = CyString::Number(m_iFakePatch).PadNumber(2);
2512
				if ( catFile.Rename(m_sCurrentDir + "/" + filename + ".cat") )
2513
				{
2514
					if ( datFile.Rename(m_sCurrentDir + "/" + filename + ".dat") )
2515
					{
2516
						fakeFile.Remove();
2517
						return true;
2518
					}
2519
 
2520
					// TODO: restore cat file
2521
				}
2522
			}
2523
		}
2524
 
2525
		fakeFile.Remove();
2526
		return false;
2527
	}
2528
 
2529
	return true;
2530
}
2531
 
2532
 
2533
/**
2534
 * Save package detail to date file
2535
 *
2536
 * Writes the current package list data into pluginmanager.dat file
2537
 */
2538
void CPackages::WriteData()
2539
{
2540
	if ( m_sCurrentDir.Empty() )
2541
		return;
2542
 
2543
	CyStringList lines;
2544
	CyString version;
2545
	version.FromFloat(GetLibraryVersion(), 2);
2546
	version.Prepend("SpkInstaller: ");
2547
	lines.PushBack(version);
2548
 
2549
	lines.PushBack(CyString("UpdateTime: ") + (long)m_iLastUpdated);
2550
	if ( m_iFakePatch != -1 )
2551
		lines.PushBack(CyString("FakePatch: ") + (long)m_iFakePatch);
2552
	if ( m_iSaveGame != -1 )
2553
		lines.PushBack(CyString("SaveGame: ") + (long)m_iSaveGame);
2554
	lines.PushBack(CyString("SaveGameManager: ") + (long)m_iSaveGameManager);
2555
	if ( !m_bVanilla )
2556
		lines.PushBack("Modified");
2557
	if ( m_bUsedWare )
2558
		lines.PushBack("UsedWare");
2559
	if ( m_bSurpressProtectedWarning )
2560
		lines.PushBack("SurpressProtectedWarning");
2561
	if ( !m_sSetMod.Empty() )
2562
		lines.PushBack(CyString("SetMod: ") + m_sSetMod);
2563
	lines.PushBack(CyString("ShipBuffer: ") + (long)m_iShipBuffer);
2564
	CyString wareBuffer = "WareBuffers:";
2565
	for ( int i = 0; i < WAREBUFFERS; i++ )
2566
		wareBuffer += CyString(" ") + (long)m_iWareBuffer[i];
2567
	lines.PushBack(wareBuffer);
2568
	for ( int i = 0; i < WAREBUFFERS; i++ )
2569
	{
2570
		if ( !m_lGameWares[i].size() )
2571
			continue;
2572
		lines.PushBack(CyString("Wares: ") + (long)i + " " + (long)m_lGameWares[i].size());
2573
 
2574
		for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
2575
		{
2576
			SGameWare *gm = node->Data();
2577
			lines.PushBack(CyString((long)gm->iPos) + " " + (long)gm->iType + " " + CyString((char)gm->cType) + " " + gm->sWareName);
2578
		}
2579
	}
2580
 
2581
	for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
2582
		lines.PushBack(CyString("NonRemovedFile: ") + str->str);
2583
 
2584
	if ( m_lGameShips.size() )
2585
	{
2586
		lines.PushBack(CyString("Ships: ") + (long)m_lGameShips.size());
2587
 
2588
		for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
2589
		{
2590
			SGameShip *gm = node->Data();
2591
			lines.PushBack(CyString((long)gm->iType) + " $#C:" + gm->sShipClass + " " + gm->sShipID);
2592
		}
2593
	}
2594
 
2595
	// write created Files
2596
	if ( !m_lCreatedFiles.Empty() )
2597
	{
2598
		for ( SStringList *node = m_lCreatedFiles.Head(); node; node = node->next )
2599
			lines.PushBack(CyString("CreatedFile: ") + node->str.Remove(m_sCurrentDir));
2600
	}
2601
 
2602
	// write uninstall files
2603
	if ( !m_lUninstallFiles.empty() )
2604
	{
2605
		for ( CListNode<C_File> *node = m_lUninstallFiles.Front(); node; node = node->next() )
2606
		{
2607
			C_File *uf = node->Data();
2608
			CyString uString = "Uninstall: ";
2609
			uString += CyString::Number((long)uf->GetCreationTime()) + " ";
2610
			uString += uf->GetFilename();
2611
			lines.PushBack(uString);
2612
		}
2613
	}
2614
 
2615
	// write the original file data
2616
	if ( !m_lOriginalFiles.empty() )
2617
	{
2618
		for ( CListNode<C_File> *node = m_lOriginalFiles.Front(); node; node = node->next() )
2619
		{
2620
			C_File *uf = node->Data();
2621
			CyString uString = "Original: ";
2622
			uString += CyString::Number((long)uf->GetFileType()) + " ";
2623
			uString += uf->GetFilename();
2624
			if ( uf->GetFileType() == FILETYPE_EXTRA && !uf->GetDir().Empty())
2625
			{
2626
				uString += ":";
2627
				uString += uf->GetDir();
2628
			}
2629
			lines.PushBack(uString);
2630
		}
2631
	}
2632
 
2633
	// write the global changes
2634
	if ( !m_lGlobals.Empty() )
2635
	{
2636
		for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
2637
			lines.PushBack(CyString("GlobalSetting: ") + str->str + ":" + str->data.Remove(";"));
2638
	}
2639
 
2640
	// write the fake patch ordering
2641
	if ( !m_lFakePatchOrder.Empty() )
2642
	{
2643
		for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
2644
			lines.PushBack(CyString("FakePatchOrder: ") + str->str + ":" + str->data);
2645
	}
2646
 
2647
	// write the global file list
2648
	lines.PushBack("GlobalFiles:");
2649
	int num = 0;
2650
	for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
2651
	{
2652
		C_File *f = fn->Data();
2653
		f->SetPos(num++);
2654
 
2655
		CyString line = CyString::Number(f->GetFileType()) + ":" + CyString::Number((long)f->GetCreationTime()) + ":" + f->GetDir() + ":";
2656
		if ( f->IsShared() && !f->IsFakePatch() )
2657
			line += "1:";
2658
		else
2659
			line += "0:";
2660
 
2661
		CyString filename = f->GetFilePointer();
2662
		filename.Remove(m_sCurrentDir);
2663
 
2664
		if ( f->IsDisabled() )
2665
			line += "D#";
2666
 
2667
		line += "G#";
2668
		line += (long)f->GetGame();
2669
		line += "#";
2670
 
2671
		line += filename;
2672
 
2673
		if ( !f->GetOriginalName().Empty() )
2674
		{
2675
			line += "O#";
2676
			line += f->GetOriginalName();
2677
		}
2678
 
2679
		lines.PushBack(line);
2680
	}
2681
 
2682
	// write the package list
2683
	for ( CListNode<CBaseFile> *pn = m_lPackages.Front(); pn; pn = pn->next() )
2684
	{
2685
		CBaseFile *package = pn->Data();
2686
 
2687
		if ( package->GetType() == TYPE_SPK )
2688
			lines.PushBack("<script>");
2689
		else if ( package->GetType() == TYPE_XSP )
2690
			lines.PushBack("<ship>");
2691
		else if ( package->GetType() == TYPE_ARCHIVE )
2692
			lines.PushBack("<archive>");
2693
		else if ( package->GetType() == TYPE_BASE )
2694
			lines.PushBack("<base>");
2695
		else
2696
			continue;
2697
 
2698
		if ( !package->GetFilename().Empty() )
2699
			lines.PushBack(CyString("Installspk: ") + package->GetFilename());
2700
 
2701
		CyString valuesline = package->CreateValuesLine();
2702
		if ( valuesline.Right(1) == '\n' )
2703
			valuesline.Truncate((int)valuesline.Length() - 1);
2704
		lines.PushBack(valuesline);
2705
 
2706
		if ( !package->IsEnabled() )
2707
			lines.PushBack("Disabled");
2708
		if ( !package->IsModifiedEnabled() )
2709
			lines.PushBack("ModifiedDisabled");
2710
 
2711
		if ( package->GetIcon() )
2712
			lines.PushBack(CyString("Icon: ") + package->GetIconExt() + " " + package->GetIcon()->GetFilePointer() );
2713
 
2714
		CyString fileline("Files:");
2715
		for ( CListNode<C_File> *fn = package->GetFileList()->Front(); fn; fn = fn->next() )
2716
		{
2717
			C_File *f = fn->Data();
2718
			fileline += " ";
2719
			fileline += CyString::Number(f->GetPos());
2720
		}
2721
		lines.PushBack(fileline);
2722
	}
2723
 
2724
	lines.PushBack("</scripts>");
2725
 
2726
	CFileIO datFile(m_sCurrentDir + "/PluginManager/PluginManager.dat");
2727
 
2728
	CDirIO Dir(m_sCurrentDir);
2729
	if ( !Dir.Exists("PluginManager") )
2730
		Dir.Create("PluginManager");
2731
 
2732
 
2733
	if ( !datFile.WriteFile(&lines) )
2734
	{
2735
	}
2736
}
2737
 
2738
 
2739
/**
2740
 * Get All Files
2741
 *
2742
 * Gets a list of all files, includes any child package files
2743
 */
2744
int CPackages::GetAllPackageFiles(CBaseFile *package, CLinkList<C_File> *fileList, bool includeChild)
2745
{
2746
	for ( CListNode<C_File> *node = package->GetFileList()->Front(); node; node = node->next() )
2747
	{
2748
		C_File *f = node->Data();
2749
		if ( !fileList->FindData(f) )
2750
			fileList->push_back(f);
2751
	}
2752
 
2753
	if ( includeChild )
2754
	{
2755
		CLinkList<CBaseFile> childList;
2756
		if ( this->GetChildPackages(package, &childList) )
2757
		{
2758
			for ( CBaseFile *child = childList.First(); child; child = childList.Next() )
2759
				this->GetAllPackageFiles(child, fileList, includeChild);
2760
		}
2761
	}
2762
 
2763
	// disablign for vanilla, make sure we add all files
2764
	if ( m_bDisableVanilla )
2765
	{
2766
		for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
2767
		{
2768
			C_File *f = node->Data();
2769
			if ( !fileList->FindData(f) )
2770
				fileList->push_back(f);
2771
		}
2772
	}
2773
 
2774
	return fileList->size();
2775
}
2776
 
2777
int CPackages::GetAllPackageFiles(CLinkList<CBaseFile> *list, CLinkList<C_File> *fileList, bool includeChild)
2778
{
2779
	for ( CListNode<CBaseFile> *node = list->Front(); node; node = node->next() )
2780
		this->GetAllPackageFiles(node->Data(), fileList, includeChild);
2781
	return fileList->size();
2782
}
2783
 
2784
/**
2785
 * Add Log
2786
 *
2787
 * Adds a log entry to displayed at end
2788
 */
2789
void CPackages::AddLogEntry(int type, CyString args, CyStringList *errors)
2790
{
2791
	if ( !errors )
2792
		return;
2793
 
2794
	errors->PushBack(args, ERRORLOG(type));
2795
}
2796
 
2797
/**
2798
 * Enable a package
2799
 *
2800
 * Enables all files in the package, unless they are already enabled, ie, used by other packages
2801
 * Backs up any original files before attempting to enable the file
2802
 *
2803
 * Fake patches are renamed to the next available slot
2804
 *
2805
 * Clears up the "PluginManager/Disabled" directory
2806
 *
2807
 * param: package		- The package class to be removed
2808
 * param: errors		- The string list for all the status for debugging, ie has an entry for whats happened to every file
2809
 * param: progress		- The progress class, updates a progress screen of the derived class
2810
 *
2811
 * return: boolen - Returns true if the package enabling was successful
2812
 */
2813
bool CPackages::EnablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
2814
{
2815
	ClearError();
2816
 
2817
	// if already enabled, just skip
2818
	if ( package->IsEnabled() )
2819
		return true;
2820
 
2821
	// check if parent is enabled
2822
	if ( package->GetParent() && !package->GetParent()->IsEnabled() )
2823
	{
2824
		m_iError = PKERR_NOPARENT;
2825
		return false;
2826
	}
2827
 
2828
	// check for modified
2829
	if ( m_bVanilla && !package->IsSigned() )
2830
	{
2831
		m_iError = PKERR_MODIFIED;
2832
		return false;
2833
	}
2834
 
2835
	if ( this->PrepareEnablePackage(package) )
2836
		return this->EnablePreparedPackages(errors, progress);
2837
 
2838
	return false;
2839
}
2840
bool CPackages::EnablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *enabledPackages )
2841
{
2842
	ClearError();
2843
 
2844
	// get all files, including children
2845
	CLinkList<C_File> fileList;
2846
	int maxFiles = this->GetAllPackageFiles(&m_lEnableList, &fileList, false);
2847
 
2848
	int fileCount = 0;
2849
	// we use this list to match cat and dat files
2850
	CyStringList fakePatches;
2851
	for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
2852
	{
2853
		C_File *f = node->Data();
2854
		if ( f->GetGame() && m_iGame ) {
2855
			if ( f->GetGame() != m_iGame )
2856
				continue;
2857
		}
2858
		CBaseFile *package = NULL;
2859
		for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
2860
		{
2861
			CBaseFile *p = node->Data();
2862
			if ( p->IsFileAdded(f) )
2863
			{
2864
				package = p;
2865
				break;
2866
			}
2867
		}
2868
 
2869
		if ( progress )
2870
		{
2871
			progress->UpdateProgress(fileCount++, maxFiles);
2872
			progress->UpdateFile(f);
2873
		}
2874
 
2875
		// only move fiels that are disabled
2876
		if ( !f->IsDisabled() )
2877
			continue;
2878
 
2879
		// make the directory if it dont exist
2880
		CDirIO Dir(m_sCurrentDir);
2881
 
2882
		// fake patches are in the root, no need for directory
2883
		if ( !f->IsFakePatch() )
2884
		{
2885
			if ( !Dir.Exists(f->GetDirectory(package)) )
2886
			{
2887
				if ( !Dir.Create(f->GetDirectory(package)) )
2888
				{
2889
					this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, f->GetDirectory(package), errors);
2890
					continue;
2891
				}
2892
				this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, f->GetDirectory(package), errors);
2893
			}
2894
		}
2895
 
2896
		// check if theres an original file to backup
2897
		C_File *of = GetOriginalFile(f);
2898
		if ( of )
2899
		{
2900
			// check if the orignal file is already backed up
2901
			if ( !CFileIO(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(package)).Exists() )
2902
			{
2903
				// lets back up the file now
2904
				BackupOriginalFile(of, errors);
2905
			}
2906
		}
2907
 
2908
		CyString newFilename = f->GetNameDirectory(package);
2909
 
2910
		// fake patches need to be renamed
2911
		if ( f->IsFakePatch() )
2912
		{
2913
			// first check if the matching file has been done already
2914
			SStringList *foundStr = fakePatches.FindString(f->GetBaseName());
2915
 
2916
			// already done one, simply change this to match
2917
			if ( foundStr )
2918
				newFilename = foundStr->data + "." + f->GetFileExt();
2919
 
2920
			// we need to find the next available number instead
2921
			else
2922
			{
2923
				CyString newPos = CyString::Number(FindNextFakePatch()).PadNumber(2);
2924
				newFilename = newPos + "." + f->GetFileExt();
2925
				// and wee need to push this onto the string list so we can adjust the matchign pair
2926
				fakePatches.PushBack(f->GetBaseName(), newPos);
2927
			}
2928
		}
2929
 
2930
		// lets actually move the file back now
2931
		// !!error checking!!
2932
		CFileIO currentFile(f->GetFilePointer());
2933
		CFileIO newFile(m_sCurrentDir + "/" + newFilename);
2934
		if ( !currentFile.Exists() )
2935
		{
2936
			// missing file ??
2937
			if ( !newFile.Exists() )
2938
			{
2939
				this->AddLogEntry(SPKINSTALL_MISSINGFILE, newFilename, errors);
2940
				continue;
2941
			}
2942
		}
2943
		// remove existing file
2944
		// file exists, so lets try to move it
2945
		else
2946
		{
2947
			if ( newFile.Exists() )
2948
				newFile.Remove();
2949
 
2950
			if ( !currentFile.Rename(newFile.GetFullFilename()) )
2951
			{
2952
				this->AddLogEntry(SPKINSTALL_ENABLEFILE_FAIL, newFilename, errors);
2953
				continue;
2954
			}
2955
		}
2956
 
2957
		this->AddLogEntry(SPKINSTALL_ENABLEFILE, newFilename, errors);
2958
 
2959
		// adjust the internal name to match the new filename
2960
		f->SetFilename(m_sCurrentDir + "/" + newFilename);
2961
		// no longer disabled, we need to remove the flag
2962
		f->SetDisabled(false);
2963
	}
2964
 
2965
	// recursive, auto enable all children
2966
	CBaseFile *oldMod = m_pEnabledMod;
2967
	CBaseFile *pMod = NULL;
2968
 
2969
	for ( CListNode<CBaseFile> *node = m_lEnableList.Front(); node; node = node->next() )
2970
	{
2971
		CBaseFile *p = node->Data();
2972
		p->SetEnabled(true);
2973
		if ( p->IsMod() && !pMod )
2974
			pMod = p;
2975
 
2976
		if ( enabledPackages )
2977
			enabledPackages->push_back(p);
2978
	}
2979
 
2980
	if ( pMod )
2981
		m_pEnabledMod = pMod;
2982
 
2983
	// disabled the mod
2984
	if ( oldMod && oldMod != m_pEnabledMod && !m_bForceModInstall )
2985
		this->DisablePackage(oldMod, errors, progress);
2986
 
2987
	// lets remove all the directories we might have left empty
2988
	CyStringList removeDirs;
2989
	removeDirs.PushBack(CyString("PluginManager/Disabled"));
2990
	RemoveUnusedDirectories(removeDirs, errors);
2991
 
2992
	m_lEnableList.clear();
2993
 
2994
	this->WriteData();
2995
 
2996
	return true;
2997
}
2998
 
2999
 
3000
bool CPackages::DisablePreparedPackages ( CyStringList *errors, CProgressInfo *progress, CLinkList<CBaseFile> *disabledPackages )
3001
{
3002
	if ( progress )
3003
		progress->UpdateStatus(PROGRESS_DISABLEFILE);
3004
 
3005
	UpdateUsedFiles(&m_lDisableList, false);
3006
 
3007
	// checks if there are any original files to restore and if any fake patches were disabled to reshuffle
3008
	bool original = false, shuffle = false;
3009
 
3010
	// holds our list of directories that we might need to remove, only empty ones from this list will actually be removed
3011
	CyStringList removeDirs;
3012
 
3013
	// get all files, including children
3014
	CLinkList<C_File> fileList;
3015
	int maxFiles = this->GetAllPackageFiles(&m_lDisableList, &fileList, true);
3016
 
3017
	// interate through all the files in the package
3018
	int fileCount = 0;
3019
	for ( CListNode<C_File> *node = fileList.Front(); node; node = node->next() )
3020
	{
3021
		C_File *f = node->Data();
3022
		CBaseFile *checkPackage = NULL;
3023
		for ( CListNode<CBaseFile> *node = m_lDisableList.Front(); node; node = node->next() )
3024
		{
3025
			CBaseFile *p = node->Data();
3026
			if ( p->IsFileAdded(f) )
3027
			{
3028
				checkPackage = p;
3029
				break;
3030
			}
3031
		}
3032
 
3033
		// update the progress count for the current file
3034
		if ( progress )
3035
		{
3036
			progress->UpdateProgress(++fileCount, maxFiles);
3037
			progress->UpdateFile(f);
3038
		}
3039
 
3040
		// only delete files that are not used by any other enabled packages, counter from UpdateUsedFiles()
3041
		if ( f->GetUsed() || (f->IsShared() && !m_bDisableVanilla) )
3042
			continue;
3043
 
3044
		// file is already disabled, no need to disable again
3045
		if ( f->IsDisabled() )
3046
			continue;
3047
 
3048
		// readmes, uninstall and extra files dont need to be disabled
3049
		// Extra files not in the "Extras" directory could be anywhere, so these should be disabled incase they are game changing files, ie in "types"
3050
		if ( f->GetFileType() == FILETYPE_README || f->GetFileType() == FILETYPE_UNINSTALL || (f->GetFileType() == FILETYPE_EXTRA && f->GetDir().Left(5).lower() == "Extra") )
3051
			continue;
3052
 
3053
		// check if there is a matching uninstall file, ie there the script file is also an uninstall script file for a previously uninstalled package that has yet to be removed
3054
		if ( f->GetFileType() == FILETYPE_SCRIPT )
3055
		{
3056
			bool found = false;
3057
			for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
3058
			{
3059
				C_File *uFile = uNode->Data();
3060
				if ( uFile->GetFilename().Compare(f->GetFilename()) )
3061
				{
3062
					found = true;
3063
					break;
3064
				}
3065
			}
3066
 
3067
			if ( found )
3068
				continue;
3069
		}
3070
 
3071
		if ( f->GetFileType() == FILETYPE_MOD && !f->IsFakePatch() && f->CheckFileExt("cat") )
3072
		{
3073
			if ( f->GetBaseName().Compare(m_sSetMod) )
3074
				m_sSetMod = NullString;
3075
		}
3076
 
3077
		// file is not being used by any enabled package
3078
		// set disabled and move to disabled directory
3079
		CDirIO Dir(m_sCurrentDir);
3080
		CyString newFilename = "PluginManager/Disabled/";
3081
 
3082
		// fake patches have thier own special directory
3083
		if ( f->IsFakePatch() )
3084
			newFilename += "FakePatches";
3085
		// otherwise we put them in thier usual directory structure inside the disabled dir
3086
		else
3087
			newFilename += f->GetDirectory(NULL);
3088
 
3089
		// make sure the directory exists so we can move the file
3090
		if ( !Dir.Exists(newFilename) )
3091
		{
3092
			// we couldn't create the directory for some reason, this is not good
3093
			if ( !Dir.Create(newFilename) )
3094
			{
3095
				this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newFilename, errors);
3096
				continue;
3097
			}
3098
			this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, newFilename, errors);
3099
		}
3100
 
3101
		// fake patches need a special directory and filename so they dont overright each other as thier filenames are always changes
3102
		// if a package with a fake patch is installed while another one is disabled, it will fill the gap left by the disabled one
3103
		// they will then end up with the same filename, if it gets disabled they would overright each other, so we change the filename to prevent that from happening
3104
		if ( f->IsFakePatch() )
3105
		{
3106
			// find package the fake patch belongs to
3107
			if ( checkPackage )
3108
			{
3109
				newFilename = m_sCurrentDir + "/PluginManager/Disabled/FakePatches/FakePatch_" + checkPackage->GetNameValidFile() + "_" + checkPackage->GetAuthor() + "_" + f->GetName();
3110
				shuffle = true;
3111
			}
3112
		}
3113
		else if ( f->IsAutoTextFile() )
3114
		{
3115
			if ( checkPackage )
3116
			{
3117
				newFilename = m_sCurrentDir + "/PluginManager/Disabled/TextFiles/Text_" + checkPackage->GetNameValidFile() + "_" + checkPackage->GetAuthor() + "_" + f->GetName();
3118
				shuffle = true;
3119
			}
3120
		}
3121
		// otherwise we can just use the standard filename
3122
		else
3123
			newFilename = m_sCurrentDir + "/PluginManager/Disabled/" + f->GetNameDirectory(checkPackage);
3124
 
3125
		// now to move the file by renameing it to its new location
3126
		// !!error checking!!
3127
		// check the file, if it doesn't exist, and exists as disabled, we should just adjust the setting instead of an error
3128
		CFileIO currentFile(f->GetFilePointer());
3129
		if ( !currentFile.Exists() )
3130
		{
3131
			if ( !CFileIO(newFilename).Exists() )
3132
			{
3133
				this->AddLogEntry(SPKINSTALL_MISSINGFILE, f->GetNameDirectory(checkPackage), errors);
3134
				continue;
3135
			}
3136
		}
3137
		// otherwise the file must exists, so lets move it
3138
		else if ( !currentFile.Rename(newFilename) )
3139
		{
3140
			this->AddLogEntry(SPKINSTALL_DISABLEFILE_FAIL, f->GetNameDirectory(checkPackage), errors);
3141
			continue;
3142
		}
3143
 
3144
		// must have been fine
3145
		this->AddLogEntry(SPKINSTALL_DISABLEFILE, f->GetNameDirectory(checkPackage), errors);
3146
 
3147
		// check if its an original file and restore
3148
		if ( IsOriginalFile(f) )
3149
		{
3150
			CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(checkPackage));
3151
			if ( of.Exists() )
3152
			{
3153
				original = true;
3154
				if ( of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(checkPackage)) )
3155
					this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE, f->GetNameDirectory(checkPackage), errors);
3156
				else
3157
					this->AddLogEntry(SPKINSTALL_ORIGINAL_RESTORE_FAIL, f->GetNameDirectory(checkPackage), errors);
3158
			}
3159
		}
3160
 
3161
		// extra file thats not in the extras directory
3162
		if ( f->GetFileType() == FILETYPE_EXTRA || f->GetFileType() == FILETYPE_MAP || f->GetFileType() == FILETYPE_SOUND )
3163
			removeDirs.PushBack(f->GetDirectory(checkPackage), NullString, true);
3164
 
3165
		// change the filename
3166
		f->SetFilename(newFilename);
3167
 
3168
		// finally mark the file as disabled so we know not to try to move it again
3169
		f->SetDisabled(true);
3170
	}
3171
 
3172
	// a fake patch has been disabled, we need to reshuffle the rest to fill in any gaps
3173
	if ( shuffle )
3174
	{
3175
		if ( progress )
3176
			progress->UpdateStatus(PROGRESS_SHUFFLEFAKE);
3177
		ShuffleFakePatches(errors);
3178
		ShuffleTextFiles(errors);
3179
	}
3180
 
3181
	// original files were restored, check to remove the original file directory if its now empty
3182
	if ( original )
3183
		removeDirs.PushBack(CyString("PluginManager/Original"));
3184
 
3185
	// remove any empty directories that we might have left
3186
	if ( !removeDirs.Empty() )
3187
		RemoveUnusedDirectories(removeDirs, errors);
3188
 
3189
	// finally mark the whole package as disabled
3190
	// recursive, we need to disable all children
3191
	for ( CBaseFile *child = m_lDisableList.First(); child; child = m_lDisableList.Next() )
3192
	{
3193
		if ( m_pEnabledMod == child )
3194
			m_pEnabledMod = NULL;
3195
		child->SetEnabled(false);
3196
 
3197
		if ( disabledPackages )
3198
			disabledPackages->push_back(child);
3199
	}
3200
 
3201
	// disabling has completed successfully, we hope
3202
	m_bDisableVanilla = false;
3203
	m_lDisableList.clear();
3204
 
3205
	this->WriteData();
3206
 
3207
	return true;
3208
}
3209
 
3210
/**
3211
 * Disables the selected package
3212
 *
3213
 * Disables all enabled files that are not being used by any other enabled package
3214
 * Any files that are being used by other enabled packages are skipped and left enabled
3215
 *
3216
 * Original Files are restored when file is disabled
3217
 *
3218
 * Fake patches are shuffled to fill in any gaps caused by disabling fake patches
3219
 *
3220
 * All files go into the Plugin/Disabled directory into thier respective directories.
3221
 *
3222
 * Any directories left empty when disabling files are then removed
3223
 *
3224
 * param: package	- Package file to be disabled
3225
 * param: errors	- A string list used to add the status as it progresses, used in debugging output
3226
 * param: progress	- The progress class, updates the progress of the current disabling.  Needs a divered class to report the progress somewhere
3227
 *
3228
 * return: boolean, true if there was no errors, otherwise false
3229
 */
3230
bool CPackages::DisablePackage ( CBaseFile *package, CyStringList *errors, CProgressInfo *progress )
3231
{
3232
	// if already disabled, just skip
3233
	if ( !package->IsEnabled() )
3234
		return true;
3235
 
3236
	m_lDisableList.clear();
3237
	if ( this->PrepareDisablePackage(package) )
3238
		return this->DisablePreparedPackages(errors, progress);
3239
 
3240
	return false;
3241
}
3242
 
3243
 
3244
/**
3245
 * Find a Package
3246
 *
3247
 * Finds a matching package so we can find if one is already installed
3248
 *
3249
 * Uses seperate functions for each package type, ie for SPK and XSP packages
3250
 */
3251
CBaseFile *CPackages::FindPackage(CBaseFile *package)
3252
{
3253
	// no point checking if we've been sent a null pointer
3254
	if ( !package )
3255
		return 0;
3256
 
3257
	// we are checking against a SPK package, so we match the name and author
3258
	if ( package->GetType() == TYPE_SPK )
3259
		return FindSpkPackage(package->GetName(), package->GetAuthor());
3260
	else if ( package->GetType() == TYPE_XSP )
3261
		return FindXspPackage(((CXspFile *)package)->GetShipID());
3262
	else if ( package->GetType() == TYPE_ARCHIVE )
3263
		return FindArchivePackage(package->GetName());
3264
 
3265
	// nothing found obviously
3266
	return 0;
3267
}
3268
 
3269
CBaseFile *CPackages::FindFirstPackageWithFile(C_File *f)
3270
{
3271
	return FindNextPackageWithFile(NULL, f);
3272
}
3273
 
3274
CBaseFile *CPackages::FindNextPackageWithFile(CBaseFile *p, C_File *f)
3275
{
3276
	bool startCheck = (p) ? false : true;
3277
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3278
	{
3279
		if ( startCheck )
3280
		{
3281
			if ( node->Data()->IsFileAdded(f) )
3282
				return node->Data();
3283
		}
3284
		else if ( p == node->Data() )
3285
			startCheck = true;
3286
	}
3287
 
3288
	return NULL;
3289
}
3290
 
3291
 
3292
/**
3293
 * Find a File
3294
 *
3295
 * Searches for a file matching the filetype and filename
3296
 * Optional dir is used for extras files
3297
 */
3298
C_File *CPackages::FindFile(int filetype, CyString filename, CyString dir)
3299
{
3300
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
3301
	{
3302
		C_File *f = node->Data();
3303
		if ( f->GetFileType() != filetype )
3304
			continue;
3305
 
3306
		if ( !f->GetFilename().Compare(filename) )
3307
			continue;
3308
 
3309
		if ( !dir.Empty() && f->GetDir().Compare(dir) )
3310
			continue;
3311
 
3312
		return f;
3313
	}
3314
 
3315
	return NULL;
3316
}
3317
 
3318
CArchiveFile *CPackages::FindArchivePackage(CyString name)
3319
{
3320
	// interate through all packages
3321
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3322
	{
3323
		CBaseFile *file = node->Data();
3324
 
3325
		// only look for archive packages
3326
		if ( file->GetType() != TYPE_ARCHIVE )
3327
			continue;
3328
 
3329
		// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
3330
		if ( file->GetName().Compare(name) )
3331
			return (CArchiveFile *)file;
3332
	}
3333
 
3334
	// nothing found
3335
	return 0;
3336
}
3337
 
3338
/**
3339
 * Find a SPK Package
3340
 *
3341
 * This searching all installed packages for a SPK Package matching the name and author
3342
 */
3343
CBaseFile *CPackages::FindSpkPackage(CyString name, CyString author)
3344
{
3345
	// interate through all packages
3346
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3347
	{
3348
		CBaseFile *file = node->Data();
3349
 
3350
		// only look for spk packages
3351
		if ( file->GetType() != TYPE_SPK )
3352
			continue;
3353
 
3354
		// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
3355
		if ( file->GetName().Compare(name) && file->GetAuthor().Compare(author) )
3356
			return file;
3357
	}
3358
 
3359
	// nothing found
3360
	return 0;
3361
}
3362
 
3363
CBaseFile *CPackages::FindPackage(CyString name, CyString author)
3364
{
3365
	// interate through all packages
3366
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3367
	{
3368
		CBaseFile *file = node->Data();
3369
 
3370
		// now compare the name and author, "Compare" is a non case senseative check, opposed to ==.
3371
		if ( file->GetName().Compare(name) && file->GetAuthor().Compare(author) )
3372
			return file;
3373
	}
3374
 
3375
	// nothing found
3376
	return 0;
3377
}
3378
 
3379
/**
3380
 * Find a XSP Package
3381
 *
3382
 * This searching all installed packages for a XSP Package matching the object id
3383
 */
3384
CBaseFile *CPackages::FindXspPackage(CyString id)
3385
{
3386
	// interate through all packages
3387
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3388
	{
3389
		CBaseFile *file = node->Data();
3390
 
3391
		// only look for spk packages
3392
		if ( file->GetType() != TYPE_XSP )
3393
			continue;
3394
 
3395
		// now compare the id, "Compare" is a non case senseative check, opposed to ==.
3396
		if ( ((CXspFile *)file)->GetShipID().Compare(id) )
3397
			return file;
3398
	}
3399
 
3400
	// nothing found
3401
	return 0;
3402
}
3403
 
3404
/**
3405
 * Update the used files count
3406
 *
3407
 * counts how many packages are currently using the file
3408
 */
3409
void CPackages::UpdateUsedFiles(CLinkList<CBaseFile> *ignoreList, bool includedisabled)
3410
{
3411
	// clear amounts for all files
3412
	CListNode<C_File> *fnode = m_lFiles.Front();
3413
	while ( fnode )
3414
	{
3415
		fnode->Data()->ClearUsed();
3416
		fnode = fnode->next();
3417
	}
3418
 
3419
	// update for all packages
3420
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node= node->next() )
3421
	{
3422
		CBaseFile *file = node->Data();
3423
		if ( (ignoreList) && (ignoreList->FindData(file)) )
3424
			continue;
3425
		if ( !includedisabled && !file->IsEnabled() )
3426
			continue;
3427
		// now mark all files
3428
		CListNode<C_File> *fnode = file->GetFileList()->Front();
3429
		while ( fnode )
3430
		{
3431
			fnode->Data()->IncUsed();
3432
			fnode = fnode->next();
3433
		}
3434
	}
3435
}
3436
 
3437
/**
3438
 * Removes all empty directories that might have been created
3439
 */
3440
void CPackages::RemoveUnusedDirectories(CyStringList &dirs, CyStringList *errors)
3441
{
3442
	CDirIO Dir(m_sCurrentDir);
3443
	for ( SStringList *str = dirs.Head(); str; str = str->next )
3444
	{
3445
		CyString dir = str->str;
3446
 
3447
		CyStringList removedDir;
3448
		if ( Dir.RemoveDir(dir, false, true, &removedDir) || !removedDir.Empty() )
3449
		{
3450
			for ( SStringList *node = removedDir.Head(); node; node = node->next )
3451
			{
3452
				CyString displayName = node->str;
3453
				displayName = displayName.Remove(m_sCurrentDir);
3454
				this->AddLogEntry(SPKINSTALL_REMOVEDIR, displayName, errors);
3455
			}
3456
		}
3457
	}
3458
}
3459
 
3460
/**
3461
 * Update signed status
3462
 *
3463
 * Updates the signed status of all packages
3464
 */
3465
void CPackages::UpdateSigned()
3466
{
3467
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
3468
	{
3469
		CBaseFile *p = node->Data();
3470
 
3471
		if ( p->GetType() != TYPE_SPK )
3472
			continue;
3473
 
3474
		((CSpkFile *)p)->UpdateSigned(false);
3475
	}
3476
}
3477
 
3478
/**
3479
 * Check validity of package file
3480
 *
3481
 * Checks if there is a newer version of the package installed
3482
 */
3483
int CPackages::CheckInstallPackage(CBaseFile *package, int check)
3484
{
3485
	if ( package->GetType() == TYPE_XSP && m_iGame == GAME_X2 )
3486
		return INSTALLCHECK_NOSHIP;
3487
 
3488
	// search for an old version
3489
	CBaseFile *oldPackage = FindPackage(package);
3490
 
3491
	// check versions are newer
3492
	if (oldPackage && (check & IC_OLDVERSION))
3493
	{
3494
		if ( oldPackage->GetVersion().CompareVersion(package->GetVersion()) == COMPARE_OLDER )
3495
			return INSTALLCHECK_OLDVERSION;
3496
	}
3497
 
3498
	// now check for game version
3499
	if ((check & IC_WRONGGAME) || (check & IC_WRONGVERSION))
3500
	{
3501
		if ( package->AnyGameCompatability() )
3502
		{
3503
			if ( (check & IC_WRONGGAME) && (!package->CheckGameCompatability(m_iGame)) )
3504
				return INSTALLCHECK_WRONGGAME;
3505
			else if ( (check & IC_WRONGVERSION) && (!package->CheckGameVersionCompatability(m_iGame, m_sGameVersion, m_iGameVersion)) )
3506
				return INSTALLCHECK_WRONGVERSION;
3507
		}
3508
	}
3509
 
3510
	// check for modified
3511
	if (m_bVanilla && (check & IC_MODIFIED))
3512
	{
3513
		if ( !package->IsSigned() )
3514
			return INSTALLCHECK_MODIFIED;
3515
	}
3516
 
3517
	return INSTALLCHECK_OK;
3518
}
3519
 
3520
bool CPackages::CheckOtherPackage(CBaseFile *package)
3521
{
3522
	// check the need for another mod
3523
	if ( package->GetType() == TYPE_SPK )
3524
	{
3525
		CSpkFile *spk = (CSpkFile *)package;
3526
		if ( spk->IsAnotherMod() )
3527
		{
3528
			if ( !FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()) )
3529
			{
3530
				// check the install list
3531
				if ( m_lInstallList.empty() )
3532
					return false;
3533
 
3534
				bool found = false;
3535
				for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
3536
				{
10 cycrow 3537
					if ( spk->GetOtherName().Compare(node->Data()->GetName().ToString()) && spk->GetOtherAuthor().Compare(node->Data()->GetAuthor().ToString()) )
1 cycrow 3538
					{
3539
						found = true;
3540
						break;
3541
					}
3542
				}
3543
 
3544
				if ( !found )
3545
					return false;
3546
			}
3547
		}
3548
	}
3549
 
3550
	return true;
3551
}
3552
 
3553
bool CPackages::CheckEnabledDependacy(CBaseFile *p)
3554
{
3555
	// check the for another mod
3556
	if ( p->GetType() == TYPE_SPK )
3557
	{
3558
		if ( ((CSpkFile *)p)->IsAnotherMod() )
3559
		{
3560
			CBaseFile *parent = this->FindSpkPackage(((CSpkFile *)p)->GetOtherName(), ((CSpkFile *)p)->GetOtherAuthor());
3561
			if ( parent )
3562
			{
3563
				if ( !parent->IsEnabled() )
3564
					return false;
3565
			}
3566
			else
3567
				return false;
3568
		}
3569
	}
3570
 
3571
	// check any dependacies
3572
	if ( p->AnyDependacies() )
3573
	{
3574
		for ( CListNode<SNeededLibrary> *dNode = p->GetNeededLibraries()->Front(); dNode; dNode = dNode->next() )
3575
		{
3576
			if ( dNode->Data()->sName.Compare("<package>") )
3577
				continue;
3578
			if ( !this->CheckInstalledDependacy(dNode->Data()->sName, dNode->Data()->sAuthor, dNode->Data()->sMinVersion, true, true) )
3579
				return false;
3580
		}
3581
	}
3582
 
3583
	return true;
3584
}
3585
 
3586
bool CPackages::CheckInstalledDependacy(CyString name, CyString author, CyString version, bool onlyEnabled, bool includePrepared)
3587
{
3588
	CBaseFile *p = this->FindSpkPackage(name, author);
3589
	if ( p )
3590
	{
3591
		// now check version
3592
		if ( version.CompareVersion(p->GetVersion()) == COMPARE_OLDER )
3593
			return false;
3594
 
3595
		if ( onlyEnabled && !p->IsEnabled() )
3596
			return false;
3597
 
3598
		return true;
3599
	}
3600
 
3601
	// now check the prepared list
3602
	if ( includePrepared )
3603
	{
3604
		for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
3605
		{
3606
			p = node->Data();
3607
			if ( !p )
3608
				continue;
3609
 
3610
			if ( p->GetName().Compare(name) && p->GetAuthor().Compare(author) )
3611
			{
3612
				if ( version.CompareVersion(p->GetVersion()) == COMPARE_OLDER )
3613
					continue;
3614
 
3615
				if ( onlyEnabled && !p->IsEnabled() )
3616
					continue;
3617
 
3618
				return true;
3619
			}
3620
		}
3621
 
3622
		m_lInstallList.RemoveEmpty();
3623
	}
3624
 
3625
	return false;
3626
}
3627
 
3628
int CPackages::GetMissingDependacies(CBaseFile *p, CyStringList *list, bool onlyEnabled, bool includePrepared)
3629
{
3630
	int count = 0;
3631
	CLinkList<SNeededLibrary> *neededList = p->GetNeededLibraries();
3632
	if ( neededList )
3633
	{
3634
		for ( CListNode<SNeededLibrary> *node = neededList->Front(); node; node = node->next() )
3635
		{
3636
			SNeededLibrary *nl = node->Data();
3637
			if ( !CheckInstalledDependacy((nl->sName.Compare("<package>")) ? p->GetName() : nl->sName, (nl->sAuthor.Compare("<author>")) ? p->GetAuthor() : nl->sAuthor, nl->sMinVersion, (nl->sName.Compare("<package>")) ? false : onlyEnabled, (nl->sName.Compare("<package>")) ? false : includePrepared) )
3638
			{
3639
				if ( list )
3640
					list->PushBack(((nl->sName.Compare("<package>")) ? p->GetName() : nl->sName) + "|" + nl->sMinVersion, (nl->sAuthor.Compare("<author>")) ? p->GetAuthor() : nl->sAuthor);
3641
				++count;
3642
			}
3643
		}
3644
	}
3645
 
3646
	if ( p->GetType() == TYPE_SPK )
3647
	{
3648
		CSpkFile *spk = (CSpkFile *)p;
3649
		if ( spk->IsAnotherMod() )
3650
		{
3651
			bool found = true;
3652
			if ( !this->FindSpkPackage(spk->GetOtherName(), spk->GetOtherAuthor()) )
3653
			{
3654
				if ( includePrepared )
3655
				{
3656
					found = false;
3657
					for ( CListNode<CBaseFile> *pNode = m_lInstallList.Front(); pNode; pNode = pNode->next() )
3658
					{
3659
						CBaseFile *checkP = pNode->Data();
3660
						if ( p->GetAuthor().Compare(checkP->GetAuthor()) && p->GetName().Compare(checkP->GetName()) )
3661
						{
3662
							found = true;
3663
							break;
3664
						}
3665
					}
3666
				}
3667
				else
3668
					found = false;
3669
			}
3670
 
3671
			if ( !found )
3672
			{
3673
				if ( list )
10 cycrow 3674
					list->PushBack(CyString(spk->GetOtherName()), CyString(spk->GetOtherAuthor()));
1 cycrow 3675
				++count;
3676
			}
3677
		}
3678
	}
3679
 
3680
	return count;
3681
}
3682
 
3683
int CPackages::CheckPreparedInstallRequired(CLinkList<CBaseFile> *list)
3684
{
3685
	// loop through all packages
3686
	int count = 0;
3687
	for ( CListNode<CBaseFile> *node = m_lInstallList.Front(); node; node = node->next() )
3688
	{
3689
		CBaseFile *p = node->Data();
3690
 
3691
		// no requirements found, remove from list
3692
		bool missingDep = false;
3693
		if ( !this->CheckOtherPackage(p) )
3694
			missingDep = true;
3695
		else if ( this->GetMissingDependacies(p, NULL, false, true) )
3696
			missingDep = true;
3697
 
3698
		if ( missingDep )
3699
		{
3700
			if ( list )
3701
			{
3702
				node->ChangeData(NULL);
3703
				list->push_back(p);
3704
			}
3705
			else
3706
				node->DeleteData();
3707
			++count;
3708
		}
3709
	}
3710
 
3711
	m_lInstallList.RemoveEmpty();
3712
 
3713
	return count;
3714
}
3715
 
3716
 
3717
/**
3718
 * Remove uninstall file
3719
 *
3720
 * Removes a single uninstall file
3721
 */
3722
bool CPackages::RemoveUninstallFile(C_File *file, CyStringList *errors)
3723
{
3724
	CFileIO fio(file->GetFilePointer());
3725
	if ( fio.Exists() )
3726
	{
3727
		if ( fio.Remove() )
3728
		{
3729
			this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE, file->GetNameDirectory(NULL), errors);
3730
			return true;
3731
		}
3732
		else if ( errors )
3733
			this->AddLogEntry(SPKINSTALL_UNINSTALL_REMOVE_FAIL, file->GetNameDirectory(NULL), errors);
3734
	}
3735
 
3736
	return false;
3737
}
3738
 
3739
/**
3740
 * Remove uninstall scripts
3741
 *
3742
 * Removes any unused unisntall scripts
3743
 * Finds if any scripts are in the current package
3744
 */
3745
int CPackages::RemoveUninstallScripts(CyStringList *errors, CProgressInfo *progress)
3746
{
3747
	if ( m_lUninstallFiles.empty() )
3748
		return 0;
3749
 
3750
	UpdateUsedFiles();
3751
	int files = 0;
3752
 
3753
	for ( CListNode<C_File> *node = m_lUninstallFiles.Back(); node; node = node->prev() )
3754
	{
3755
		if ( progress )
3756
			progress->UpdateProgress(files, m_lUninstallFiles.size());
3757
		files++;
3758
 
3759
		C_File *file = node->Data();
3760
 
3761
		// first check if there is a matching file
3762
		bool found = false;
3763
		for ( CListNode<C_File> *fNode = m_lFiles.Front(); fNode; fNode = fNode->next() )
3764
		{
3765
			C_File *checkFile = fNode->Data();
3766
			if ( checkFile->GetFileType() != FILETYPE_SCRIPT )
3767
				continue;
3768
 
3769
			if ( checkFile->GetFilename().Compare(file->GetFilename()) )
3770
			{
3771
				found = true;
3772
				break;
3773
			}
3774
		}
3775
 
3776
		// not found a matching file, we can safetly remove it
3777
		if ( !found )
3778
		{
3779
			if ( RemoveUninstallFile(file) )
3780
				node->DeleteData();
3781
		}
3782
	}
3783
 
3784
	m_lUninstallFiles.RemoveEmpty();
3785
 
3786
	return files;
3787
}
3788
 
3789
 
3790
/**
3791
 * Remove unused shared file
3792
 *
3793
 * Removes a single file
3794
 */
3795
bool CPackages::RemoveSharedFile(C_File *file, CyStringList *errors)
3796
{
3797
	CFileIO fio(file->GetFilePointer());
3798
	if ( fio.Exists() )
3799
	{
3800
		if ( fio.Remove() )
3801
		{
3802
			this->AddLogEntry(SPKINSTALL_SHARED, file->GetNameDirectory(NULL), errors);
3803
			delete file;
3804
			return true;
3805
		}
3806
		else if ( errors )
3807
			this->AddLogEntry(SPKINSTALL_SHARED_FAIL, file->GetNameDirectory(NULL), errors);
3808
	}
3809
 
3810
	return false;
3811
}
3812
 
3813
/**
3814
 * Remove Unused Shared Files
3815
 *
3816
 * Files that have been marked as shared will not be removed or disabled when the last package is removed
3817
 * This function will remove any that are no longer connected with packages
3818
 *
3819
 * Marked shared fiels are mainly used for library scripts that arn't always added to packages
3820
 */
3821
int CPackages::RemoveUnusedSharedFiles(CyStringList *errors, CProgressInfo *progress)
3822
{
3823
	UpdateUsedFiles();
3824
	int files = 0;
3825
	int done = 0;
3826
 
3827
	for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
3828
	{
3829
		if ( progress )
3830
			progress->UpdateProgress(files, m_lFiles.size());
3831
		files++;
3832
 
3833
		C_File *file = node->Data();
3834
 
3835
		// only do marked shared files
3836
		if ( !file->IsShared() )
3837
			continue;
3838
 
3839
		// only do ones that are no longer needed
3840
		if ( file->GetUsed() )
3841
			continue;
3842
 
3843
		if ( RemoveSharedFile(file, errors) )
3844
			++done;
3845
		node->ChangeData(NULL);
3846
	}
3847
 
3848
	m_lFiles.RemoveEmpty();
3849
 
3850
	return done;
3851
}
3852
 
3853
/**
3854
 * Any Unused Shared
3855
 *
3856
 * Checks if theres any unused shared files available
3857
 *
3858
 * Any file thats marked as shared, and is no longer connected to any installed package
3859
 */
3860
bool CPackages::AnyUnusedShared()
3861
{
3862
	UpdateUsedFiles();
3863
 
3864
	for ( CListNode<C_File> *node = m_lFiles.Back(); node; node = node->prev() )
3865
	{
3866
		C_File *file = node->Data();
3867
 
3868
		// only do marked shared files
3869
		if ( !file->IsShared() )
3870
			continue;
3871
 
3872
		// only do ones that are no longer needed
3873
		if ( file->GetUsed() )
3874
			continue;
3875
 
3876
		return true;
3877
	}
3878
 
3879
	return false;
3880
 
3881
}
3882
 
3883
/**
3884
 * Update original files list
3885
 *
3886
 * Scan the current directory for any fiels that are already there, ie ones that have not been installed
3887
 * Original files should be all the scripts/sounds, etc that are in an unmodified game directory
3888
 *
3889
 * Save list of files and check if any packages overright these files
3890
 * Any file that gets overrighten from this list will automatically be backed up, and restored once the packages are removed
3891
 */
3892
void CPackages::StoreOriginalFiles(int filetype, CyString searchPath)
3893
{
3894
	CyString ext = "pck";
3895
	switch ( filetype )
3896
	{
3897
		case FILETYPE_SOUND:
3898
			ext = "mp3";
3899
			break;
3900
	}
3901
 
3902
	CDirIO Dir(m_sCurrentDir + "/" + searchPath);
3903
 
3904
	CyStringList *files = Dir.DirList();
3905
	if ( !files )
3906
		return;
3907
 
3908
	for ( SStringList *node = files->Head(); node; node = node->next )
3909
	{
3910
		CFileIO File(Dir.File(node->str));
3911
 
3912
		if ( File.GetFileExtension().Compare(ext) )
3913
			AddOriginalFile(filetype, File.GetFilename(), searchPath);
3914
	}
3915
 
3916
	delete files;
3917
}
3918
 
3919
/**
3920
 * Adds a file onto the original files list
3921
 *
3922
 * Checks if it already exists so we dont create multiples
3923
 */
3924
void CPackages::AddOriginalFile(int filetype, CyString filename, CyString searchPath)
3925
{
3926
	// dont add plugin manager as original files
3927
	if ( filetype == FILETYPE_SCRIPT && filename.IsIn("!init.pmanager") )
3928
		return;
3929
	// first check if a matching one exists
3930
	for ( CListNode<C_File> *node = m_lOriginalFiles.Front(); node; node = node->next() )
3931
	{
3932
		C_File *file = node->Data();
3933
		if ( file->GetFileType() != filetype )
3934
			continue;
3935
 
3936
		if ( file->GetFilename().Compare(filename) )
3937
			return;
3938
	}
3939
 
3940
	// check that no packages are currently using them either, not original if being used already
3941
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
3942
	{
3943
		C_File *file = node->Data();
3944
		if ( file->GetFileType() != filetype )
3945
			continue;
3946
 
3947
		if ( file->GetFilename().Compare(filename) )
3948
			return;
3949
	}
3950
 
3951
	C_File *of = new C_File(filename);
3952
	of->SetFileType(filetype);
3953
	if ( filetype == FILETYPE_EXTRA )
3954
		of->SetDir(searchPath);
3955
	of->SetFilename(m_sCurrentDir + "/" + of->GetNameDirectory(NULL));
3956
 
3957
	m_lOriginalFiles.push_back(of);
3958
}
3959
 
3960
/**
3961
 * Check for original file
3962
 *
3963
 * Checks if the file is an original file or not
3964
 */
3965
bool CPackages::IsOriginalFile(C_File *file)
3966
{
3967
	if ( GetOriginalFile(file) )
3968
		return true;
3969
	return false;
3970
}
3971
 
3972
/**
3973
 * Get original file
3974
 *
3975
 * Finds a matching original file entry and returns it
3976
 */
3977
C_File *CPackages::GetOriginalFile(C_File *file)
3978
{
3979
	if ( !file )
3980
		return NULL;
3981
 
3982
	for ( CListNode<C_File> *node = m_lOriginalFiles.Front(); node; node = node->next() )
3983
	{
3984
		C_File *of = node->Data();
3985
 
3986
		// not of the same file type
3987
		if ( of->GetFileType() != file->GetFileType() )
3988
			continue;
3989
 
3990
		// same file name, must be the same file
3991
		if ( of->GetFilename().Compare(file->GetFilename()) )
3992
			return of;
3993
	}
3994
 
3995
	return NULL;
3996
}
3997
 
3998
/**
3999
 * Backs up an original file
4000
 *
4001
 * Moves the file into the PluginManager/Original directory so packages dont replace them
4002
 * Allows these files to be restored when the package is remove to prevent breaking the game
4003
 */
4004
bool CPackages::BackupOriginalFile(C_File *f, CyStringList *errors)
4005
{
4006
	CyString newDir = CyString("PluginManager/Original/") + f->GetDirectory(NULL);
4007
	CDirIO oDir(m_sCurrentDir);
4008
	if ( oDir.Exists(newDir + "/" + f->GetFilename()) )
4009
		return true;
4010
 
4011
	// make sure the directory exissts
4012
	if ( !oDir.Exists(newDir) )
4013
	{
4014
		if ( !oDir.Create(newDir) )
4015
		{
4016
			this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY_FAIL, newDir, errors);
4017
			return false;
4018
		}
4019
 
4020
		this->AddLogEntry(SPKINSTALL_CREATEDIRECTORY, newDir, errors);
4021
	}
4022
 
4023
	// now lets copy the file
4024
	CFileIO CopyFile(f->GetFilePointer());
4025
	if ( CopyFile.Copy(oDir.File(newDir + "/" + f->GetFilename())) )
4026
	{
4027
		this->AddLogEntry(SPKINSTALL_ORIGINAL_BACKUP, f->GetNameDirectory(NULL), errors);
4028
		return true;
4029
	}
4030
	else
4031
	{
4032
		this->AddLogEntry(SPKINSTALL_ORIGINAL_BACKUP_FAIL, f->GetNameDirectory(NULL), errors);
4033
		return false;
4034
	}
4035
}
4036
 
4037
void CPackages::ShuffleTextFiles(CyStringList *errors)
4038
{
4039
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
4040
	{
4041
		C_File *f = node->Data();
4042
		// only do files that are enabled
4043
		if ( f->IsDisabled() )
4044
			continue;
4045
 
4046
		if ( f->GetFileType() != FILETYPE_TEXT )
4047
			continue;
4048
 
4049
		// check if the file is an auto text file
4050
		if ( !f->IsAutoTextFile() )
4051
			continue;
4052
 
4053
		// we need to rename it
4054
		int current = FindNextTextFile();
4055
		if ( current < f->GetTextFileID() )
4056
		{
4057
			CFileIO moveFile(f->GetFilePointer());
4058
 
4059
			CyString newName = SPK::FormatTextName(current, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT)) + "." + moveFile.GetFileExtension();
4060
			if ( moveFile.Rename(m_sCurrentDir + "/t/" + newName) )
4061
			{
4062
				this->AddLogEntry(SPKINSTALL_AUTOTEXT, f->GetName() + "~" + newName, errors);
4063
				f->SetName(newName);
4064
			}
4065
			else
4066
				this->AddLogEntry(SPKINSTALL_AUTOTEXT_FAIL, f->GetName() + "~" + newName, errors);
4067
		}
4068
	}
4069
}
4070
 
4071
/**
4072
 * Shuffle Fake Patches
4073
 *
4074
 * Rename the fake patches so they are always in sequence to be loaded into the game
4075
 *
4076
 * IE, when one is removed, the rest need to be shuffled down so theres no gaps
4077
 */
4078
void CPackages::ShuffleFakePatches(CyStringList *errors)
4079
{
4080
	int check = FindNextFakePatch();
4081
 
4082
	// lets make sure our order is correct
4083
	// find Lowest Fake Patch Installed
4084
	CLinkList<C_File> doneList;
4085
	int lowest = FindLowestFakePatchInstalled();
4086
	if ( check < lowest ) lowest = check; // gap at the beginning, lets use that
4087
 
4088
	// lets define the order
4089
	CyStringList fakePatchOrder;
4090
	for ( SStringList *str = m_lFakePatchOrder.Head(); str; str = str->next )
4091
		fakePatchOrder.PushBack(str->str, str->data);
4092
 
4093
	// add any other packages that need to be ordered
4094
	// we need to work out the correct order
4095
	CLinkList<CBaseFile> packages;
4096
	for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
4097
	{
4098
		CBaseFile *p = pNode->Data();
4099
		// search for any fake patches in package
4100
		if ( !p->IsEnabled() ) continue;
4101
		if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
4102
 
4103
		// must have an order define
4104
		if ( !p->AnyFakePatchOrder() ) continue;
4105
 
4106
		// check if theres any already ordered (manual)
4107
		bool added = false;
4108
		for ( SStringList *sCheck = fakePatchOrder.Head(); sCheck; sCheck = sCheck->next )
4109
		{
4110
			if ( sCheck->str.Compare(p->GetName()) && sCheck->data.Compare(p->GetAuthor()) )
4111
			{
4112
				added = true;
4113
				break;
4114
			}
4115
		}
4116
 
4117
		if ( added ) continue;
4118
 
4119
		bool anyFound = false;
4120
		for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
4121
		{
4122
			if ( !file->IsFakePatch() ) continue;
4123
			if ( !file->CheckFileExt("cat") ) continue;
4124
			if ( doneList.FindData(file) ) 	continue;
4125
			anyFound = true;
4126
			break;
4127
		}
4128
 
4129
		// we have some fake patches that need to be shuffled
4130
		if ( anyFound )
4131
			packages.push_back(p);
4132
	}
4133
 
4134
	// lets adjust the order (only if theres more than 1
4135
	if ( packages.size() > 1 )
4136
	{
4137
		CLinkList<CBaseFile> sortedPackages;
4138
 
4139
		// first add all the packages that dont need to be installed after
4140
		for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
4141
		{
4142
			CBaseFile *p = pNode->Data();
4143
			// we have a before and not after
4144
			if ( !p->GetFakePatchBeforeOrder().Empty() && p->GetFakePatchAfterOrder().Empty() )
4145
			{
4146
				// if we have to install before, check if any on the list
4147
				int earliestPos = -1;
4148
				bool notAdded = true;
4149
				for ( SStringList *str = p->GetFakePatchBeforeOrder().Head(); str; str = str->next )
4150
				{
4151
					int pos = 0;
4152
					for ( CListNode<CBaseFile> *sNode = sortedPackages.Front(); sNode; sNode = sNode->next() )
4153
					{
4154
						if ( str->str.Compare(sNode->Data()->GetName()) && str->data.Compare(sNode->Data()->GetAuthor()) )
4155
						{
4156
							if ( earliestPos == -1 || pos < earliestPos )
4157
								earliestPos = pos;
4158
							break;
4159
						}
4160
						++pos;
4161
					}					
4162
				}
4163
 
4164
				if ( earliestPos > -1 )
4165
					sortedPackages.insert(earliestPos, p);
4166
				// otherwise just add it at the back
4167
				else
4168
					sortedPackages.push_back(p);
4169
 
4170
				// remove from the list
4171
				pNode->ChangeData(NULL);
4172
			}
4173
		}
4174
 
4175
		// now do the packages that have both before and after
4176
		packages.RemoveEmpty();
4177
		for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
4178
		{
4179
			CBaseFile *p = pNode->Data();
4180
			if ( !p->GetFakePatchBeforeOrder().Empty() && !p->GetFakePatchAfterOrder().Empty() )
4181
			{
4182
			}
4183
		}
4184
 
4185
		// add them onto the list
4186
		for ( CListNode<CBaseFile> *pNode = sortedPackages.Front(); pNode; pNode = pNode->next() )
4187
			fakePatchOrder.PushBack(pNode->Data()->GetName(), pNode->Data()->GetAuthor());
4188
	}
4189
 
4190
	// now add to do list
4191
	for ( CListNode<CBaseFile> *pNode = packages.Front(); pNode; pNode = pNode->next() )
4192
		fakePatchOrder.PushBack(pNode->Data()->GetName(), pNode->Data()->GetAuthor());
4193
 
4194
	for ( SStringList *str = fakePatchOrder.Head(); str; str = str->next )
4195
	{
4196
		CBaseFile *package = FindPackage(str->str, str->data);
4197
		if ( package )
4198
		{
4199
			// might have more than 1 fake patch for the file, so we'll need the lowest
4200
			while ( true )
4201
			{
4202
				C_File *lowestFile = NULL;
4203
				for ( C_File *file = package->GetFirstFile(FILETYPE_MOD); file; file = package->GetNextFile(file) )
4204
				{
4205
					if ( !file->IsFakePatch() ) continue;
4206
					if ( !file->CheckFileExt("cat") ) continue; // only do the cat file, we can shuffle the dat file to match later
4207
					if ( doneList.FindData(file) ) continue; // already done?
4208
 
4209
					if ( !lowestFile )
4210
						lowestFile = file;
4211
					else
4212
					{
4213
						if ( file->GetBaseName().ToInt() < lowestFile->GetBaseName().ToInt() )
4214
							lowestFile = file;
4215
					}
4216
				}
4217
 
4218
				if ( !lowestFile ) // no more files ?
4219
					break;
4220
 
4221
				// check its filename, it might already be in the correct place
4222
				if ( lowestFile->GetBaseName().ToInt() != lowest )
4223
				{
4224
					// if the file already exists, we need to move it elsewhere
4225
					CyString nextName = CyString::Number(lowest).PadNumber(2);
4226
					if ( CFileIO(m_sCurrentDir + "/" + nextName + ".cat").Exists() )
4227
					{
4228
						// find the file in our internal file list
4229
						C_File *moveFile = FindFile(FILETYPE_MOD, nextName + ".cat");
4230
						if ( !moveFile ) // must not have it in our list ? lets move to the next
4231
						{
4232
							lowest++;
4233
							continue;
4234
						}
4235
 
4236
						// now we can move the the cat/dat file elsewhere, lets shuffle it to the highest free number
4237
						ShufflePatchTo(moveFile, FindLastFakePatch(), errors);
4238
					}
4239
 
4240
					// space should be free, now lets shuffle it
4241
					ShufflePatchTo(lowestFile, lowest, errors);
4242
				}
4243
 
4244
				doneList.push_back(lowestFile); // we've done this file now
4245
				lowest++; // move up the lowest ready for the next patch
4246
			}
4247
		}
4248
	}
4249
 
4250
	// now lets shuffle the rest
4251
	// now find any packages with greater fake patchs and fill the gaps
4252
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
4253
	{
4254
		C_File *f = node->Data();
4255
		// already done?
4256
		if ( doneList.FindData(f) ) 
4257
			continue;
4258
 
4259
		// only do files that are enabled
4260
		if ( f->IsDisabled() )
4261
			continue;
4262
 
4263
		// check if the file is a fake patch
4264
		if ( !f->IsFakePatch() )
4265
			continue;
4266
 
4267
		// we only want cat and dat files, all fake patchs should be, but incase theres an error in the package somewhere we can check
4268
		if ( !f->CheckFileExt("cat") )
4269
			continue;
4270
 
4271
		// now lets check if its greater than our gap
4272
		int check = FindNextFakePatch();
4273
		int patchNum = f->GetFilename().GetToken(".", 1, 1).ToInt();
4274
		if ( patchNum <= check )
4275
			continue;
4276
 
4277
		ShufflePatchTo(f, check, errors);
4278
	}
4279
}
4280
 
4281
void CPackages::ShufflePatchTo(C_File *file, int to, CyStringList *errors)
4282
{
4283
	// it is, we need to shift this to fill the gap
4284
	CyString newName = CyString::Number(to).PadNumber(2) + "." + file->GetFileExt();
4285
 
4286
	// now rename the file
4287
	CFileIO moveFile(file->GetFilePointer());
4288
	if ( moveFile.Rename(m_sCurrentDir + "/" + newName) )
4289
	{
4290
		// display moveing
4291
		this->AddLogEntry(SPKINSTALL_FAKEPATCH, file->GetName() + "~" + newName, errors);
4292
 
4293
		// now find the matching pairing if it exists
4294
		for ( CListNode<C_File> *node2 = m_lFiles.Front(); node2; node2 = node2->next() )
4295
		{
4296
			C_File *f2 = node2->Data();
4297
 
4298
			if ( f2->IsDisabled() || !f2->IsFakePatch() || f2->CheckFileExt(file->GetFileExt()) )
4299
				continue;
4300
 
4301
			// needs to be the same file
4302
			if ( f2->GetBaseName() != file->GetBaseName() )
4303
				continue;
4304
 
4305
			CyString newName2 = CyString::Number(to).PadNumber(2) + "." + f2->GetFileExt();
4306
			CFileIO moveFile(f2->GetFilePointer());
4307
			if ( moveFile.Rename(m_sCurrentDir + "/" + newName2) )
4308
			{
4309
				this->AddLogEntry(SPKINSTALL_FAKEPATCH, f2->GetName() + "~" + newName2, errors);
4310
				f2->SetName(newName2);
4311
			}
4312
			else
4313
				this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, f2->GetName() + "~" + newName2, errors);
4314
		}
4315
 
4316
		// finally make sure the internal name matches the new one
4317
		file->SetName(newName);
4318
	}
4319
	else
4320
		this->AddLogEntry(SPKINSTALL_FAKEPATCH_FAIL, file->GetName() + "~" + newName, errors);
4321
}
4322
 
4323
int CPackages::FindLowestFakePatchInstalled()
4324
{
4325
	int lowest = 99;
4326
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
4327
	{
4328
		CBaseFile *p = node->Data();
4329
		if ( !p->IsEnabled() ) continue;
4330
		if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
4331
 
4332
		// now get all fake patches
4333
		for ( C_File *file = p->GetFirstFile(FILETYPE_MOD); file; file = p->GetNextFile(file) )
4334
		{
4335
			if ( !file->IsFakePatch() ) continue;
4336
			if ( file->CheckFileExt("dat") ) continue;
4337
			int check = file->GetBaseName().ToInt();
4338
			if ( check < lowest )
4339
				lowest = check;
4340
		}
4341
	}
4342
 
4343
	return lowest;
4344
}
4345
 
4346
int CPackages::FindLastFakePatch(int start, CyString dir)
4347
{
4348
	CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
4349
 
4350
	int check = start;
4351
	while ( check > 0 )
4352
	{
4353
		CyString checkStr = CyString::Number(check).PadNumber(2);
4354
 
4355
		// check if a cat file exists
4356
		if ( !Dir.Exists(checkStr + ".cat") )
4357
		{
4358
			// it doen't, check if theres a dat file (incase of package error)
4359
			if ( !Dir.Exists(checkStr + ".dat") )
4360
				break;
4361
		}
4362
		--check;
4363
	}
4364
 
4365
	return check;
4366
}
4367
 
4368
/**
4369
 * Find next fake patch
4370
 *
4371
 * Searching for the next gap in patches, starting with 01.cat to 99.cat
4372
 */
4373
int CPackages::FindNextFakePatch(int start, CyString dir)
4374
{
4375
	CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
4376
 
4377
	int check = start;
4378
	while ( check < 99 )
4379
	{
4380
		++check;
4381
		CyString checkStr = CyString::Number(check).PadNumber(2);
4382
 
4383
		// check if a cat file exists
4384
		if ( !Dir.Exists(checkStr + ".cat") )
4385
		{
4386
			// it doen't, check if theres a dat file (incase of package error)
4387
			if ( !Dir.Exists(checkStr + ".dat") )
4388
				break;
4389
		}
4390
	}
4391
 
4392
	return check;
4393
}
4394
 
4395
/**
4396
 * Find next text file
4397
 *
4398
 * Searching for the next gap in automatic text files, start with 0003-LXXX or XX0003
4399
 */
4400
int CPackages::FindNextTextFile(int start, CyString dir)
4401
{
4402
	CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
4403
	Dir.cd("t");
4404
 
4405
	int check = start;
4406
	if ( check < 2 ) check = 2;
4407
	while ( check < 9999 )
4408
	{
4409
		++check;
4410
		CyString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
4411
 
4412
		// check if a packed file exists
4413
		if ( !Dir.Exists(newFilename + ".pck") )
4414
		{
4415
			// it doen't, check if theres an unpacked file
4416
			if ( !Dir.Exists(newFilename + ".xml") )
4417
				break;
4418
		}
4419
	}
4420
 
4421
	return check;
4422
}
4423
 
4424
int CPackages::FindLastTextFile(int start, CyString dir)
4425
{
4426
	CDirIO Dir((dir.Empty()) ? m_sCurrentDir : dir);
4427
	Dir.cd("t");
4428
 
4429
	int check = start;
4430
	while ( check < 9999 )
4431
	{
4432
		++check;
4433
		CyString newFilename = SPK::FormatTextName(check, m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
4434
 
4435
		// check if a packed file exists
4436
		if ( !Dir.Exists(newFilename + ".pck") )
4437
		{
4438
			// it doen't, check if theres an unpacked file
4439
			if ( !Dir.Exists(newFilename + ".xml") )
4440
				break;
4441
		}
4442
	}
4443
 
4444
	return check;
4445
}
4446
 
4447
/**
4448
 * Read game language
4449
 *
4450
 * Reads the lang.dat file from the game directory for the language id
4451
 */
4452
void CPackages::ReadGameLanguage(bool force)
4453
{
4454
	// check if lauguage is already set
4455
	if ( !force && m_iLanguage )
4456
		return;
4457
 
4458
	CDirIO Dir(m_sCurrentDir);
4459
 
4460
	// check for lang.dat file
4461
	if ( Dir.Exists("lang.dat") )
4462
	{
4463
		CFileIO File(Dir.File("lang.dat"));
4464
 
4465
		size_t size;
4466
		char *data = File.ReadToData(&size);
4467
 
4468
		if ( data )
4469
		{
4470
			CyString str(data);
4471
			m_iLanguage = str.GetToken("\n", 1, 1).GetToken(" ", 1, 1).ToInt();
4472
		}
4473
	}
4474
}
4475
 
4476
/**
4477
 * Create Language Text File
4478
 *
4479
 * Creates text files for all packages into the correct language
4480
 */
4481
void CPackages::CreateLanguageTextFiles(CyStringList *errors)
4482
{
4483
	// no need to create them if theres no language to use
4484
	if ( !m_iLanguage )
4485
		return;
4486
 
4487
	// find all text files
4488
	CyStringList ids;
4489
 
4490
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
4491
	{
4492
		C_File *f = node->Data();
4493
		// only do text fiels
4494
		if ( f->GetFileType() != FILETYPE_TEXT )
4495
			continue;
4496
 
4497
		CyString id, lang;
4498
		if ( m_iGameFlags & EXEFLAG_TCTEXT )
4499
		{
4500
			id = f->GetBaseName().GetToken("-", 1, 1);
4501
			lang = f->GetBaseName().GetToken("-", 2, 2);
4502
			if ( lang.Empty() )
4503
				lang = "NULL";
4504
			else
4505
				lang = lang.Erase(0, 1);  // remove the "L"
4506
		}
4507
		else
4508
		{
4509
			lang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
4510
			id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
4511
		}
4512
 
4513
		SStringList *strList = ids.FindString(id);
4514
 
4515
		// not added, add a new one
4516
		if ( !strList )
4517
			strList = ids.PushBack(id, lang);
4518
		else
4519
		{
4520
			if ( strList->data.Empty() )
4521
				strList->data = lang;
4522
			else
4523
			{
4524
				strList->data += ":";
4525
				strList->data += lang;
4526
			}
4527
		}
4528
	}
4529
 
4530
	// we should now have a list of all text files, we need to remove those that have a matching language
4531
	for ( SStringList *sNode = ids.Head(); sNode; sNode = sNode->next )
4532
	{
4533
		int size = 0;
4534
		CyString *data = sNode->data.SplitToken(':', &size);
4535
 
4536
		// huh ? we shouldn't have this
4537
		if ( !size )
4538
			continue;
4539
 
4540
		// lets search for a matching id
4541
		int useId = 0;
4542
		bool found = false;
4543
		for ( int i = 0; i < size; i++ )
4544
		{
4545
			if ( data[i] == "NULL" )
4546
				useId = -1;
4547
			else
4548
			{
4549
				int num = data[i].ToInt();
4550
				if ( num == m_iLanguage )
4551
				{
4552
					found = true;
4553
					useId = m_iLanguage;
4554
					break;
4555
				}
4556
				// prioities the text file to use, no language first, then 44, then 49, otherwise, we pick the first available
4557
				else if ( num == 44 && useId != -1)
4558
					useId = 44;
4559
				// if we have a german language, and its not yet found english or null
4560
				else if ( num == 49 && useId != 44 && useId != -1 )
4561
					useId = 49;
4562
				// if we have not found a valid language yet, we will use this one
4563
				else if ( !useId )
4564
					useId = num;
4565
			}
4566
		}
4567
 
4568
		if ( found )
4569
			continue;
4570
 
4571
		if ( !useId )
4572
			useId = data[0].ToInt();
4573
		if ( !useId )
4574
			useId = -1;
4575
 
4576
		CLEANSPLIT(data, size);
4577
 
4578
		RenameTextFile(sNode->str, useId, errors);
4579
	}
4580
}
4581
 
4582
/**
4583
 * Rename a text file
4584
 *
4585
 * Creates a new text file and copies an existing one
4586
 */
4587
bool CPackages::RenameTextFile(CyString textid, int languageid, CyStringList *errors)
4588
{
4589
	// lets check if the file already exists
4590
	CyString newFilename = SPK::FormatTextName(textid.ToInt(), m_iLanguage, (m_iGameFlags & EXEFLAG_TCTEXT));
4591
	C_File *addFile = FindFile(FILETYPE_TEXT, newFilename + ".xml");
4592
	if ( !addFile )
4593
		addFile = FindFile(FILETYPE_TEXT, newFilename + ".pck");
4594
 
4595
	// we have found the file, lets just add it to our scripts
4596
	if ( addFile )
4597
	{
4598
		AddTextFileToScripts(addFile, textid);
4599
		return true;
4600
	}
4601
 
4602
	// first we need to find our text file
4603
	CyString filename;
4604
	if ( languageid != -1 )
4605
		filename = SPK::FormatTextName(textid.ToInt(), languageid, (m_iGameFlags & EXEFLAG_TCTEXT));
4606
	else
4607
		filename = textid;
4608
 
4609
	C_File *textFile = FindFile(FILETYPE_TEXT, filename + ".xml");
4610
	if ( !textFile )
4611
	{
4612
		textFile = FindFile(FILETYPE_TEXT, filename + ".pck");
4613
		if ( !textFile )
4614
			return false;
4615
	}
4616
 
4617
	// now lets create out new text file
4618
	CFileIO readFile(textFile->GetFilePointer());
4619
	std::vector<CyString> *lines = readFile.ReadLines();
4620
 
4621
	// find the language id in the lines
4622
	std::vector<CyString> frontLines;
4623
	for(std::vector<CyString>::iterator it = lines->begin(); it != lines->end(); ++it)
4624
	{
4625
		CyString line = *it;
4626
		int pos = line.FindPos("<language id");
4627
		if ( pos != -1)
4628
		{
4629
			CyString newLine = "<language id=\"";
4630
			newLine += CyString::Number(m_iLanguage);
4631
			newLine += "\">";
4632
			newLine += line.GetToken(">", 2);
4633
 
4634
			frontLines.insert(frontLines.begin(), newLine);
4635
 
4636
			lines->erase(lines->begin(), ++it);
4637
			break;
4638
		}
4639
		frontLines.insert(frontLines.begin(), line);
4640
	}
4641
 
4642
	for(std::vector<CyString>::iterator it = frontLines.begin(); it != frontLines.end(); ++it)
4643
		lines->insert(lines->begin(), *it);
4644
 
4645
	addFile = new C_File(newFilename + ".xml");
4646
	addFile->SetFileType(FILETYPE_TEXT);
4647
 
4648
	CFileIO writeFile(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
4649
	if ( writeFile.WriteFile(lines) )
4650
	{
4651
		this->AddLogEntry(SPKINSTALL_WRITEFILE, addFile->GetNameDirectory(NULL), errors);
4652
 
4653
		addFile->SetFilename(m_sCurrentDir + "/" + addFile->GetNameDirectory(NULL));
4654
		// now we have the file wrriten, we need to add it to all scripts that need it
4655
		// first we add it to the global list
4656
		m_lFiles.push_back(addFile);
4657
 
4658
		// now add it to the scripts
4659
		AddTextFileToScripts(addFile, textid);
4660
	}
4661
	else
4662
	{
4663
		this->AddLogEntry(SPKINSTALL_WRITEFILE_FAIL, addFile->GetNameDirectory(NULL), errors);
4664
		if ( lines )
4665
			delete lines;
4666
		return false;
4667
	}
4668
 
4669
	if ( lines )
4670
		delete lines;
4671
 
4672
 
4673
	return true;
4674
}
4675
 
4676
/**
4677
 * Add file to scripts
4678
 *
4679
 * Adds a file to all scripts that need it
4680
 */
4681
void CPackages::AddTextFileToScripts(C_File *file, CyString textid)
4682
{
4683
	// now we need to find all scripts that have a matching file and add this to the list
4684
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
4685
	{
4686
		CBaseFile *package = node->Data();
4687
 
4688
		// first make sure it doesn't already exist
4689
		if ( package->GetFileList()->FindData(file) )
4690
			continue;
4691
 
4692
		bool found = false;
4693
		for ( CListNode<C_File> *fNode = package->GetFileList()->Front(); fNode; fNode = fNode->next() )
4694
		{
4695
			C_File *tFile = fNode->Data();
4696
			if ( tFile->GetFileType() != FILETYPE_TEXT )
4697
				continue;
4698
 
4699
			CyString id = tFile->GetBaseName().GetToken("-", 1, 1);
4700
			if ( id == textid )
4701
			{
4702
				found = true;
4703
				break;
4704
			}
4705
		}
4706
 
4707
		if ( found )
4708
			package->GetFileList()->push_back(file);
4709
	}
4710
}
4711
 
4712
bool CPackages::RemoveFile(C_File *file, CyStringList *errors)
4713
{
4714
	if ( !file )
4715
		return true;
4716
 
4717
	CyString remFileStr = file->GetFilePointer();
4718
	remFileStr.FindReplace(m_sCurrentDir, "");
4719
 
4720
	if ( file->GetFilePointer().IsIn("::") ) {
4721
		CFileIO CatFile(file->GetFilePointer().GetToken("::", 1, 1));
4722
		if ( CatFile.Exists() ) {
4723
			CCatFile cat;
4724
			if ( cat.Open(CatFile.GetFullFilename(), this->GetAddonDir(), CATREAD_DAT, false) == CATERR_NONE ) {
4725
				CyString fileName = file->GetFilePointer().GetToken("::", 2, 2);
4726
				if ( cat.FindData(fileName) ) {
4727
					if ( cat.RemoveFile(fileName) ) {
4728
						this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
4729
					}
4730
					else {
4731
						this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
4732
						return false;
4733
					}
4734
				}
4735
			}
4736
		}
4737
	}
4738
	else {
4739
		CFileIO f(file->GetFilePointer());
4740
		if ( f.Exists() )
4741
		{
4742
			if ( f.Remove() )
4743
				this->AddLogEntry(SPKINSTALL_DELETEFILE, remFileStr, errors);
4744
			else if ( errors )
4745
			{
4746
				this->AddLogEntry(SPKINSTALL_DELETEFILE_FAIL, remFileStr, errors);
4747
				return false;
4748
			}
4749
			else
4750
				return false;
4751
		}
4752
	}
4753
 
4754
	return true;
4755
}
4756
 
4757
int CPackages::RemoveAllPackages(CyStringList *errors, CProgressInfo *progress)
4758
{
4759
	int files = 0;
4760
 
4761
	// remove all files
4762
	int max = m_lFiles.size() + m_lOriginalFiles.size() + m_lUninstallFiles.size();
4763
	for ( CListNode<C_File> *node = m_lFiles.Front(); node; node = node->next() )
4764
	{
4765
		// update the progress
4766
		if ( progress )
4767
			progress->UpdateProgress(files, max);
4768
		++files;
4769
 
4770
		RemoveFile(node->Data());
4771
		delete node->Data();
4772
	}
4773
	m_lFiles.clear();
4774
 
4775
	// restore any original files that are backed up
4776
	for ( CListNode<C_File> *oNode = m_lOriginalFiles.Front(); oNode; oNode = oNode->next() )
4777
	{
4778
		// update the progress
4779
		if ( progress )
4780
			progress->UpdateProgress(files, max);
4781
		++files;
4782
 
4783
		C_File *f = oNode->Data();
4784
		CFileIO of(m_sCurrentDir + "/PluginManager/Original/" + f->GetNameDirectory(NULL));
4785
		if ( of.Exists() )
4786
			of.Rename(m_sCurrentDir + "/" + f->GetNameDirectory(NULL));
4787
 
4788
		//RemoveFile(oNode->Data());
4789
		delete oNode->Data();
4790
	}
4791
	m_lOriginalFiles.clear();
4792
 
4793
	// remove any uninstall files that remain
4794
	for ( CListNode<C_File> *uNode = m_lUninstallFiles.Front(); uNode; uNode = uNode->next() )
4795
	{
4796
		// update the progress
4797
		if ( progress )
4798
			progress->UpdateProgress(files, max);
4799
		++files;
4800
 
4801
		RemoveFile(uNode->Data());
4802
		delete uNode->Data();
4803
	}
4804
	m_lUninstallFiles.clear();
4805
 
4806
	// delete all packages
4807
	for ( CListNode<CBaseFile> *pNode = m_lPackages.Front(); pNode; pNode = pNode->next() )
4808
	{
4809
		// clear file list as we have removed them already
4810
		pNode->Data()->GetFileList()->clear();
4811
		// delete the memory for the package
4812
		delete pNode->Data();
4813
	}
4814
	m_lPackages.clear();
4815
 
4816
	return files;
4817
}
4818
 
4819
/**
4820
 * Get Install Before Text
4821
 *
4822
 * Returns the install before text for the package in the correct language
4823
 */
4824
CyString CPackages::GetInstallBeforeText(CBaseFile *package)
4825
{
4826
	if ( !package->IsThereInstallText() )
4827
		return NullString;
4828
 
4829
	return package->GetInstallBeforeText(m_iLanguage);
4830
}
4831
CyString CPackages::GetInstallAfterText(CBaseFile *package)
4832
{
4833
	if ( !package->IsThereInstallText() )
4834
		return NullString;
4835
 
4836
	return package->GetInstallAfterText(m_iLanguage);
4837
}
4838
CyString CPackages::GetUninstallBeforeText(CBaseFile *package)
4839
{
4840
	if ( !package->IsThereUninstallText() )
4841
		return NullString;
4842
 
4843
	return package->GetUninstallBeforeText(m_iLanguage);
4844
}
4845
CyString CPackages::GetUninstallAfterText(CBaseFile *package)
4846
{
4847
	if ( !package->IsThereUninstallText() )
4848
		return NullString;
4849
 
4850
	return package->GetUninstallAfterText(m_iLanguage);
4851
}
4852
 
4853
int CPackages::GetChildPackages(CBaseFile *package, CLinkList<CBaseFile> *children, bool recursive)
4854
{
4855
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
4856
	{
4857
		CBaseFile *p = node->Data();
4858
		if ( p->GetParent() == package )
4859
		{
4860
			children->push_back(p);
4861
 
4862
			if ( recursive )
4863
				this->GetChildPackages(p, children, recursive);
4864
		}
4865
	}
4866
 
4867
	return children->size();
4868
}
4869
 
4870
void CPackages::AssignPackageNumbers()
4871
{
4872
	int num = 0;
4873
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
4874
		node->Data()->SetNum(num++);
4875
}
4876
 
4877
CyString CPackages::FindDataDir(CyString dir, CyString file)
4878
{
4879
	CDirIO Dir(dir);
4880
	// data files could be in 4 places depending on what program is used, check for all of these
4881
	if ( Dir.Exists(file) )
4882
		return Dir.File(file);
4883
	else if ( Dir.Exists(CyString("Data/") + file) )
4884
		return Dir.File(CyString("Data/") + file);
4885
	else if ( Dir.Exists(CyString("../") + file) )
4886
		return Dir.File(CyString("../") + file);
4887
	else if ( Dir.Exists(CyString("../Data/") + file) )
4888
		return Dir.File(CyString("../Data/") + file);
4889
 
4890
	return NullString;
4891
}
4892
 
4893
void CPackages::Startup(CyString dir, CyString tempDir, CyString myDoc, CyString mod)
4894
{
4895
	this->SetTempDirectory(tempDir);
4896
	this->SetMyDocuments(myDoc);
4897
 
4898
	m_sSetMod = mod;
4899
 
4900
	// need to read the game exe versions
4901
	m_gameExe.Reset();
4902
 
4903
	CyString exeFile = this->FindDataDir(dir, "exe");
4904
 
4905
	// if file exists, read it, otherwise, just add
4906
	if ( !exeFile.Empty() && CFileIO(exeFile).Exists() )
4907
		m_gameExe.ReadFile(exeFile);
4908
	else
4909
	{
4910
		m_gameExe.ParseExe("x2.exe:5:NOSAVESUBDIR:HKCU/Software/EgoSoftware/X2/ModName:X2 The Threat:!GAMEDIR!:2:1604608!2150400:1.4 Artifical Life:1974272:1.5 Uplink");
4911
		m_gameExe.ParseExe("x3.exe:5:0:HKCU/Software/Egosoft/X3/ModName:X3 Reunion:Egosoft/X3:2:2347008:2.0 Bala Gi:2367488!2375680:2.5 Uplink");
4912
		m_gameExe.ParseExe("x3tc.exe:5:NO_XOR|TC_TEXT|MYDOCLOG:HKCU/Software/Egosoft/X3TC/ModName:X3 Terran Conflict:Egosoft/X3TC:3:1933464!1933520:2.0 Aldrin Expansion:-1:2.5 A New Home (Superbox):-1:3.0 Balance of Power");
4913
		m_gameExe.ParseExe("x3ap.exe:2:NO_XOR|TC_TEXT|MYDOCLOG|ADDON:HKCU/Software/Egosoft/X3AP/ModName:X3 Albion Prelude:Egosoft/X3AP:addon!x3tc.exe:0");
4914
	}
4915
}
4916
 
4917
int CPackages::GetGameLanguage(CyString dir)
4918
{
4919
	if ( dir.Empty() )
4920
		dir = m_sCurrentDir;
4921
 
4922
	CDirIO Dir(dir);
4923
 
4924
	// check for lang.dat file
4925
	if ( Dir.Exists("lang.dat") )
4926
	{
4927
		CFileIO File(Dir.File("lang.dat"));
4928
 
4929
		size_t size;
4930
		char *data = File.ReadToData(&size);
4931
 
4932
		if ( data )
4933
		{
4934
			CyString str(data);
4935
			return str.GetToken("\n", 1, 1).GetToken(" ", 1, 1).ToInt();
4936
		}
4937
	}
4938
 
4939
	return 0;
4940
}
4941
CyString CPackages::GetGameRunExe(CyString dir)
4942
{
4943
	return m_gameExe.GetGameRunExe((dir.Empty()) ? m_sCurrentDir : dir);
4944
}
4945
 
4946
CyString CPackages::GetGameNameFromType(int game)
4947
{
4948
	return m_gameExe.GetGameNameFromType(game - 1);
4949
}
4950
CyString CPackages::GetGameName(CyString dir)
4951
{
4952
	return m_gameExe.GetGameName((dir.Empty()) ? m_sCurrentDir : dir);
4953
}
4954
CyString CPackages::GetProperDir(CyString dir)
4955
{
4956
	return m_gameExe.GetProperDir((dir.Empty()) ? m_sCurrentDir : dir);
4957
}
4958
CyString CPackages::GetAddonDir(CyString dir)
4959
{
4960
	return m_gameExe.GetAddonDir((dir.Empty()) ? m_sCurrentDir : dir);
4961
}
4962
int CPackages::GetGameAddons(CyStringList &exes, CyString dir)
4963
{
4964
	return m_gameExe.GetGameAddons((dir.Empty()) ? m_sCurrentDir : dir, exes);
4965
}
4966
 
4967
CyString CPackages::GetGameTypesString(CBaseFile *package, bool includeVersion)
4968
{
4969
	if ( !package->AnyGameCompatability() )
4970
		return NullString;
4971
 
4972
	CyString sGames;
4973
	for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
4974
		if ( !sGames.Empty() )
4975
			sGames += ", ";
4976
		sGames += m_gameExe.GetGameNameFromType(gNode->Data()->iGame - 1);
4977
		if ( includeVersion ) {
4978
			sGames += " (";
4979
			sGames += m_gameExe.GetGameVersionFromType(gNode->Data()->iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
4980
			sGames += ")";
4981
		}
4982
	}
4983
	return sGames;
4984
}
4985
 
4986
CyString CPackages::GetGameVersionString(CBaseFile *package)
4987
{
4988
	for ( CListNode<SGameCompat> *gNode = package->GetGameCompatabilityList()->Front(); gNode; gNode = gNode->next() ) {
4989
		if ( gNode->Data()->iGame == m_iGame ) {
4990
			return m_gameExe.GetGameVersionFromType(m_iGame - 1, gNode->Data()->iVersion - 1, gNode->Data()->sVersion);
4991
		}
4992
	}
4993
 
4994
	return NullString;
4995
}
4996
 
4997
CyString CPackages::GetGameVersionFromType(int game, int version, CyString sVersion)
4998
{
4999
	return m_gameExe.GetGameVersionFromType(game - 1, version - 1, sVersion);
5000
}
5001
 
5002
int CPackages::CountBuiltInPackages(bool onlyEnabled)
5003
{
5004
	int count = 0;
5005
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
5006
	{
5007
		if ( !node->Data()->CheckGameCompatability(m_iGame) )
5008
			continue;
5009
		if ( onlyEnabled && !node->Data()->IsEnabled() )
5010
			continue;
5011
		if ( node->Data()->GetAuthor().Compare("PluginManager") )
5012
			++count;
5013
	}
5014
 
5015
	return count;
5016
}
5017
 
5018
int CPackages::CountPackages(int type, bool onlyEnabled)
5019
{
5020
	int count = 0;
5021
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
5022
	{
5023
		CBaseFile *p = node->Data();
5024
 
5025
		if ( (type != TYPE_BASE) && (p->GetType() != type) )
5026
			continue;
5027
 
5028
		if ( (onlyEnabled) && (!p->IsEnabled()) )
5029
			continue;
5030
 
5031
		++count;
5032
	}
5033
 
5034
	return count;
5035
}
5036
 
5037
int CPackages::ExtractGameFile(CyString aFilename, CyString aTo, CyString dir, CyString addon)
5038
{
5039
	// first check the enabled mod
5040
	if ( dir.Empty() )
5041
		dir = m_sCurrentDir;
5042
 
5043
	CyString addonDir = addon;
5044
	if ( addonDir.Empty() ) addonDir = this->GetAddonDir(dir);
5045
 
5046
	if ( m_pEnabledMod && m_pEnabledMod->AnyFileType(FILETYPE_MOD) )
5047
	{
5048
		for ( C_File *file = m_pEnabledMod->GetFirstFile(FILETYPE_MOD); file; file = m_pEnabledMod->GetNextFile(file) )
5049
		{
5050
			if ( !file->CheckFileExt("cat") )
5051
				continue;
5052
 
5053
			CCatFile catFile;
5054
			if ( catFile.Open(file->GetFilePointer(), addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
5055
			{
5056
				if ( catFile.ExtractFile(aFilename, aTo) )
5057
					return 1;
5058
				if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
5059
				{
5060
					if ( catFile.ExtractFile(aFilename) )
5061
						return -1;
5062
				}
5063
 
5064
			}
5065
		}
5066
	}
5067
	else if ( !m_sSetMod.Empty() )
5068
	{
5069
		if ( CFileIO(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat").Exists() )
5070
		{
5071
			CCatFile catFile;
5072
			if ( catFile.Open(m_sCurrentDir + "/mods/" + m_sSetMod + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
5073
			{
5074
				if ( catFile.ExtractFile(aFilename, aTo) )
5075
					return 1;
5076
				if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
5077
				{
5078
					if ( catFile.ExtractFile(aFilename) )
5079
						return -1;
5080
				}
5081
			}
5082
		}
5083
	}
5084
 
5085
	// find the highest cat number
5086
	int catNumber = 1;
5087
	while ( CFileIO(dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat").Exists() && catNumber < 99 )
5088
		++catNumber;
5089
 
5090
	// get the last one, not the next free one
5091
	--catNumber;
5092
 
5093
	// work backwards until we find the file
5094
	for ( ; catNumber; catNumber-- )
5095
	{
5096
		CCatFile catFile;
5097
		if ( catFile.Open(dir + "/" + CyString::Number(catNumber).PadNumber(2) + ".cat", addonDir, CATREAD_CATDECRYPT, false) == CATERR_NONE )
5098
		{
5099
			// check for the file
5100
			if ( catFile.ExtractFile(aFilename, aTo) )
5101
				return 1;
5102
			if ( catFile.Error() == CATERR_INVALIDDEST || catFile.Error() == CATERR_CANTCREATEDIR )
5103
			{
5104
				if ( catFile.ExtractFile(aFilename) )
5105
					return -1;
5106
			}
5107
		}
5108
	}
5109
 
5110
	return 0;
5111
}
5112
 
5113
void CPackages::CreateWareFiles()
5114
{
5115
	// do each ware
5116
	int wareTextID = WARETEXTSTART;
5117
	for ( int i = 0; i < WAREBUFFERS; i++ )
5118
	{
5119
		// its empty, no need to create ware files
5120
		if ( !m_lGameWares[i].size() )
5121
			continue;
5122
 
5123
		// lets extract the ware file
5124
		char wareType = CPackages::ConvertWareTypeBack(i);
5125
		CyString wareFile = "TWare";
5126
		wareFile += (char)UPPER(wareType);
5127
 
5128
		CyString openFile;
5129
 
5130
		int e;
5131
		if ( i == WARES_TECH && CFileIO(m_sTempDir + "/TWareT.txt").Exists() )
5132
			openFile = m_sTempDir + "/TWareT.txt";
5133
		else {
5134
			e = ExtractGameFile(CyString("types/") + wareFile + ".pck", m_sTempDir + "/" + wareFile + ".txt");
5135
			if ( e == 1 )
5136
				openFile = m_sTempDir + "/" + wareFile + ".txt";
5137
			else if ( e == -2 )
5138
				openFile = wareFile + ".txt";
5139
		}
5140
 
5141
		if ( !openFile.Empty() )
5142
		{
5143
			// read the file into memory
5144
			CyStringList wareLines;
5145
			int oldSize = -1;
5146
			int version = -1;
5147
 
5148
			// read first number
5149
			CFileIO readFile(m_sTempDir + "/" + wareFile + ".txt");
5150
			CyStringList *lines = readFile.ReadLinesStr();
5151
			if ( lines )
5152
			{
5153
				for ( SStringList *str = lines->Head(); str; str = str->next )
5154
				{
5155
					CyString line(str->str);
5156
					line.RemoveFirstSpace();
5157
					line.RemoveChar(9);
5158
					line.RemoveChar('\r');
5159
					if ( line.Empty() )
5160
						continue;
5161
					if ( line[0] == '/' )
5162
						continue;
5163
 
5164
					if ( oldSize == -1 )
5165
					{
5166
						version = line.GetToken(";", 1, 1).ToInt();
5167
						oldSize = line.GetToken(";", 2, 2).ToInt();
5168
					}
5169
					else
5170
					{
5171
						line.RemoveEndSpace();
5172
						if ( line.Right(1) != ';' )
5173
							line += ";";
5174
						wareLines.PushBack(line);
5175
						if ( wareLines.Count() >= oldSize )
5176
							break;
5177
					}
5178
				}
5179
 
5180
				delete lines;
5181
 
5182
				// apply the buffer
5183
				if ( m_iWareBuffer[i] <= 0 )
5184
					m_iWareBuffer[i] = wareLines.Count() + 10;
5185
				// last resort, readjust the buffer
5186
				else if ( wareLines.Count() > m_iWareBuffer[i] )
5187
					m_iWareBuffer[i] = wareLines.Count();
5188
 
5189
				// add the buffers
5190
				while ( wareLines.Count() < m_iWareBuffer[i] )
5191
					wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
5192
 
5193
				// add the ware lines
5194
				bool create = false;
5195
				for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
5196
				{
5197
					SGameWare *w = node->Data();
5198
 
5199
					if ( w->iType == WARETYPE_NONE )
5200
						continue;
5201
					else if ( w->iType == WARETYPE_DISABLED )
5202
					{
5203
						create = true;
5204
						wareLines.PushBack(CyString("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;") + w->sWareName + "_DISABLED;");
5205
					}
5206
					else if ( w->iType == WARETYPE_DELETED || !w->pWare )
5207
					{
5208
						create = true;
5209
						wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_DELETED;");
5210
					}
5211
					else if ( w->iType == WARETYPE_ADDED )
5212
					{
5213
						create = true;
5214
						w->pWare->iDescID = wareTextID;
5215
						w->iText = wareTextID;
5216
						wareTextID += 10;
5217
						w->iPos = wareLines.Count();
5218
						wareLines.PushBack(CyString("28;0;0;0;0;") + (long)wareLines.Count() + ";" + (long)(w->iText + 3) + ";" + (long)w->pWare->iVolumn + ";" + (long)w->pWare->iPrice + ";1;1;" + (long)w->pWare->iSize + ";" + (long)w->pWare->iPrice + ";" + (long)w->pWare->iNotority + ";0;0;" + w->sWareName.ToUpper() + ";");
5219
					}
5220
				}
5221
 
5222
				if ( create )
5223
				{
5224
					wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
5225
					CyString strV;
5226
					strV.FromFloat(GetLibraryVersion(), 2);
5227
					wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
5228
					if ( readFile.WriteFile(&wareLines) )
5229
						this->PackFile(&readFile, CyString("types\\") + wareFile + ".pck");
5230
				}
5231
			}
5232
			readFile.Remove();
5233
		}
5234
	}
5235
}
5236
 
5237
void CPackages::CreateEMPFile(CyString progDir)
5238
{
5239
	// do emp wares
5240
	int maxsize = 0;
5241
	CyString empWares;
5242
 
5243
	if ( m_iGame == GAME_X3TC )
5244
	{
5245
		maxsize = EMP_X3TC;
5246
		empWares = GetX3TCEmp();
5247
	}
5248
	else if ( m_iGame == GAME_X3AP )
5249
	{
5250
		maxsize = EMP_X3AP;
5251
		empWares = GetX3TCEmp();
5252
	}
5253
	else if ( m_iGame == GAME_X3 )
5254
	{
5255
		maxsize = EMP_X3;
5256
		empWares = GetX3Emp();
5257
	}
5258
 
5259
	if ( maxsize )
5260
	{
5261
		int e = ExtractGameFile("types/TWareT.pck", m_sTempDir + "/TWareT.txt");
5262
		if ( e )
5263
		{
5264
			// read the file into memory
5265
			CyStringList wareLines;
5266
			int oldSize = -1;
5267
			int version = -1;
5268
 
5269
			// read first number
5270
			CFileIO readFile((e == -1) ? "TWareT.txt" : m_sTempDir + "/TWareT.txt");
5271
			std::vector<CyString> *lines = readFile.ReadLines();
5272
			if ( lines )
5273
			{
5274
				for ( int i = 0; i < (int)lines->size(); i++ )
5275
				{
5276
					CyString line(lines->at(i));
5277
					line.RemoveFirstSpace();
5278
					line.RemoveChar('\r');
5279
					line.RemoveChar(9);
5280
					if ( line[0] == '/' )
5281
						continue;
5282
 
5283
					if ( oldSize == -1 )
5284
					{
5285
						version = line.GetToken(";", 1, 1).ToInt();
5286
						oldSize = line.GetToken(";", 2, 2).ToInt();
5287
					}
5288
					else
5289
					{
5290
						line.RemoveEndSpace();
5291
						if ( line.Right(1) != ";" )
5292
							line += ";";
5293
						wareLines.PushBack(line);
5294
						if ( wareLines.Count() >= oldSize )
5295
							break;
5296
					}
5297
				}
5298
 
5299
				delete lines;
5300
			}
5301
 
5302
			// now we too add/remove entries to match
5303
			// need filler entries
5304
			while ( wareLines.Count() < maxsize )
5305
				wareLines.PushBack("27;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;SS_WARE_FILLER;");
5306
 
5307
			int empEntries = 0;
5308
			CyString *empStr = empWares.SplitToken("\n", &empEntries);
5309
 
5310
			if ( empEntries && empStr )
5311
			{
5312
				// remove any empty end entries
5313
				while ( empStr[empEntries - 1].Empty() )
5314
					--empEntries;
5315
 
5316
				CyStringList addAfter;
5317
				if ( wareLines.Count() > maxsize )
5318
				{
5319
					// force emp, remove entries to allow them to be added
5320
					if ( m_bForceEMP )
5321
					{
5322
						// more after emp
5323
						if ( wareLines.Count() > (maxsize + empEntries) )
5324
						{
5325
							SStringList *node = wareLines.GetAt(maxsize + empEntries);
5326
							while ( node )
5327
							{
5328
								addAfter.PushBack(node->str);
5329
								node = node->next;
5330
							}
5331
						}
5332
 
5333
						// no remove them all
5334
						wareLines.DeleteFrom(maxsize);
5335
					}
5336
					else if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3AP ) // check if old emp is included, and convert it
5337
					{
5338
						if ( wareLines.Count() > 128 )
5339
						{
5340
							CyString test = wareLines.GetAt(128)->str;
5341
							if ( test.GetToken(";", -2).Compare("SS_WARE_SW_CUSTOM16_1;") )
5342
							{
5343
								// if theres any at the end, remove the last emp entry
5344
								if ( wareLines.Count() > (maxsize + empEntries - 1) )
5345
								{
5346
									SStringList *node = wareLines.GetAt(maxsize + empEntries - 2);
5347
									if ( node )
5348
										node->remove = true;
5349
								}
5350
								wareLines.Insert(128, "0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;128;");
5351
								wareLines.RemoveMarked();
5352
							}
5353
						}
5354
					}
5355
				}
5356
 
5357
				// too many entries, need to remove the first set of EMP wares
5358
				int i = 0;
5359
				if ( wareLines.Count() > maxsize )
5360
					i = wareLines.Count() - maxsize;
5361
 
5362
				for ( ; i < empEntries; i++ )
5363
				{
5364
					CyString str = empStr[i];
5365
					str.RemoveEndSpace();
5366
					str.RemoveChar(9);
5367
					str.RemoveChar('\r');
5368
					if ( str.Empty() )
5369
						continue;
5370
					if ( str.Right(1) != ";" )
5371
						str += ";";
5372
					wareLines.PushBack(str);
5373
				}
5374
 
5375
				for ( SStringList *afterNode = addAfter.Head(); afterNode; afterNode = afterNode->next )
5376
					wareLines.PushBack(afterNode->str);
5377
 
5378
				// finally we write the whole file
5379
				wareLines.PushFront(CyString::Number(version) + ";" + CyString::Number(wareLines.Count()) + ";", NullString);
5380
				CyString strV;
5381
				strV.FromFloat(GetLibraryVersion(), 2);
5382
				wareLines.PushFront(CyString("// Created by SPKInstaller Libraries V") + strV, NullString);
5383
				if ( readFile.WriteFile(&wareLines) )
5384
					this->PackFile(&readFile, "types\\TWareT.pck");
5385
			}
5386
			CLEANSPLIT(empStr, empEntries);
5387
		}
5388
	}
5389
}
5390
 
5391
CyString CPackages::ConvertTextString(CyString text)
5392
{
5393
	text = text.FindReplace("(", "\\(");
5394
	text = text.FindReplace(")", "\\)");
5395
	text = text.FindReplace("[", "{");
5396
	text = text.FindReplace("]", "}");
5397
	text = text.FindReplace(">", "&gt;");
5398
	text = text.FindReplace("<", "&lt;");
5399
	text = text.FindReplace("&", "&amp;");
5400
	return text;
5401
}
5402
 
5403
void CPackages::CreatePluginManagerText()
5404
{
5405
	int gameNumber = 0;
5406
	if ( m_iGame == GAME_X3 )
5407
		gameNumber = 30;
5408
	else if ( m_iGame == GAME_X3TC )
5409
		gameNumber = 35;
5410
	else if ( m_iGame == GAME_X3AP )
5411
		gameNumber = 38;
5412
 
5413
	int lang = m_iLanguage;
5414
	if ( !lang || lang < 0 )
5415
		lang = 44;
5416
 
5417
	CDirIO Dir(m_sCurrentDir);
5418
	if ( !Dir.Exists("t") )
5419
		Dir.Create("t");
5420
 
5421
	m_iLastUpdated = (int)time(NULL);
5422
 
5423
	CyString filename = SPK::FormatTextName(PMTEXTFILE, lang, (m_iGameFlags & EXEFLAG_TCTEXT));
5424
	CFileIO textFile(m_sCurrentDir + "/t/" + filename + ".xml");
5425
 
5426
	std::vector<CyString> writeData;
5427
 
5428
	CLinkList<SGameWare> lWares;
5429
	CLinkList<SGameShip> lShips;
5430
	for ( int i = 0; i < WAREBUFFERS; i++ )
5431
	{
5432
		for ( CListNode<SGameWare> *node = m_lGameWares[i].Front(); node; node = node->next() )
5433
		{
5434
			SGameWare *w = node->Data();
5435
			if ( w->iType == WARETYPE_NONE )
5436
				continue;
5437
			lWares.push_back(w);
5438
		}
5439
	}
5440
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
5441
	{
5442
		if ( node->Data()->iType == WARETYPE_NONE )
5443
			continue;
5444
		lShips.push_back(node->Data());
5445
	}
5446
 
5447
	writeData.push_back(CyString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"));
5448
	writeData.push_back(CyString("<language id=\"") + CyString::Number(lang) + "\">");
5449
 
5450
	if ( !gameNumber )
5451
		writeData.push_back(CyString("	<page id=\"") + CyString::Number(PMTEXTFILE) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
5452
	else
5453
		writeData.push_back(CyString("	<page id=\"") + (long)gameNumber + CyString::Number(PMTEXTFILE).PadNumber(4) + "\" title=\"Plugin Manager Text File\" descr=\"Contains text used for the plugin manager, packages, settings, wares, ship, etc\">");
5454
 
5455
	// write the heading
5456
	int start = 10000;
5457
	writeData.push_back(CyString("		<t id=\"1\">") + CyString::Number(m_iLastUpdated) + "</t>");
5458
	writeData.push_back(CyString("		<t id=\"2\">") + CyString::Number(this->CountPackages(TYPE_SPK, true)) + "</t>");
5459
	writeData.push_back(CyString("		<t id=\"3\">") + CyString::Number(start) + "</t>");
5460
	writeData.push_back(CyString("		<t id=\"4\">") + CyString::Number(lWares.size()) + "</t>");
5461
	writeData.push_back(CyString("		<t id=\"6\">") + CyString::Number(lShips.size()) + "</t>");
5462
 
5463
	// write some generic texts
5464
	writeData.push_back(CyString("		<t id=\"110\">") + (long)m_iLanguage + "</t>");
5465
	writeData.push_back(CyString("		<t id=\"109\">Plugin Manager: \\033GPoll Gui Data\\033X</t>"));
5466
	writeData.push_back(CyString("		<t id=\"107\">Plugin Manager: \\033GExport Game Data\\033X </t>"));
5467
	writeData.push_back(CyString("		<t id=\"100\">\\n</t>"));
5468
	writeData.push_back(CyString("		<t id=\"101\">\\033B</t>"));
5469
	writeData.push_back(CyString("		<t id=\"102\">\\033G</t>"));
5470
	writeData.push_back(CyString("		<t id=\"103\">\\033B</t>"));
5471
	writeData.push_back(CyString("		<t id=\"104\">\\033X</t>"));
5472
	writeData.push_back(CyString("		<t id=\"105\">\\033Y</t>"));
5473
	writeData.push_back(CyString("		<t id=\"106\">\\033C</t>"));
5474
	writeData.push_back(CyString("		<t id=\"108\">\\033</t>"));
5475
	// now write each package
5476
	int settingStart = 100000;
5477
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
5478
	{
5479
		CBaseFile *p = node->Data();
5480
		if ( !p->IsEnabled() )
5481
			continue;
5482
 
5483
		if ( p->GetType() != TYPE_SPK )
5484
			continue;
5485
 
5486
		CSpkFile *spk = (CSpkFile *)p;
5487
 
5488
		// count text files
5489
		CyString textEntries;
5490
		int textCount = 0;
5491
		C_File *f = p->GetFirstFile(FILETYPE_TEXT);
5492
		while ( f )
5493
		{
5494
			CyString sLang;
5495
			CyString id;
5496
			if ( m_iGameFlags & EXEFLAG_TCTEXT )
5497
			{
5498
				id = f->GetBaseName().GetToken("-", 1, 1);
5499
				sLang = f->GetBaseName().GetToken("-", 2, 2);
5500
				if ( sLang.Empty() )
5501
					sLang = "NULL";
5502
				else
5503
					sLang = sLang.Erase(0, 1);  // remove the "L"
5504
			}
5505
			else
5506
			{
5507
				sLang = f->GetBaseName().Left((int)f->GetBaseName().Length() - 4).PadNumber(3);
5508
				id = f->GetBaseName().Mid(((int)f->GetBaseName().Length() - 4) + 1, 4);
5509
			}
5510
 
5511
			if ( sLang != "NULL" )
5512
			{
5513
				if ( sLang.ToInt() == lang )
5514
				{
5515
					++textCount;
5516
					if ( !textEntries.Empty() )
5517
						textEntries += " ";
5518
					textEntries += id;
5519
				}
5520
			}
5521
			f = p->GetNextFile(f);
5522
		}
5523
 
5524
		CyString sTextCount((long)textCount);
5525
		writeData.push_back(CyString("		<t id=\"") + (long)start + "\">" + this->ConvertTextString(p->GetName()) + "</t>");
5526
		writeData.push_back(CyString("		<t id=\"") + (long)(start + 1) + "\">" + this->ConvertTextString(p->GetAuthor()) + "</t>");
5527
		writeData.push_back(CyString("		<t id=\"") + (long)(start + 2) + "\">" + this->ConvertTextString(p->GetVersion()) + "</t>");
5528
		writeData.push_back(CyString("		<t id=\"") + (long)(start + 3) + "\">" + this->ConvertTextString(p->GetLanguageName(lang)) + "</t>");
5529
 
13 cycrow 5530
		CLinkList<CSpkFile::SSettingType> *settings = spk->GetSettingsList();
1 cycrow 5531
		if ( settings && settings->size() )
5532
		{
5533
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 4) + "\">" + (long)settings->size() + "</t>");
5534
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 5) + "\">" + (long)settingStart + "</t>");
5535
 
13 cycrow 5536
			for ( CListNode<CSpkFile::SSettingType> *sNode = settings->Front(); sNode; sNode = sNode->next() )
1 cycrow 5537
			{
13 cycrow 5538
				CSpkFile::SSettingType *st = sNode->Data();
1 cycrow 5539
				writeData.push_back(CyString("		<t id=\"") + (long)(settingStart++) + "\">" + st->sKey + "</t>");
5540
				writeData.push_back(CyString("		<t id=\"") + (long)(settingStart++) + "\">" + spk->GetSetting(st) + "</t>");
5541
			}
5542
		}
5543
		else
5544
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 4) + "\">0</t>");
5545
 
5546
		writeData.push_back(CyString("		<t id=\"") + (long)(start + 6) + "\">" + sTextCount + "</t>");
5547
		if ( textCount )
5548
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 7) + "\">" + textEntries + "</t>");
5549
 
5550
		start += 10;
5551
	}
5552
 
5553
	// write ware names
5554
	if ( lWares.size() )
5555
	{
5556
		writeData.push_back(CyString("		<t id=\"5\">") + CyString::Number(start) + "</t>");
5557
		for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
5558
		{
5559
			SGameWare *w = node->Data();
5560
			if ( w->pWare && w->iType == WARETYPE_ADDED )
5561
				writeData.push_back(CyString("		<t id=\"") + (long)start + "\">" + w->sWareName + "</t>");
5562
			else
5563
				writeData.push_back(CyString("		<t id=\"") + (long)start + "\">-1</t>");
5564
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 1) + "\">" + CyString((char)w->cType) + "</t>");
5565
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 2) + "\">" + (long)w->iPos + "</t>");
5566
			start += 10;
5567
		}
5568
	}
5569
 
5570
	if ( lShips.size() )
5571
	{
5572
		writeData.push_back(CyString("		<t id=\"7\">") + CyString::Number(start) + "</t>");
5573
		for ( CListNode<SGameShip> *node = lShips.Front(); node; node = node->next() )
5574
		{
5575
			SGameShip *gs = node->Data();
5576
			if ( gs->iType == WARETYPE_NONE )
5577
				continue;
5578
			if ( gs->pPackage && gs->iType == WARETYPE_ADDED )
5579
				writeData.push_back(CyString("		<t id=\"") + (long)start + "\">" + gs->sShipID + "</t>");
5580
			else
5581
				writeData.push_back(CyString("		<t id=\"") + (long)start + "\">-1</t>");
5582
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 1) + "\">" + (long)gs->iPos + "</t>");
5583
			writeData.push_back(CyString("		<t id=\"") + (long)(start + 2) + "\">Ship</t>");
5584
 
5585
			// write shipyard info
5586
			if ( gs->pPackage )
5587
			{
5588
				int doStart = start + 5;
5589
				for ( int i = SHIPYARD_ARGON; i <= SHIPYARD_MAX; i *= 2 )
5590
				{
5591
					writeData.push_back(CyString("		<t id=\"") + (long)(doStart) + "\">" + ((gs->pPackage->IsShipyard(i)) ? CyString::Number(1) : CyString::Number(0)) + "</t>");
5592
					++doStart;
5593
				}
5594
			}
5595
 
5596
			start += 20;
5597
		}
5598
	}
5599
 
5600
	writeData.push_back(CyString("	</page>"));
5601
 
5602
	// wares
5603
	if ( m_iGame == GAME_X3AP || m_iGame == GAME_X3TC || m_iGame == GAME_X3 || lWares.size() || lShips.size() )
5604
	{
5605
		if ( !gameNumber )
5606
			writeData.push_back(CyString("  <page id=\"17\" title=\"Plugin Manager Objects\">"));
5607
		else
5608
			writeData.push_back(CyString("  <page id=\"") + (long)gameNumber + "0017\" title=\"Plugin Manager Objects\">");
5609
 
5610
		writeData.push_back(CyString("		<t id=\"") + (long)(SHIPSTARTTEXT - 1) + "\">ZZ_BLANKSHIP</t>");
5611
		// do emp
5612
		if ( m_iGame == GAME_X3TC || m_iGame == GAME_X3 || m_iGame == GAME_X3AP )
5613
			writeData.push_back(GetEMPText());
5614
 
5615
		// object names
5616
		for ( CListNode<SGameWare> *node = lWares.Front(); node; node = node->next() )
5617
		{
5618
			SGameWare *w = node->Data();
5619
			if ( !w->pWare || w->iType != WARETYPE_ADDED )
5620
				continue;
5621
 
5622
			// find the correct text for the language
5623
			CyString name = CSpkFile::GetWareText(w->pWare, m_iLanguage);
5624
			CyString desc = CSpkFile::GetWareDesc(w->pWare, m_iLanguage);
5625
			if ( !name.Empty() )
5626
				writeData.push_back(CyString("		<t id=\"") + (long)(w->iText + 3) + "\">" + name + "</t>");
5627
			if ( !desc.Empty() )
5628
				writeData.push_back(CyString("		<t id=\"") + (long)(w->iText + 4) + "\">" + desc + "</t>");
5629
		}
5630
		for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
5631
		{
5632
			SGameShip *s = node->Data();
5633
			if ( !s->pPackage || s->iType != WARETYPE_ADDED )
5634
				continue;
5635
			if ( s->pPackage->GetOriginalDescription() )
5636
				continue;
5637
 
5638
			CyString name = s->pPackage->GetTextName(m_iLanguage);
5639
			CyString desc = s->pPackage->GetTextDescription(m_iLanguage);
5640
			if ( !name.Empty() )
5641
				writeData.push_back(CyString("		<t id=\"") + (long)s->iText + "\">" + name + "</t>");
5642
			if ( !desc.Empty() )
5643
				writeData.push_back(CyString("		<t id=\"") + (long)(s->iText + 1) + "\">" + desc + "</t>");
5644
		}
5645
		writeData.push_back(CyString("  </page>"));
5646
	}
5647
	writeData.push_back(CyString("</language>"));
5648
	textFile.WriteFileUTF(&writeData);
5649
 
5650
	size_t fileSize;
5651
	char *fileData = CFileIO(textFile.GetFullFilename()).ReadToData(&fileSize);
5652
 
5653
	if ( fileData && fileSize)
5654
	{
5655
		size_t newFileSize;
5656
		unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
5657
		if ( pckData )
5658
		{
5659
			CFileIO pckFile(m_sCurrentDir + "/t/" + filename + ".pck");
5660
			pckFile.WriteData((char *)pckData, newFileSize);
5661
			this->AddCreatedFile(pckFile.GetFullFilename());
5662
		}
5663
	}
5664
	textFile.Remove();
5665
}
5666
 
5667
bool CPackages::IsCurrentDir(CyString dir)
5668
{
5669
	if ( dir.Compare(m_sCurrentDir) )
5670
		return true;
5671
 
5672
	CyString checkDir = m_sCurrentDir;
5673
	checkDir = checkDir.FindReplace("/", "\\");
5674
	if ( checkDir.Compare(dir) )
5675
		return true;
5676
 
5677
	checkDir = checkDir.FindReplace("\\", "/");
5678
	if ( checkDir.Compare(dir) )
5679
		return true;
5680
 
5681
	return false;
5682
}
5683
 
5684
void CPackages::BackupSaves(bool vanilla)
5685
{
5686
	// copy any saves into the vanilla directory
5687
	CyString dir = (vanilla) ? "Vanilla" : "Modified";
5688
 
5689
	// make sure the directory exists
5690
	CDirIO saveDir(this->GetSaveDirectory());
5691
	CDirIO gameSaveDir(m_sCurrentDir);
5692
	if ( !gameSaveDir.Exists("PluginManager") )
5693
		gameSaveDir.Create("PluginManager");
5694
	gameSaveDir.cd("PluginManager");
5695
	if ( !gameSaveDir.Exists("Saves") )
5696
		gameSaveDir.Create("Saves");
5697
	gameSaveDir.cd("Saves");
5698
	if ( !gameSaveDir.Exists(dir) )
5699
		gameSaveDir.Create(dir);
5700
	gameSaveDir.cd(dir);
5701
 
5702
	// backup the saves
5703
	CyStringList *dirs = saveDir.DirList();
5704
	if ( dirs )
5705
	{
5706
		for ( SStringList *node = dirs->Head(); node; node = node->next )
5707
		{
5708
			CFileIO File(saveDir.File(node->str));
5709
			if ( !File.CheckFileExtension("sav") )
5710
				continue;
5711
			// remove the file if already exists
5712
			if ( gameSaveDir.Exists(node->str) )
5713
				CFileIO(gameSaveDir.File(node->str)).Remove();
5714
 
5715
			// copy the file into the games save dir for backup
5716
			File.Copy(gameSaveDir.File(File.GetFilename()), true);
5717
		}
5718
 
5719
		delete dirs;
5720
	}
5721
 
5722
}
5723
 
5724
void CPackages::RestoreSaves(bool vanilla)
5725
{
5726
	// get dir to restore from
5727
	CyString dir = (vanilla) ? "Vanilla" : "Modified";
5728
	CDirIO restoreDir(m_sCurrentDir + "/PluginManager/Saves/" + dir);
5729
	CDirIO toDir(this->GetSaveDirectory());
5730
 
5731
	CyStringList *dirs = restoreDir.DirList();
5732
	if ( dirs )
5733
	{
5734
		for ( SStringList *node = dirs->Head(); node; node = node->next )
5735
		{
5736
			CFileIO File(restoreDir.File(node->str));
5737
			// remove the file if already exists
5738
			if ( toDir.Exists(node->str) )
5739
				CFileIO(toDir.File(node->str)).Remove();
5740
 
5741
			// move file over
5742
			File.Copy(toDir.File(node->str), true);
5743
		}
5744
 
5745
		delete dirs;
5746
	}
5747
 
5748
}
5749
 
5750
bool CPackages::RemoveCurrentDirectory()
5751
{
5752
	if ( !m_bLoaded )
5753
		return false;
5754
 
5755
	// remove all package files
5756
	this->RemoveAllPackages();
5757
 
5758
	// remove all plugin manager files
5759
	this->RemoveCreatedFiles();
5760
 
5761
	// restore any vanilla saves
5762
	/*
5763
	if ( !m_bVanilla )
5764
	{
5765
		this->BackupSaves();
5766
		m_bVanilla = true;
5767
		this->RestoreSaves();
5768
	}*/
5769
 
5770
	this->Reset();
5771
	m_bLoaded = false;
5772
 
5773
	// clear the plugin manager directory
5774
	CDirIO Dir(m_sCurrentDir);
5775
	Dir.RemoveDir("PluginManager", true, true, 0);
5776
	Dir.RemoveDir("dds", false, true, 0);
5777
	Dir.RemoveDir("objects", false, true, 0);
5778
	Dir.RemoveDir("types", false, true, 0);
5779
	Dir.RemoveDir("textures", false, true, 0);
5780
 
5781
	// remove the plugin manager mod files
5782
	if ( Dir.Exists("mods/PluginManager.cat") )
5783
		CFileIO(Dir.File("mods/PluginManager.cat")).Remove();
5784
	if ( Dir.Exists("mods/PluginManager.dat") )
5785
		CFileIO(Dir.File("mods/PluginManager.dat")).Remove();
5786
 
5787
	return true;
5788
}
5789
 
5790
void CPackages::CreateDummies()
5791
{
5792
	// first check we have any ships
5793
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
5794
		return;
5795
 
5796
	CLinkList<SDummyEntry> dummyList;
5797
 
5798
	// now extract the existing dummies
5799
	int e = ExtractGameFile("types/Dummies.pck", m_sTempDir + "/Dummies.txt");
5800
	if ( e )
5801
	{
5802
		// read the dummies
5803
		CFileIO File;
5804
		if ( File.Open((e == -1) ? "Dummies.txt" : m_sTempDir + "/Dummies.txt") )
5805
		{
5806
			std::vector<CyString> *lines = File.ReadLines();
5807
			if ( lines )
5808
			{
5809
				int insection = 0;
5810
				SDummyEntry *currentSection = NULL;
5811
				for ( int j = 0; j < (int)lines->size(); j++ )
5812
				{
5813
					CyString line(lines->at(j));
5814
					line.RemoveChar(9);
5815
					line.RemoveChar('\r');
5816
					line.RemoveFirstSpace();
5817
					if ( line.Empty() )
5818
						continue;
5819
					if ( line[0] == '/' )
5820
						continue;
5821
 
5822
					// read the section, first entry is section, second is size
5823
					while ( !line.Empty() )
5824
					{
5825
						if ( !insection )
5826
						{
5827
							CyString section = line.GetToken(";", 1, 1);
5828
							insection = line.GetToken(";", 2, 2).ToInt();
5829
 
5830
							// search for the sections
5831
							currentSection = NULL;
5832
							for ( CListNode<SDummyEntry> *node = dummyList.Front(); node; node = node->next() )
5833
							{
5834
								SDummyEntry *d = node->Data();
5835
								if ( d->sSection.Compare(section) )
5836
								{
5837
									currentSection = node->Data();
5838
									break;
5839
								}
5840
							}
5841
 
5842
							if ( !currentSection )
5843
							{
5844
								currentSection = new struct SDummyEntry;
5845
								currentSection->sSection = section;
5846
								dummyList.push_back(currentSection);
5847
							}
5848
 
5849
							// we have some more ?
5850
							line = line.DelToken(";", 1, 2);
5851
						}
5852
						else
5853
						{
5854
							--insection;
5855
							// check the last entry for number of states
5856
							if ( currentSection->sSection.Compare("SDTYPE_GUN") )
5857
							{
5858
								int states = line.GetToken(";", 3, 3).ToInt();
5859
								int parts = line.GetToken(";", 4 + (states * 2), 4 + (states * 2)).ToInt();
5860
								CyString data = line.GetToken(";", 1, 4 + (states * 2) + (parts * 2)) + ";";
5861
								currentSection->lEntries.PushBack(data);
5862
 
5863
								// remove done
5864
								line = line.DelToken(";", 1, 4 + (states * 2) + (parts * 2));
5865
							}
5866
							else
5867
							{
5868
								int states = line.GetToken(";", 3, 3).ToInt();
5869
								CyString data = line.GetToken(";", 1, 3 + (states * 2)) + ";";
5870
								currentSection->lEntries.PushBack(data);
5871
 
5872
								// remove done
5873
								line = line.DelToken(";", 1, 3 + (states * 2));
5874
							}
5875
						}
5876
					}
5877
				}
5878
 
5879
				delete lines;
5880
			}
5881
 
5882
			File.Remove();
5883
		}
5884
 
5885
		// add the new entries for the ships
5886
		for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
5887
		{
5888
			SGameShip *s = node->Data();
5889
			if ( s->iType != WARETYPE_ADDED || !s->pPackage )
5890
				continue;
5891
 
5892
			// no dummies to add?
5893
			if ( !s->pPackage->AnyDummies() )
5894
				continue;
5895
 
5896
			// add each dummy to list
5897
			for ( CListNode<SDummy> *dNode = s->pPackage->GetDummies()->Front(); dNode; dNode = dNode->next() )
5898
			{
5899
				SDummy *dummy = dNode->Data();
5900
				SDummyEntry *found = NULL;
5901
				for ( CListNode<SDummyEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
5902
				{
5903
					if ( eNode->Data()->sSection.Compare(dummy->sSection) )
5904
					{
5905
						found = eNode->Data();
5906
						break;
5907
					}
5908
				}
5909
				if ( !found )
5910
				{
5911
					found = new SDummyEntry;
5912
					found->sSection = dummy->sSection;
5913
					dummyList.push_back(found);
5914
				}
5915
				// check if its already on the list
5916
				else
5917
				{
5918
					bool f = false;
5919
					for ( SStringList *strNode = found->lEntries.Head(); strNode; strNode = strNode->next )
5920
					{
5921
						if ( strNode->str.GetToken(";", 1, 1).Compare(dummy->sData.GetToken(";", 1, 1)) )
5922
						{
5923
							f = true;
5924
							break;
5925
						}
5926
					}
5927
 
5928
					if ( f )
5929
						continue;
5930
				}
5931
 
5932
				found->lEntries.PushBack(dummy->sData);
5933
			}
5934
		}
5935
 
5936
		// finally, write the file
5937
		std::vector<CyString> lines;
5938
		lines.push_back(CyString("// Dummies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
5939
		for ( SDummyEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
5940
		{
5941
			lines.push_back("");
5942
			lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.Count());
5943
			lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.Count()) + ";");
5944
			for ( SStringList *str = dummy->lEntries.Head(); str; str = str->next )
5945
			{
5946
				CyString strLine = str->str;
5947
				strLine.RemoveChar(9);
5948
				strLine.RemoveChar('\r');
5949
				strLine.RemoveEndSpace();
5950
				strLine.RemoveFirstSpace();
5951
				strLine = strLine.FindReplace("<::PiPe::>", "|");
5952
				if ( strLine.Right(1) != ";" )
5953
					strLine += ";";
5954
				lines.push_back(strLine);
5955
			}
5956
		}
5957
		lines.push_back("");
5958
 
5959
		// write the file to disk
5960
		CFileIO WriteFile(m_sTempDir + "/dummies.txt");
5961
		if ( WriteFile.WriteFile(&lines) )
5962
		{
5963
			this->PackFile(&WriteFile, "types\\dummies.pck");
5964
			WriteFile.Remove();
5965
		}
5966
	}
5967
}
5968
 
5969
void CPackages::CreateCutData()
5970
{
5971
	// first check we have any ships
5972
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
5973
		return;
5974
 
5975
	bool found = false;
5976
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
5977
	{
5978
		SGameShip *s = node->Data();
5979
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
5980
			continue;
5981
 
5982
		// no dummies to add?
5983
		if ( !s->pPackage->AnyCutData() )
5984
			continue;
5985
		found = true;
5986
		break;
5987
	}
5988
 
5989
	if ( !found )
5990
		return;
5991
 
5992
	CyStringList cutList;
5993
	int e = ExtractGameFile("types/CutData.pck", m_sTempDir + "/CutData.txt");
5994
	if ( e )
5995
	{
5996
		CFileIO File;
5997
		if ( File.Open((e == -1) ? "CutData.txt" : m_sTempDir + "/CutData.txt") )
5998
		{
5999
			std::vector<CyString> *lines = File.ReadLines();
6000
			if ( lines )
6001
			{
6002
				int entries = -1;
6003
				for ( int j = 0; j < (int)lines->size(); j++ )
6004
				{
6005
					CyString line(lines->at(j));
6006
					line.RemoveChar(9);
6007
					line.RemoveChar('\r');
6008
					line.RemoveChar(' ');
6009
					if ( line.Empty() || line[0] == '/' )
6010
						continue;
6011
					if ( entries == -1 )
6012
						entries = line.GetToken(";", 1, 1).ToInt();
6013
					else
6014
					{
6015
						if ( line.Right(1) != ";" )
6016
							line += ";";
6017
						cutList.PushBack(line);
6018
						if ( cutList.Count() == entries )
6019
							break;
6020
					}
6021
				}
6022
 
6023
				delete lines;
6024
			}
6025
 
6026
			File.Remove();
6027
		}
6028
	}
6029
 
6030
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6031
	{
6032
		SGameShip *s = node->Data();
6033
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6034
			continue;
6035
 
6036
		// no dummies to add?
6037
		if ( !s->pPackage->AnyCutData() )
6038
			continue;
6039
 
6040
		// add each dummy to list
6041
		for ( SStringList *strNode = s->pPackage->GetCutData()->Head(); strNode; strNode = strNode->next )
6042
		{
6043
			CyString str = strNode->str;
6044
			str.RemoveChar(' ');
6045
			if ( str.Right(1) != ";" )
6046
				str += ";";
6047
			cutList.PushBack(str, true);
6048
		}
6049
	}
6050
 
6051
	cutList.PushFront(CyString::Number(cutList.Count()) + ";");
6052
	cutList.PushFront("/cut id;filename (leave blank to use id)");
6053
	cutList.PushFront(CyString("// Cut Data file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
6054
 
6055
	// write the file to disk
6056
	CFileIO WriteFile(m_sTempDir + "/CutData.txt");
6057
	if ( WriteFile.WriteFile(&cutList) )
6058
	{
6059
		this->PackFile(&WriteFile, "types\\CutData.pck");
6060
		WriteFile.Remove();
6061
	}
6062
}
6063
 
6064
void CPackages::CreateAnimations()
6065
{
6066
	// first check we have any ships
6067
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
6068
		return;
6069
 
6070
	bool found = false;
6071
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6072
	{
6073
		SGameShip *s = node->Data();
6074
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6075
			continue;
6076
 
6077
		// no dummies to add?
6078
		if ( !s->pPackage->AnyAnimations() )
6079
			continue;
6080
		found = true;
6081
		break;
6082
	}
6083
 
6084
	if ( !found )
6085
		return;
6086
 
6087
	CyStringList aniList;
6088
	int e = ExtractGameFile("types/Animations.pck", m_sTempDir + "/Animations.txt");
6089
	if ( e )
6090
	{
6091
		CFileIO File;
6092
		if ( File.Open((e == -1) ? "Animations.txt" : m_sTempDir + "/Animations.txt") )
6093
		{
6094
			std::vector<CyString> *lines = File.ReadLines();
6095
			if ( lines )
6096
			{
6097
				for ( int j = 0; j < (int)lines->size(); j++ )
6098
				{
6099
					CyString line(lines->at(j));
6100
					aniList.PushBack(line);
6101
				}
6102
 
6103
				delete lines;
6104
			}
6105
 
6106
			File.Remove();
6107
		}
6108
	}
6109
 
6110
	CyStringList parsedAniList;
6111
	CXspFile::ReadAnimations(&aniList, &parsedAniList, 0);
6112
 
6113
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6114
	{
6115
		SGameShip *s = node->Data();
6116
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6117
			continue;
6118
 
6119
		// no dummies to add?
6120
		if ( !s->pPackage->AnyAnimations() )
6121
			continue;
6122
 
6123
		// add each dummy to list
6124
		for ( SStringList *strNode = s->pPackage->GetAnimations()->Head(); strNode; strNode = strNode->next )
6125
			parsedAniList.PushBack(strNode->str);
6126
	}
6127
 
6128
	// format the list with added spaces
6129
	CyStringList formatedAniList;
6130
	int lineCount = -1;
6131
	for ( SStringList *strNode = parsedAniList.Head(); strNode; strNode = strNode->next )
6132
	{
6133
		// format the comment to match the line number
6134
		lineCount++;
6135
		CyString oldComment = strNode->str.GetToken("//", 2);
6136
		CyString comment = CyString("//") + CyString::Number(lineCount);
6137
		if ( !oldComment.Empty() )
6138
		{
6139
			comment += " ";
6140
			oldComment.RemoveFirstSpace();
6141
			if ( oldComment.GetToken(" ", 1, 1).IsNumber() )
6142
				comment += oldComment.GetToken(" ", 2);
6143
			else
6144
				comment += oldComment;
6145
		}
6146
		CyString line = strNode->str.GetToken("//", 1, 1);
6147
 
6148
		// split into seperate lines
6149
		CyString first = line.GetToken(";", 1, 1);
6150
		if ( first.Compare("TAT_TAGSINGLESTEP") )
6151
		{
6152
			formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
6153
			int max;
6154
			CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
6155
			if ( max && sLines )
6156
			{
6157
				if ( sLines[max - 1].Empty() )
6158
					--max; // remove the last ";"
6159
 
6160
				for ( int i = 0; i < max; i++ )
6161
				{
6162
					CyString l = CyString("\t") + sLines[i] + ";";
6163
					if ( i == (max - 1) )
6164
						formatedAniList.PushBack(l + comment);
6165
					else
6166
						formatedAniList.PushBack(l);
6167
				}
6168
			}
6169
			CLEANSPLIT(sLines, max);
6170
		}
6171
		else if ( (first.Compare("TAT_TAGONESHOT") || first.Compare("TAT_TAGLOOP")) && (line.IsIn("TATF_COORDS")) )
6172
		{
6173
			formatedAniList.PushBack(line.GetToken(";", 1, 5) + ";");
6174
			int max;
6175
			CyString *sLines = line.GetToken(";", 6).SplitToken(";", &max);
6176
			if ( max && sLines )
6177
			{
6178
				if ( sLines[max - 1].Empty() )
6179
					--max; // remove the last ";"
6180
 
6181
				CyString prevLine;
6182
				for ( int i = 0; i < max; i++ )
6183
				{
6184
					CyString l = sLines[i] + ";";
6185
					if ( l.IsIn("TATF_COORDS") && !prevLine.Empty() )
6186
					{
6187
						formatedAniList.PushBack(CyString("\t") + prevLine);
6188
						prevLine = "";
6189
					}
6190
					prevLine += l;
6191
				}
6192
 
6193
				if ( !prevLine.Empty() )
6194
					formatedAniList.PushBack(CyString("\t") + prevLine + comment);
6195
 
6196
			}
6197
			CLEANSPLIT(sLines, max);
6198
		}
6199
		else
6200
			formatedAniList.PushBack(line + comment);
6201
	}
6202
 
6203
	formatedAniList.PushFront(CyString::Number(parsedAniList.Count()) + ";");
6204
	formatedAniList.PushFront(CyString("// Animations, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
6205
 
6206
	// write the file to disk
6207
	CFileIO WriteFile(m_sTempDir + "/Animations.txt");
6208
	if ( WriteFile.WriteFile(&formatedAniList) )
6209
	{
6210
		this->PackFile(&WriteFile, "types\\Animations.pck");
6211
		WriteFile.Remove();
6212
	}
6213
}
6214
 
6215
void CPackages::CreateBodies()
6216
{
6217
	// first check we have any ships
6218
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
6219
		return;
6220
 
6221
	bool found = false;
6222
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6223
	{
6224
		SGameShip *s = node->Data();
6225
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6226
			continue;
6227
 
6228
		// no dummies to add?
6229
		if ( !s->pPackage->AnyBodies() )
6230
			continue;
6231
		found = true;
6232
		break;
6233
	}
6234
 
6235
	if ( !found )
6236
		return;
6237
 
6238
	// lets read our current bodies file
6239
	CLinkList<SBodies> bodiesList;
6240
	SBodies *currentSection = NULL;
6241
	int e = ExtractGameFile("types/Bodies.pck", m_sTempDir + "/Bodies.txt");
6242
	if ( e )
6243
	{
6244
		CFileIO File;
6245
		if ( File.Open((e == -1) ? "Bodies.txt" : m_sTempDir + "/Bodies.txt") )
6246
		{
6247
			std::vector<CyString> *lines = File.ReadLines();
6248
			if ( lines )
6249
			{
6250
				int entries = 0;
6251
				for ( int j = 0; j < (int)lines->size(); j++ )
6252
				{
6253
					CyString line(lines->at(j));
6254
					line.RemoveChar(' ');
6255
					line.RemoveChar(9);
6256
					if ( line.Empty() || line[0] == '/' )
6257
						continue;
6258
					if ( entries <= 0 )
6259
					{
6260
						entries = line.GetToken(";", 2, 2).ToInt();
6261
						currentSection = new SBodies;
6262
						currentSection->sSection = line.GetToken(";", 1, 1);
6263
						bodiesList.push_back(currentSection);
6264
					}
6265
					else if ( currentSection )
6266
					{
6267
						int num;
6268
						CyString *strs = line.SplitToken(";", &num);
6269
						if ( num && strs )
6270
						{
6271
							for ( int i = 0; i < num; i++ )
6272
							{
6273
								if ( strs[i].Empty() )
6274
									continue;
6275
								currentSection->lEntries.PushBack(strs[i] + ";", true);
6276
								--entries;
6277
							}
6278
						}
6279
						CLEANSPLIT(strs, num);
6280
					}
6281
				}
6282
 
6283
				delete lines;
6284
			}
6285
			File.Remove();
6286
		}
6287
	}
6288
 
6289
	// lets now add any new entries
6290
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6291
	{
6292
		SGameShip *s = node->Data();
6293
		if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6294
			continue;
6295
 
6296
		// no dummies to add?
6297
		if ( !s->pPackage->AnyBodies() )
6298
			continue;
6299
 
6300
		// add each dummy to list
6301
		for ( SStringList *strNode = s->pPackage->GetBodies()->Head(); strNode; strNode = strNode->next )
6302
		{
6303
			CyString section = strNode->str.GetToken(";", 1, 1);
6304
			CyString body = strNode->str.GetToken(";", 2).Remove(' ');
6305
			if ( body.Right(1) != ";" )
6306
				body += ";";
6307
 
6308
			// find the section to add into
6309
			SBodies *foundSection = NULL;
6310
			for ( CListNode<SBodies> *checkBody = bodiesList.Front(); checkBody; checkBody = checkBody->next() )
6311
			{
6312
				if ( checkBody->Data()->sSection.Compare(section) )
6313
				{
6314
					foundSection = checkBody->Data();
6315
					break;
6316
				}
6317
			}
6318
 
6319
			if ( !foundSection )
6320
			{
6321
				foundSection = new SBodies;
6322
				foundSection->sSection = section;
6323
				bodiesList.push_back(foundSection);
6324
			}
6325
			foundSection->lEntries.PushBack(body, true);
6326
		}
6327
	}
6328
 
6329
	// now write the file
6330
	CyStringList writeList;
6331
	// the header first
6332
	writeList.PushBack(CyString("// Bodies file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
6333
	writeList.PushBack("//body type;num bodies;");
6334
	writeList.PushBack("//[body id/name]");
6335
 
6336
	// now our sections
6337
	for ( SBodies *bSection = bodiesList.First(); bSection; bSection = bodiesList.Next() )
6338
	{
6339
		writeList.PushBack("");
6340
		writeList.PushBack(CyString("// Section: ") + bSection->sSection);
6341
		writeList.PushBack(bSection->sSection + ";" + CyString::Number(bSection->lEntries.Count()) + ";");
6342
		for ( SStringList *strNode = bSection->lEntries.Head(); strNode; strNode = strNode->next )
6343
		{
6344
			CyString str = strNode->str;
6345
			str.RemoveChar(9);
6346
			str.RemoveChar(' ');
6347
			if ( str.Right(1) != ";" )
6348
				str += ";";
6349
			writeList.PushBack(str);
6350
		}
6351
	}
6352
 
6353
	// write the file to disk
6354
	CFileIO WriteFile(m_sTempDir + "/Bodies.txt");
6355
	if ( WriteFile.WriteFile(&writeList) )
6356
	{
6357
		this->PackFile(&WriteFile, "types\\Bodies.pck");
6358
		WriteFile.Remove();
6359
	}
6360
}
6361
 
6362
void CPackages::CreateCustomStarts()
6363
{
6364
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
6365
	{
6366
		// find all spk files (only ones that can be custom starts
6367
		if ( node->Data()->GetType() != TYPE_SPK )
6368
			continue;
6369
 
6370
		CSpkFile *p = (CSpkFile *)node->Data();
6371
 
6372
		// only use custom starts
6373
		if ( !p->IsCustomStart() )
6374
			continue;
6375
 
6376
		// get the name of the start to use
6377
		CyString name = p->GetCustomStartName();
6378
		if ( name.Empty() )
6379
			continue;
6380
 
6381
		// find if maps file exists
6382
		CyStringList createFiles;
6383
		createFiles.PushBack(name, "maps/x3_universe");
6384
		createFiles.PushBack(name, "types/Jobs");
6385
		createFiles.PushBack(name, "types/JobWings");
6386
 
6387
		for ( SStringList *str = createFiles.Head(); str; str = str->next )
6388
		{
6389
			CyString dir = CFileIO(str->data).GetDir();
6390
			int type = FILETYPE_EXTRA;
6391
			if ( dir.Compare("maps") )
6392
				type = FILETYPE_MAP;
6393
 
6394
			if ( !p->FindFile(str->str + ".xml", type) && !p->FindFile(str->str + ".pck", type) )
6395
			{
6396
				// create a maps files
6397
				int e = this->ExtractGameFile(str->data + ".pck", m_sTempDir + "/" + str->data + ".pck");
6398
				if ( e )
6399
				{
6400
					CFileIO File((e == -1) ? (str->data + ".pck") : (m_sTempDir + "/" + str->data + ".pck"));
6401
					if ( File.Exists() )
6402
					{
6403
						File.Rename(m_sCurrentDir + "/" + dir + "/" + str->str + ".pck");
6404
						this->AddCreatedFile(dir + "/" + str->str + ".pck");
6405
					}
6406
				}
6407
			}
6408
		}
6409
	}
6410
}
6411
 
6412
void CPackages::AddCreatedFile(CyString file)
6413
{
6414
	file = file.Remove(m_sCurrentDir);
6415
	while ( file[0] == '/' )
6416
		file.Erase(0, 1);
6417
	while ( file[0] == '\\' )
6418
		file.Erase(0, 1);
6419
 
6420
	m_lCreatedFiles.PushBack(file, true);
6421
}
6422
 
6423
void CPackages::CreateComponants()
6424
{
6425
	// first check we have any ships
6426
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
6427
		return;
6428
 
6429
	CLinkList<SComponantEntry> dummyList;
6430
 
6431
	// now extract the existing dummies
6432
	int e = ExtractGameFile("types/Components.pck", m_sTempDir + "/Components.txt");
6433
	if ( e )
6434
	{
6435
		// read the dummies
6436
		CFileIO File;
6437
		if ( File.Open((e == -1) ? "Components.txt" : m_sTempDir + "/Components.txt") )
6438
		{
6439
			std::vector<CyString> *lines = File.ReadLines();
6440
			if ( lines )
6441
			{
6442
				int insection = 0;
6443
				int insubsection = 0;
6444
				SComponantEntry *currentSection = NULL;
6445
				SComponantEntry2 *currentSubSection = NULL;
6446
				for ( int j = 0; j < (int)lines->size(); j++ )
6447
				{
6448
					CyString line(lines->at(j));
6449
					if ( line[0] == '/' )
6450
						continue;
6451
					line.RemoveChar('\r');
6452
					line = line.RemoveFirstSpace();
6453
					line = line.RemoveEndSpace();
6454
					if ( line.Empty() )
6455
						continue;
6456
 
6457
 
6458
					// read the section, first entry is section, second is size
6459
					while ( !line.Empty() )
6460
					{
6461
						line = line.RemoveFirstSpace();
6462
						if ( line.Empty() )
6463
							break;
6464
 
6465
						if ( !insection && !insubsection )
6466
						{
6467
							CyString section = line.GetToken(";", 1, 1);
6468
							insection = line.GetToken(";", 2, 2).ToInt();
6469
 
6470
							// search for the sections
6471
							currentSection = NULL;
6472
							for ( CListNode<SComponantEntry> *node = dummyList.Front(); node; node = node->next() )
6473
							{
6474
								SComponantEntry *d = node->Data();
6475
								if ( d->sSection.Compare(section) )
6476
								{
6477
									currentSection = node->Data();
6478
									break;
6479
								}
6480
							}
6481
 
6482
							if ( !currentSection )
6483
							{
6484
								currentSection = new SComponantEntry;
6485
								currentSection->sSection = section;
6486
								dummyList.push_back(currentSection);
6487
							}
6488
 
6489
							// we have some more ?
6490
							line = line.DelToken(";", 1, 2);
6491
						}
6492
						else if ( !insubsection )
6493
						{
6494
							--insection;
6495
							CyString section = line.GetToken(";", 1, 1);
6496
							insubsection = line.GetToken(";", 2, 2).ToInt();
6497
 
6498
							currentSubSection = new SComponantEntry2;
6499
							currentSubSection->sSection = section;
6500
							currentSection->lEntries.push_back(currentSubSection);
6501
 
6502
							line = line.DelToken(";", 1, 2);
6503
						}
6504
						else
6505
						{
6506
							--insubsection;
6507
							currentSubSection->lEntries.PushBack(line.Remove(' '), true);
6508
							line = "";
6509
						}
6510
					}
6511
				}
6512
 
6513
				delete lines;
6514
			}
6515
 
6516
			File.Remove();
6517
		}
6518
 
6519
		// add the new entries for the ships
6520
		for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6521
		{
6522
			SGameShip *s = node->Data();
6523
			if ( s->iType != WARETYPE_ADDED || !s->pPackage )
6524
				continue;
6525
 
6526
			// no dummies to add?
6527
			if ( !s->pPackage->AnyComponents() )
6528
				continue;
6529
 
6530
			// add each dummy to list
6531
			for ( CListNode<SComponent> *dNode = s->pPackage->GetComponents()->Front(); dNode; dNode = dNode->next() )
6532
			{
6533
				SComponent *dummy = dNode->Data();
6534
				SComponantEntry *found = NULL;
6535
				SComponantEntry2 *found2 = NULL;
6536
				for ( CListNode<SComponantEntry> *eNode = dummyList.Front(); eNode; eNode = eNode->next() )
6537
				{
6538
					if ( eNode->Data()->sSection.Compare(dummy->sSection) )
6539
					{
6540
						found = eNode->Data();
6541
						break;
6542
					}
6543
				}
6544
				if ( !found )
6545
				{
6546
					found = new SComponantEntry;
6547
					found->sSection = dummy->sSection;
6548
					dummyList.push_back(found);
6549
					found2 = new SComponantEntry2;
6550
					found2->sSection = dummy->sSection2;
6551
					found->lEntries.push_back(found2);
6552
				}
6553
				// else check for the 2nd section
6554
				else
6555
				{
6556
					for ( CListNode<SComponantEntry2> *cNode = found->lEntries.Front(); cNode; cNode = cNode->next() )
6557
					{
6558
						if ( cNode->Data()->sSection.Compare(dummy->sSection2) )
6559
						{
6560
							found2 = cNode->Data();
6561
							break;
6562
						}
6563
					}
6564
 
6565
					if ( !found2 )
6566
					{
6567
						found2 = new SComponantEntry2;
6568
						found2->sSection = dummy->sSection2;
6569
						found->lEntries.push_back(found2);
6570
					}
6571
					else
6572
					{
6573
						bool f = false;
6574
						for ( SStringList *strNode = found2->lEntries.Head(); strNode; strNode = strNode->next )
6575
						{
6576
							if ( dummy->sData.Remove(' ').Compare(strNode->str) )
6577
							{
6578
								f = true;
6579
								break;
6580
							}
6581
						}
6582
 
6583
						if ( f )
6584
							continue;
6585
					}
6586
				}
6587
 
6588
				found2->lEntries.PushBack(dummy->sData.Remove(' '));
6589
			}
6590
		}
6591
 
6592
		// finally, write the file
6593
		std::vector<CyString> lines;
6594
		lines.push_back(CyString("// Components file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2));
6595
		for ( SComponantEntry *dummy = dummyList.First(); dummy; dummy = dummyList.Next() )
6596
		{
6597
			lines.push_back("");
6598
			lines.push_back(CyString("// Section: ") + dummy->sSection + " Entries: " + (long)dummy->lEntries.size());
6599
			lines.push_back(dummy->sSection + ";" + CyString::Number(dummy->lEntries.size()) + ";");
6600
			for ( CListNode<SComponantEntry2> *comp = dummy->lEntries.Front(); comp; comp = comp->next() )
6601
			{
6602
				lines.push_back(comp->Data()->sSection + ";" + (long)comp->Data()->lEntries.Count() + ";");
6603
				for ( SStringList *str = comp->Data()->lEntries.Head(); str; str = str->next )
6604
				{
6605
					CyString cStr = str->str;
6606
					cStr.RemoveEndSpace();
6607
					cStr.RemoveChar(9);
6608
					cStr.RemoveChar('\r');
6609
					if ( cStr.Right(1) != ";" )
6610
						cStr += ";";
6611
					lines.push_back(cStr);
6612
				}
6613
			}
6614
		}
6615
 
6616
		// write the file to disk
6617
		CFileIO WriteFile(m_sTempDir + "/Components.txt");
6618
		if ( WriteFile.WriteFile(&lines) )
6619
		{
6620
			this->PackFile(&WriteFile, "types\\Components.pck");
6621
			WriteFile.Remove();
6622
		}
6623
	}
6624
}
6625
 
6626
bool CPackages::ReadGlobals(CyStringList &globals)
6627
{
6628
	int e = ExtractGameFile("types/Globals.pck", m_sTempDir);
6629
	if ( e )
6630
	{
6631
		CFileIO File(((e == -1) ? "." : m_sTempDir) + "/Globals.txt");
6632
		if ( File.Exists() )
6633
		{
6634
			CyStringList *lines = File.ReadLinesStr();
6635
			if ( lines )
6636
			{
6637
				int entries = -1;
6638
				for ( SStringList *str = lines->Head(); str; str = str->next )
6639
				{
6640
					str->str.RemoveChar('\r');
6641
					str->str.RemoveChar(9);
6642
					str->str.RemoveFirstSpace();
6643
 
6644
					if ( str->str.Empty() )
6645
						continue;
6646
					if ( str->str[0] == '/' )
6647
						continue;
6648
 
6649
					// remove comments
6650
					CyString l = str->str;
6651
					if ( l.IsIn("/") ) 
6652
						l = l.GetToken("/", 1, 1);
6653
 
6654
					if ( entries == -1 )
6655
						entries = l.GetToken(";", 1, 1).ToInt();
6656
					else
6657
						globals.PushBack(l.GetToken(";", 1, 1), l.GetToken(";", 2, 2));
6658
				}
6659
 
6660
				delete lines;
6661
 
6662
				return true;
6663
			}
6664
		}
6665
	}
6666
 
6667
	return false;
6668
}
6669
 
6670
void CPackages::CreateGlobals()
6671
{
6672
	if ( m_lGlobals.Empty() )
6673
		return; // no global settings
6674
 
6675
	CyStringList globals;
6676
	if ( ReadGlobals(globals) )
6677
	{
6678
		// apply out settings
6679
		for ( SStringList *str = m_lGlobals.Head(); str; str = str->next )
6680
		{
6681
			SStringList *found = globals.FindString(str->str);
6682
			if ( found )
6683
				found->data = str->data;
6684
		}
6685
 
6686
		// now write it
6687
		CyStringList writeList;
6688
		for ( SStringList *str = globals.Head(); str; str = str->next )
6689
			writeList.PushBack(str->str + ";" + str->data + ";");
6690
 
6691
		// finally, write the file
6692
		writeList.PushFront(CyString::Number(writeList.Count()) + "; /globals amount", "");
6693
		writeList.PushFront(CyString("// Globals file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
6694
 
6695
		CFileIO WriteFile(m_sTempDir + "/Globals.txt");
6696
		if ( WriteFile.WriteFile(&writeList) )
6697
		{
6698
			this->PackFile(&WriteFile, "types/Globals.pck");
6699
			WriteFile.Remove();
6700
		}
6701
	}
6702
}
6703
 
6704
void CPackages::CreateTShips()
6705
{
6706
	// no ships ?
6707
	if ( m_lGameShips.empty() )
6708
		return;
6709
 
6710
	// get the cockpit list to match with ships turrets
6711
	CyStringList Cockpits;
6712
	CyStringList *cockpitList = this->CreateCockpits();
6713
	if ( cockpitList )
6714
	{
6715
		for ( SStringList *str = cockpitList->Head(); str; str = str->next )
6716
		{
6717
			CyString id = str->str.GetToken(";", 19, 19);
6718
			Cockpits.PushBack(id);
6719
		}
6720
 
6721
		delete cockpitList;
6722
	}
6723
 
6724
	CLinkList<SGameShip> shipOverrides;
6725
	for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6726
	{
6727
		if ( node->Data()->iType != WARETYPE_ADDED || !node->Data()->pPackage )
6728
			continue;		
6729
		if ( !node->Data()->pPackage->IsExistingShip() )
6730
			continue;
6731
		shipOverrides.push_back(node->Data());
6732
	}
6733
 
6734
	// read the existing tships file
6735
	int e = ExtractGameFile("types/TShips.pck", m_sTempDir + "/TShips.txt");
6736
	if ( e )
6737
	{
6738
		int fileType = 51;
6739
		CyStringList tshipsList;
6740
 
6741
		// if we have no buffer, lets create one
6742
		CFileIO File;
6743
		if ( File.Open((e == -1) ? "TShips.txt" : m_sTempDir + "/TShips.txt") )
6744
		{
6745
			int shiptext = SHIPSTARTTEXT;
6746
 
6747
			std::vector<CyString> *lines = File.ReadLines();
6748
			if ( lines )
6749
			{
6750
				int count = -1;
6751
				for ( int j = 0; j < (int)lines->size(); j++ )
6752
				{
6753
					CyString line(lines->at(j));
6754
					if ( line[0] == '/' )
6755
						continue;
6756
					line.RemoveChar('\r');
6757
					line.RemoveChar(9);
6758
					line = line.RemoveFirstSpace();
6759
					line = line.RemoveEndSpace();
6760
					if ( line.Empty() )
6761
						continue;
6762
 
6763
					if ( count == -1 )
6764
					{
6765
						fileType = line.GetToken(";", 1, 1).ToInt();
6766
						count = line.GetToken(";", 2, 2).ToInt();
6767
					}
6768
					else
6769
					{
6770
						if ( line.Right(1) != ";" )
6771
							line += ";";
6772
 
6773
						// check for any ship overrides
6774
						bool added = false;
6775
						if ( !shipOverrides.empty() )
6776
						{
6777
							CShipData shipData;
6778
							if ( shipData.ReadShipData(line) )
6779
							{
6780
								for ( CListNode<SGameShip> *node = shipOverrides.Front(); node; node = node->next() )
6781
								{
6782
									SGameShip *s = node->Data();
6783
									if ( !s->pPackage->GetShipID().Compare(shipData.sID) )
6784
										continue;
6785
									s->iText = shiptext;
6786
									if ( !s->pPackage->GetOriginalDescription() )
6787
										shiptext += 2;
6788
									s->iPos = tshipsList.Count();
6789
									added = true;
6790
									tshipsList.PushBack(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame));
6791
									shipOverrides.remove(node);
6792
									break;
6793
								}
6794
							}
6795
						}
6796
 
6797
						if ( !added )
6798
							tshipsList.PushBack(line);
6799
						--count;
6800
						if ( count < 0 )
6801
							break;
6802
					}
6803
				}
6804
 
6805
				delete lines;
6806
 
6807
			}
6808
 
6809
			File.Remove();
6810
 
6811
			// assign the ship buffer
6812
			if ( !m_iShipBuffer )
6813
				m_iShipBuffer = tshipsList.Count() + 15;
6814
			// there seems to be too many additional entries, we have no choise but to change the buffer
6815
			else if ( m_iShipBuffer <= tshipsList.Count() )
6816
				m_iShipBuffer = tshipsList.Count() + 15;
6817
 
6818
			CyString bufferStart;
6819
			if ( m_iGame == GAME_X3 )
6820
				bufferStart = "0;0;0;0;0;2;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;2092;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
6821
			else
6822
				bufferStart = "0;0;0;0;0;SG_SH_M5;499999;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0.049988;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;OBJ_BEACON;1;1;-1;0;0;1;1;0;1;1;1;0;0;0;;-1;0;0;0;0;0;0;0;0;0;";
6823
			// add the buffers now
6824
			for ( int i = tshipsList.Count(); i < m_iShipBuffer; i++ )
6825
				tshipsList.PushBack(bufferStart + "SHIP_BUFFER;");
6826
 
6827
			// now lets add our tships line
6828
			for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
6829
			{
6830
				SGameShip *s = node->Data();
6831
				if ( s->pPackage && s->pPackage->IsExistingShip() )
6832
					continue;
6833
				s->iPos = tshipsList.Count();
6834
				if ( s->iType == WARETYPE_ADDED && s->pPackage )
6835
				{
6836
					s->iText = shiptext;
6837
					if ( !s->pPackage->GetOriginalDescription() )
6838
						shiptext += 2;
6839
 
6840
					tshipsList.PushBack(s->pPackage->FormatShipData(&Cockpits, &s->iText, m_iGame));
6841
				}
6842
				else if ( s->iType == WARETYPE_DELETED )
6843
					tshipsList.PushBack(bufferStart + "SHIP_DELETED;");
6844
				else if ( s->iType == WARETYPE_DISABLED )
6845
					tshipsList.PushBack(bufferStart + "SHIP_DISABLED;");
6846
				else
6847
					tshipsList.PushBack(bufferStart + "SHIP_SPACER;");
6848
			}
6849
 
6850
			// finally, write the file
6851
			tshipsList.PushFront(CyString::Number(fileType) + ";" + CyString::Number(tshipsList.Count()) + ";", "");
6852
			tshipsList.PushFront(CyString("// TShips file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
6853
 
6854
			CFileIO WriteFile(m_sTempDir + "/TShips.txt");
6855
			if ( WriteFile.WriteFile(&tshipsList) )
6856
			{
6857
				this->PackFile(&WriteFile, "types/TShips.pck");
6858
				WriteFile.Remove();
6859
			}
6860
		}
6861
	}
6862
 
6863
}
6864
 
6865
bool CPackages::PackFile(CyString filename)
6866
{
6867
	// compress the file
6868
	CFileIO File(filename);
6869
	size_t fileSize;
6870
	char *fileData = File.ReadToData(&fileSize);
6871
 
6872
	if ( fileData && fileSize)
6873
	{
6874
		size_t newFileSize;
6875
		unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
6876
		if ( pckData )
6877
		{
6878
			CyString ext = "pck";
6879
			if ( File.CheckFileExtension("bob") )
6880
				ext = "pbb";
6881
			else if ( File.CheckFileExtension("bod") )
6882
				ext = "pbd";
6883
			CFileIO pckFile(File.ChangeFileExtension(ext));
6884
			if ( !CDirIO(pckFile.GetDir()).Exists() )
6885
				CDirIO(pckFile.GetDir()).Create();
6886
			pckFile.WriteData((char *)pckData, newFileSize);
6887
			return true;
6888
		}
6889
	}
6890
 
6891
	return false;
6892
}
6893
 
6894
bool CPackages::UnPackFile(CyString filename, bool checkxml)
6895
{
6896
	// compress the file
6897
	CFileIO File(filename);
6898
	size_t fileSize;
6899
	char *fileData = File.ReadToData(&fileSize);
6900
 
6901
	if ( fileData && fileSize)
6902
	{
6903
		size_t newFileSize;
6904
		unsigned char *pckData = UnPCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
6905
		if ( pckData )
6906
		{
6907
			CyString ext = "txt";
6908
			if ( File.CheckFileExtension("pbb") )
6909
				ext = "bob";
6910
			else if ( File.CheckFileExtension("pbd") )
6911
				ext = "bod";
6912
			CFileIO pckFile(File.ChangeFileExtension(ext));
6913
			if ( !CDirIO(pckFile.GetDir()).Exists() )
6914
				CDirIO(pckFile.GetDir()).Create();
6915
			pckFile.WriteData((char *)pckData, newFileSize);
6916
 
6917
			// check for xml and rename
6918
			if ( checkxml )
6919
			{
6920
				int readmaxlines = 20;
6921
				bool isxml = false;
6922
				do {
6923
					CyString line = pckFile.ReadToEndLine(true);
6924
					if ( line.IsIn("<language id=") )
6925
					{
6926
						isxml = true;
6927
						break;
6928
					}
6929
					else if ( line.IsIn("<page id=") )
6930
					{
6931
						isxml = true;
6932
						break;
6933
					}
6934
					else if ( line.IsIn("<?xml") || line.IsIn("<script>") )
6935
					{
6936
						isxml = true;
6937
						break;
6938
					}
6939
					--readmaxlines;
6940
					if ( readmaxlines <= 0 )
6941
						break;
6942
				} while (pckFile.IsOpened());
6943
 
6944
				if ( pckFile.IsOpened() )
6945
					pckFile.StopRead();
6946
 
6947
				if ( isxml )
6948
					pckFile.Rename(pckFile.ChangeFileExtension("xml"));
6949
			}
6950
 
6951
			return true;
6952
		}
6953
	}
6954
 
6955
	return false;
6956
}
6957
 
6958
bool CPackages::PackFile(CFileIO *File, CyString filename)
6959
{
6960
	filename = filename.FindReplace("\\", "/");
6961
	if ( m_iGame == GAME_X3 )
6962
	{
6963
		CCatFile catFile;
6964
		int error = catFile.Open(m_sCurrentDir + "/mods/PluginManager.cat", this->GetAddonDir(), CATREAD_CATDECRYPT, true);
6965
		if ( error == CATERR_NONE || error == CATERR_CREATED )
6966
		{
6967
			// it it wrote ok, remove the old ones
6968
			if ( !catFile.AppendFile(File->GetFullFilename(), filename, true, true) )
6969
				return false;
6970
			return true;
6971
		}
6972
	}
6973
	else
6974
	{
6975
		// compress the file
6976
		size_t fileSize;
6977
		char *fileData = CFileIO(File->GetFullFilename()).ReadToData(&fileSize);
6978
 
6979
		if ( fileData && fileSize)
6980
		{
6981
			size_t newFileSize;
6982
			unsigned char *pckData = PCKData((unsigned char *)fileData, fileSize, &newFileSize, true);
6983
			if ( pckData )
6984
			{
6985
//				if ( !this->GetAddonDir().Empty() && CCatFile::IsAddonDir(filename) )
6986
//					filename = this->GetAddonDir() + "/" + filename;
6987
				CFileIO pckFile(m_sCurrentDir + "/" + filename);
6988
				if ( !CDirIO(pckFile.GetDir()).Exists() )
6989
					CDirIO(pckFile.GetDir()).Create();
6990
				pckFile.WriteData((char *)pckData, newFileSize);
6991
				this->AddCreatedFile(pckFile.GetFullFilename());
6992
				return true;
6993
			}
6994
		}
6995
	}
6996
 
6997
	return false;
6998
}
6999
 
7000
CyStringList *CPackages::CreateCockpits()
7001
{
7002
	// first check we have any ships
7003
	if ( m_lGameShips.empty() || !this->CountPackages(TYPE_XSP, true) )
7004
		return NULL;
7005
 
7006
	CyStringList *cockpitList = new CyStringList;
7007
 
7008
	// now extract the existing cockpits
7009
	int fileType = 51;
7010
	int e = ExtractGameFile("types/TCockpits.pck", m_sTempDir + "/TCockpits.txt");
7011
	if ( e )
7012
	{
7013
		// read the dummies
7014
		CFileIO File;
7015
		if ( File.Open((e == -1) ? "TCockpits.txt" : m_sTempDir + "/TCockpits.txt") )
7016
		{
7017
			CyStringList *lines = File.ReadLinesStr();
7018
			if ( lines )
7019
			{
7020
				int count = -1;
7021
				for ( SStringList *str = lines->Head(); str; str = str->next )
7022
				{
7023
					CyString line(str->str);
7024
					line.RemoveChar('\r');
7025
					line.RemoveChar(9);
7026
					line = line.RemoveFirstSpace();
7027
					line = line.RemoveEndSpace();
7028
					if ( line.Empty() )
7029
						continue;
7030
					if ( line[0] == '/' )
7031
						continue;
7032
 
7033
					if ( count == -1 )
7034
					{
7035
						fileType = line.GetToken(";", 1, 1).ToInt();
7036
						count = line.GetToken(";", 2, 2).ToInt();
7037
					}
7038
					else
7039
					{
7040
						while ( !line.Empty() )
7041
						{
7042
							CyString data = line.GetToken(";", 1, 19);
7043
							cockpitList->PushBack(data + ";");
7044
							line = line.DelToken(";", 1, 19);
7045
 
7046
							--count;
7047
							if ( count < 1 )
7048
								break;
7049
						}
7050
					}
7051
				}
7052
 
7053
				delete lines;
7054
			}
7055
 
7056
			File.Remove();
7057
		}
7058
 
7059
		// now add the new ones
7060
		for ( CListNode<SGameShip> *node = m_lGameShips.Front(); node; node = node->next() )
7061
		{
7062
			SGameShip *s = node->Data();
7063
			if ( s->iType != WARETYPE_ADDED || !s->pPackage )
7064
				continue;
7065
 
7066
			if ( !s->pPackage->AnyCockpits() )
7067
				continue;
7068
 
7069
			bool found = false;
7070
			for ( CListNode<SCockpit> *cn = s->pPackage->GetCockpits()->Front(); cn; cn = cn->next() )
7071
			{
7072
				bool foundEntry = false;
7073
				CyString cockpitStr = cn->Data()->sCockpit;
7074
				// search for matching game entry
7075
				for ( CListNode<SWeaponMask> *wm = cn->Data()->lWeaponMask.Front(); wm; wm = wm->next() )
7076
				{
7077
					if ( wm->Data()->iGame == (m_iGame - 1) )
7078
					{
7079
						if ( wm->Data()->iMask != -1 )
7080
							cockpitStr = cockpitStr.RepToken(";", 9, CyString::Number(wm->Data()->iMask));
7081
						foundEntry = true;
7082
						break;
7083
					}
7084
				}
7085
				for ( SStringList *str = cockpitList->Head(); str; str = str->next )
7086
				{
7087
					if ( str->str.GetToken(";", 19, 19).Compare(cn->Data()->sCockpit.GetToken(";", 19, 19)) )
7088
					{
7089
						// only replace existing entry if we have sepeperate weapon masks set
7090
						if ( foundEntry )
7091
							str->str = cockpitStr;
7092
						found = true;
7093
						break;
7094
					}
7095
				}
7096
 
7097
				if ( !found )
7098
					cockpitList->PushBack(cockpitStr);
7099
			}
7100
		}
7101
 
7102
		// finally, write the file
7103
		cockpitList->PushFront(CyString::Number(fileType) + ";" + CyString::Number(cockpitList->Count()) + ";", "");
7104
		cockpitList->PushFront(CyString("// TCockpits file, created by SPK Libraries V") + CyString::CreateFromFloat(GetLibraryVersion(), 2), "");
7105
 
7106
		CFileIO WriteFile(m_sTempDir + "/TCockpits.txt");
7107
		if ( WriteFile.WriteFile(cockpitList) )
7108
		{
7109
			this->PackFile(&WriteFile, "types\\TCockpits.pck");
7110
			WriteFile.Remove();
7111
		}
7112
 
7113
		// remove those entrys
7114
		cockpitList->PopFront();
7115
		cockpitList->PopFront();
7116
	}
7117
 
7118
	return cockpitList;
7119
}
7120
 
7121
CBaseFile *CPackages::FindScriptByAuthor(CyString author, CBaseFile *prev)
7122
{
7123
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
7124
	{
7125
		CBaseFile *p = node->Data();
7126
		if ( prev )
7127
		{
7128
			if ( p == prev )
7129
				prev = NULL;
7130
			continue;
7131
		}
7132
		if ( p->GetAuthor().Compare(author) )
7133
			return p;
7134
	}
7135
 
7136
	return NULL;
7137
}
7138
 
7139
CBaseFile *CPackages::LoadPackagerScript(CyString filename, int compression, CyString (*askFunc)(CyString), CyStringList *malformedLines, CyStringList *unknownCommands, CyStringList *variables)
7140
{
7141
	// check the file exists
7142
	if ( !CFileIO(filename).Exists() )
7143
		return NULL;
7144
 
7145
	// read all the lines
7146
	CFileIO File(filename);
7147
	std::vector<CyString> *lines = File.ReadLines();
7148
 
7149
	if ( !lines )
7150
		return NULL;
7151
 
7152
	CBaseFile *package = NULL;
7153
 
7154
	// filter out blank lines and comments
7155
	CyStringList fileData;
7156
	for ( int i = 0; i < (int)lines->size(); i++ )
7157
	{
7158
		CyString line(lines->at(i));
7159
 
7160
		// dont include empty lines (whitespace)
7161
		if ( line.Empty() )
7162
			continue;
7163
 
7164
		// filter out any spaces, tabs in front
7165
		line.RemoveChar('\t');
7166
		line.RemoveChar('\r');
7167
		CyString linenospace = line;
7168
		linenospace.RemoveFirstSpace();
7169
		if ( linenospace.Empty() )
7170
			continue;
7171
 
7172
		// check for any comments
7173
		if ( linenospace.Left(2) == "//" )
7174
			continue;
7175
		if ( linenospace[0] == '#' )
7176
			continue;
7177
 
7178
		// all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
7179
		if ( !line.IsIn(':') )
7180
		{
7181
			// there are some exeptions, and these are one word entrys only
7182
			line.RemoveEndSpace();
7183
			if ( line.IsIn(" ") )
7184
			{
7185
				if ( malformedLines )
7186
					malformedLines->PushBack(line, CyString::Number(i));
7187
				continue;
7188
			}
7189
		}
7190
 
7191
		// check for the type line
7192
		if ( !package && line.GetToken(":", 1, 1).Compare("FileType") )
7193
		{
7194
			CyString sFileType = line.GetToken(":", 2).RemoveFirstSpace();
7195
			if ( sFileType.Compare("Ship") )
7196
				package = new CXspFile();
7197
			else if ( sFileType.Compare("Script") )
7198
				package = new CSpkFile();
7199
			else if ( sFileType.Compare("Base") )
7200
				package = new CBaseFile();
7201
		}
7202
 
7203
		fileData.PushBack(line, CyString::Number(i));
7204
	}
7205
 
7206
	delete lines;
7207
 
7208
	// assume its a script if no type is set (all old versions are scripts)
7209
	if ( !package )
7210
		package = new CSpkFile();
7211
 
7212
	if ( compression != -1 )
7213
		package->SetDataCompression(compression);
7214
 
7215
	CyString ftpaddr;
7216
	CyString ftpuser;
7217
	CyString ftppass;
7218
	CyString ftpdir;
7219
	// now lets read the rest of the day
7220
	CyStringList listVaribles;
7221
	for ( SStringList *strNode = fileData.Head(); strNode; strNode = strNode->next )
7222
	{
7223
		CyString first = strNode->str.GetToken(":", 1, 1);
7224
		CyString rest = strNode->str.GetToken(":", 2).RemoveFirstSpace();
7225
 
7226
		if ( first.Compare("Varible") || first.Compare("Variable") )
7227
			listVaribles.PushBack(rest.GetToken(" ", 1, 1), rest.GetToken(" ", 2), true);
7228
		else
7229
		{
7230
			// replace variables
7231
			if ( rest.IsIn("$") )
7232
			{
7233
				for ( SStringList *strVar = listVaribles.Head(); strVar; strVar = strVar->next )
7234
				{
7235
					if ( rest.IsIn(strVar->str) )
7236
						rest.FindReplace(strVar->str, strVar->data);
7237
				}
7238
 
7239
				if ( variables )
7240
				{
7241
					for ( SStringList *strVar = variables->Head(); strVar; strVar = strVar->next )
7242
					{
7243
						if ( rest.IsIn(strVar->str) )
7244
							rest.FindReplace(strVar->str, strVar->data);
7245
					}
7246
				}
7247
			}
7248
 
7249
			//check for the built in varibles
7250
			if ( rest.IsIn("$ASK") )
7251
			{
7252
				CyString replace = "$ASK";
7253
				CyString result;
7254
 
7255
				if ( askFunc )
7256
					result = askFunc(first);
7257
 
7258
				if ( rest.IsIn("$ASK(") )
7259
				{
7260
					replace = rest.GetToken("$ASK(", 2).GetToken(")", 1, 1);
7261
					if ( result.Empty() )
7262
						result = replace;
7263
					replace = CyString("$ASK(") + replace + ")";
7264
				}
7265
 
7266
 
7267
				if ( !result.Empty() )
7268
					rest.FindReplace(replace, result);
7269
			}
7270
			// todays date
7271
			if ( rest.IsIn("$DATE") )
7272
			{
7273
				time_t now;
7274
				time(&now);
7275
				struct tm *timeinfo = localtime(&now);
7276
				CyString result = CyString::Number(timeinfo->tm_mday) + "." + CyString::Number(timeinfo->tm_mon + 1) + "." + CyString::Number(timeinfo->tm_year + 1900);
7277
				if ( !result.Empty() )
7278
					rest.FindReplace("$DATE", result);
7279
			}
7280
			// mydocuments
7281
			if ( rest.IsIn("$MYDOCUMENTS") )
7282
			{
7283
				if ( !m_sMyDoc.Empty() )
7284
					rest.FindReplace("$MYDOCUMENTS", m_sMyDoc);
7285
			}
7286
 
7287
			// current path
7288
			if ( rest.IsIn("$PATH") )
7289
			{
7290
				CyString currentDir = CFileIO(filename).GetDir();
7291
				if ( !currentDir.Empty() )
7292
					rest.FindReplace("$PATH", currentDir);
7293
			}
7294
 
7295
			// now parse the rest of the values
7296
			if ( first.Compare("FtpUpload") )
7297
				ftpaddr = rest.GetToken(" ", 1, 1) + ":" + rest.GetToken(" ", 2, 2);
7298
			else if ( first.Compare("FtpUser") )
7299
				ftpuser = rest;
7300
			else if ( first.Compare("FtpPass") )
7301
				ftppass = rest;
7302
			else if ( first.Compare("FtpDir") )
7303
				ftpdir = rest;
7304
			else if ( !package->LoadPackageData(first, rest) )
7305
			{
7306
				if ( unknownCommands )
7307
					unknownCommands->PushBack(first, rest);
7308
			}
7309
		}
7310
	}
7311
 
7312
	if ( package->GetFilename().Empty() )
7313
		package->LoadPackageData("AutoSave", "$AUTOSAVE");
7314
 
7315
	if ( !ftpaddr.Empty() )
7316
	{
7317
		if ( !ftpuser.Empty() )
7318
		{
7319
			if ( !ftppass.Empty() )
7320
				ftpaddr = ftpuser + ":" + ftppass + "@" + ftpaddr;
7321
			else
7322
				ftpaddr = ftpuser + "@" + ftpaddr;
7323
		}
7324
 
7325
		if ( !ftpdir.Empty() )
7326
			ftpaddr += ftpdir;
7327
 
7328
		package->SetFtpAddr(ftpaddr);
7329
	}
7330
 
7331
	return package;
7332
}
7333
 
7334
CyString CPackages::GetLanguageName()
7335
{
7336
	return CPackages::ConvertLanguage(m_iLanguage);
7337
}
7338
 
7339
CyString CPackages::ConvertLanguage(int lang)
7340
{
7341
	switch ( lang )
7342
	{
7343
		case 44:
7344
			return "English";
7345
		case 49:
7346
			return "German";
7347
		case 7:
7348
			return "Russian";
7349
		case 33:
7350
			return "French";
7351
		case 30:
7352
			return "Greek";
7353
		case 31:
7354
			return "Dutch";
7355
		case 32:
7356
			return "Belgian";
7357
		case 34:
7358
			return "Spanish";
7359
		case 36:
7360
			return "Hungarian";
7361
		case 39:
7362
			return "Italian";
7363
		case 40:
7364
			return "Romanian";
7365
		case 41:
7366
			return "Swiss";
7367
		case 42:
7368
			return "Czech";
7369
		case 43:
7370
			return "Austrian";
7371
		case 45:
7372
			return "Danish";
7373
		case 46:
7374
			return "Swedish";
7375
		case 47:
7376
			return "Norweigen";
7377
		case 48:
7378
			return "Polish";
7379
	}
7380
 
7381
	return CyString::Number(lang);
7382
}
7383
 
7384
bool CPackages::CheckAccessRights(CyString dir)
7385
{
7386
	if ( dir.Empty() )
7387
		dir = m_sCurrentDir;
7388
 
7389
	// write a file, then read the contents
7390
	CFileIO File(dir + "/accessrightscheck.dat");
7391
 
7392
	// check if file exists and remove it
7393
	if ( File.Exists() )
7394
	{
7395
		// if we cant remove it, we dont have enough rights
7396
		if ( !File.Remove() )
7397
			return false;
7398
 
7399
		// if its still there, we dont have enough rights
7400
		if ( File.Exists() )
7401
			return false;
7402
	}
7403
 
7404
	// now create the file
7405
	if ( !File.WriteString("testing access rights") )
7406
		return false;
7407
 
7408
	// now check it exists
7409
	if ( !File.Exists() )
7410
		return false;
7411
 
7412
	// now read the file for the correct contents
7413
	CyStringList *lines = File.ReadLinesStr();
7414
	if ( !lines )
7415
		return false;
7416
 
7417
	// check that one of the lines is correct
7418
	for ( SStringList *s = lines->Head(); s; s = s->next )
7419
	{
7420
		if ( s->str == "testing access rights" )
7421
		{
7422
			delete lines;
7423
			return true;
7424
		}
7425
	}
7426
 
7427
	delete lines;
7428
 
7429
	return false;
7430
}
7431
 
7432
bool CPackages::LoadShipData(CyString file, CyStringList *list)
7433
{
7434
	CFileIO File;
7435
	bool deleteFile = false;
7436
 
7437
	// load from cat file
7438
	if ( CFileIO(file).CheckFileExtension("cat") )
7439
	{
7440
		CCatFile cat;
7441
		if ( cat.Open(file, this->GetAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
7442
			return false;
7443
 
7444
		if ( !cat.ExtractFile("types\\TShips.pck", m_sTempDir + "/tships.txt") )
7445
			return false;
7446
 
7447
		File.Open(m_sTempDir + "/tships.txt");
7448
		deleteFile = true;
7449
	}
7450
	// otherwise its a normal file
7451
	else if ( CFileIO(file).CheckFileExtension("pck") )
7452
	{
7453
		C_File f(file);
7454
		if ( !f.ReadFromFile() )
7455
			return false;
7456
		f.UnPCKFile();
7457
 
7458
		f.SetFilename(m_sTempDir + "/tships.txt");
7459
		if ( !f.WriteFilePointer() )
7460
			return false;
7461
 
7462
		File.Open(m_sTempDir + "/tships.txt");
7463
		deleteFile = true;
7464
	}
7465
	else
7466
		File.Open(file);
7467
 
7468
	if ( !File.Exists() )
7469
		return false;
7470
 
7471
	bool ret = false;
7472
	CyStringList *lines = File.ReadLinesStr();
7473
	if ( lines )
7474
	{
7475
		bool readFirst = false;
7476
		for ( SStringList *str = lines->Head(); str; str = str->next )
7477
		{
7478
			if ( str->str.Empty() )
7479
				continue;
7480
			str->str.RemoveChar('\r');
7481
			str->str.RemoveChar(9);
7482
			str->str.RemoveFirstSpace();
7483
			if ( str->str.Empty() )
7484
				continue;
7485
			if ( str->str[0] == '/' || str->str[0] == '#' )
7486
				continue;
7487
 
7488
			if ( !readFirst )
7489
				readFirst = true;
7490
			else
7491
			{
7492
				CyString t = str->str.GetToken(";", -2);
7493
				while ( t.Right(1) == ";" )
7494
					t.Truncate((int)t.Length() - 1);
7495
				list->PushBack(t, str->str);
7496
			}
7497
		}
7498
 
7499
		delete lines;
7500
		ret = true;
7501
	}
7502
 
7503
	if ( deleteFile )
7504
		File.Remove();
7505
 
7506
	return ret;
7507
}
7508
 
7509
CyString CPackages::ReadShipData(CyString file, CyString id)
7510
{
7511
	CyStringList *list = this->LoadShipData(file);
7512
	if ( !list )
7513
		return NullString;
7514
 
7515
	CShipData data;
7516
	for ( SStringList *str = list->Head(); str; str = str->next )
7517
	{
7518
		if ( str->str.Compare(id) )
7519
		{
7520
			delete list;
7521
			return str->data;
7522
		}
7523
	}
7524
 
7525
	delete list;
7526
	return NullString;
7527
}
7528
 
7529
CyStringList *CPackages::LoadShipData(CyString file)
7530
{
7531
	CyStringList *list = new CyStringList;
7532
	if ( this->LoadShipData(file, list) )
7533
		return list;
7534
 
7535
	delete list;
7536
	return NULL;
7537
}
7538
 
7539
bool CPackages::ReadTextPage(CyString file, CyStringList *list, bool search, int page)
7540
{
7541
	CFileIO File;
7542
	bool deleteFile = false;
7543
 
7544
	// read all text files from mod
7545
	if ( CFileIO(file).CheckFileExtension("cat") )
7546
	{
7547
		bool done = false;
7548
 
7549
		CCatFile cat;
7550
		if ( cat.Open(file, this->GetAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
7551
			return false;
7552
 
7553
		// extract 1 at a time
7554
		for ( int i = 0; i < cat.GetNumFiles(); i++ )
7555
		{
7556
			SInCatFile *f = cat.GetFile(i);
7557
			CyString sF = f->sFile;
7558
			// is a text file
7559
			sF = sF.FindReplace("\\", "/");
7560
			if ( !sF.GetToken("/", 1, 1).Compare("t") )
7561
				continue;
7562
 
7563
			CyString baseFile = CFileIO(sF).GetBaseName();
7564
			// check language
7565
			int lang = 0;
7566
			if ( baseFile.FindPos("-L") != -1 ) // new language file
7567
				lang = baseFile.Right(3).ToInt();
7568
			else
7569
			{
7570
				baseFile.Truncate((int)baseFile.Length() - 4);
7571
				lang = baseFile.ToInt();
7572
			}
7573
 
7574
			if ( lang != m_iLanguage )
7575
				continue;
7576
 
7577
			// now extract and parse
7578
			if ( cat.ExtractFile(f->sFile, m_sTempDir + "/" + CFileIO(f->sFile).GetBaseName() + ".xml") )
7579
			{
7580
				if ( this->ReadTextPage(m_sTempDir + "/" + CFileIO(f->sFile).GetBaseName() + ".xml", list, search, page) )
7581
					done = true;
7582
			}
7583
		}
7584
 
7585
		return done;
7586
	}
7587
	// otherwise its a normal file
7588
	else if ( CFileIO(file).CheckFileExtension("pck") )
7589
	{
7590
		C_File f(file);
7591
		if ( !f.ReadFromFile() )
7592
			return false;
7593
		f.UnPCKFile();
7594
 
7595
		f.SetFilename(m_sTempDir + "/textfile.xml");
7596
		if ( !f.WriteFilePointer() )
7597
			return false;
7598
 
7599
		File.Open(m_sTempDir + "/textfile.xml");
7600
		deleteFile = true;
7601
	}
7602
	else
7603
		File.Open(file);
7604
 
7605
	if ( !File.Exists() )
7606
		return false;
7607
 
7608
	// open and read file
7609
	CyStringList *lines = File.ReadLinesStr();
7610
	if ( !lines )
7611
		return false;
7612
 
7613
	bool inPage = false;
7614
	for ( SStringList *str = lines->Head(); str; str = str->next )
7615
	{
7616
		// search for page
7617
		if ( !inPage )
7618
		{
7619
			if ( str->str.FindPos("<page") > -1 )
7620
			{
7621
				// find the page id
7622
				int pos = str->str.FindPos("\"");
7623
				if ( pos > -1 )
7624
				{
7625
					int endpos = str->str.FindPos("\"", pos + 1);
7626
					if ( endpos > -1 )
7627
					{
7628
						CyString p = str->str.Mid(pos + 2, endpos - pos - 1);
7629
						if ( p.Length() > 4 )
7630
							p = p.Right(4);
7631
						int checkPage = p.ToInt();
7632
						if ( checkPage == page )
7633
							inPage = true;
7634
					}
7635
				}
7636
			}
7637
 
7638
		}
7639
		// add each id
7640
		else
7641
		{
7642
			if ( str->str.FindPos("</page") > -1 )
7643
				break;
7644
 
7645
			if ( str->str.FindPos("<t id") > -1 )
7646
			{
7647
				int pos = str->str.FindPos("\"");
7648
				if ( pos > -1 )
7649
				{
7650
					int endpos = str->str.FindPos("\"", pos + 1);
7651
					if ( endpos > -1 )
7652
					{
7653
						int id = str->str.Mid(pos + 2, endpos - pos - 1).ToInt();
7654
						pos = str->str.FindPos(">", endpos);
7655
						if ( pos > -1 )
7656
						{
7657
							++pos;
7658
							endpos = str->str.FindPos("</", pos);
7659
							if ( endpos > -1 )
7660
							{
7661
								CyString text = str->str.Mid(pos + 1, endpos - pos);
7662
								while ( text.FindPos('(') != -1 && text.FindPos(')') != -1 )
7663
								{
7664
									int s = text.FindPos('(');
7665
									text = text.Erase(s, text.FindPos(')') - s + 1);
7666
								}
7667
								list->PushBack(CyString::Number(id), text, search);
7668
							}
7669
						}
7670
					}
7671
				}
7672
			}
7673
		}
7674
	}
7675
 
7676
	delete lines;
7677
 
7678
	return true;
7679
}
7680
 
7681
CyStringList *CPackages::ReadTextPage(CyString file, bool search, int page)
7682
{
7683
	CyStringList *list = new CyStringList;
7684
	if ( this->ReadTextPage(file, list, search, page) )
7685
		return list;
7686
 
7687
	delete list;
7688
	return NULL;
7689
}
7690
 
7691
int CPackages::AdjustFileType(CyString file, int filetype)
7692
{
7693
	CFileIO File(file);
7694
	CyString dir = File.GetDirIO().TopDir();
7695
	CyString basename = File.GetBaseName();
7696
 
7697
	// mod files
7698
	if ( File.CheckFileExtension("cat") || File.CheckFileExtension("dat") )
7699
		return FILETYPE_MOD;
7700
	// check for text files
7701
	if ( File.GetFilename().IsIn("-L") && File.GetFilename().Left(4).ToInt() )
7702
		return FILETYPE_TEXT;
7703
	if ( basename.Length() <= 4 && basename.IsNumber() && (File.CheckFileExtension("xml") || File.CheckFileExtension("pck")) )
7704
		return FILETYPE_TEXT;
7705
	// X2/X3 text file
7706
	if ( basename.Length() >= 5 && basename.Length() <= 8 && File.GetBaseName().ToInt() )
7707
		return FILETYPE_TEXT;
7708
	if ( filetype == FILETYPE_TEXT ) // should no longer be anything text
7709
		return FILETYPE_SCRIPT;
7710
	if ( File.CheckFileExtension("wav") || File.CheckFileExtension("mp3") )
7711
		return FILETYPE_SOUND;
7712
	return filetype;
7713
}
7714
 
7715
void CPackages::RemoveFailedFiles()
7716
{
7717
	for ( SStringList *str = m_lNonRemovedFiles.Head(); str; str = str->next )
7718
	{
7719
		if ( CFileIO(str->str).Remove() )
7720
			str->remove = true;
7721
	}
7722
 
7723
	m_lNonRemovedFiles.RemoveMarked();
7724
}
7725
 
7726
CXspFile *CPackages::ExtractShip(CyString catfile, CyString id, CProgressInfo *progress)
7727
{
7728
	CCatFile cat;
7729
	if ( cat.Open(catfile, this->GetAddonDir(), CATREAD_CATDECRYPT, false) != CATERR_NONE )
7730
		return NULL;
7731
 
7732
	CXspFile *newShip = new CXspFile;
7733
	if ( !newShip->ExtractShip(&cat, id, progress) )
7734
	{
7735
		delete newShip;
7736
		return NULL;
7737
	}
7738
 
7739
	return newShip;
7740
}
7741
 
7742
CyStringList *CPackages::GetMergedFiles(CCatFile *cat1, CCatFile *cat2)
7743
{
7744
	CyStringList *list = new CyStringList;
7745
 
7746
	// first add all files from the "primary" mod
7747
	for ( SInCatFile *f = cat1->GetFiles()->First(); f; f = cat1->GetFiles()->Next() )
7748
		list->PushBack(f->sFile.findreplace("\\", "/"), "1");
7749
 
7750
	// now add the ones from the secondary
7751
	for ( SInCatFile *f = cat2->GetFiles()->First(); f; f = cat2->GetFiles()->Next() )
7752
	{
7753
		CyString sFile = f->sFile.findreplace("\\", "/");
7754
		// if its found on the 2nd list, dont add, just adjust the type
7755
		SStringList *found = list->FindString(sFile);
7756
		if ( found )
7757
			found->data = "-1"; // found in both
7758
		else
7759
			list->PushBack(sFile, "2");
7760
	}
7761
 
7762
	return list;
7763
}
7764
 
7765
bool CPackages::CanWeMerge(CyString file)
7766
{
7767
	return CModDiff::CanBeDiffed(file);
7768
}
7769
 
7770
bool CPackages::NeedToMerge(CyString file)
7771
{
7772
	CyString firstDir = file.GetToken("/", 1, 1);
7773
	if ( firstDir.Compare("t") )
7774
		return true;
7775
	if ( firstDir.Compare("types") )
7776
		return true;
7777
	if ( firstDir.Compare("maps") )
7778
		return true;
7779
 
7780
	return false;
7781
}
7782
 
7783
bool CPackages::MergeMods(CCatFile *mod1, CCatFile *mod2, CyString outFile, CyStringList *cantMerge)
7784
{
7785
	CyStringList *list = this->GetMergedFiles(mod1, mod2);
7786
	if ( !list )
7787
		return false;
7788
 
7789
	CCatFile newCat;
7790
	if ( newCat.Open(outFile, this->GetAddonDir()) != CATERR_CREATED )
7791
		return false;
7792
 
7793
	// add all the files to the new mod first
7794
	CyStringList conflicts;
7795
	for ( SStringList *str = list->Head(); str; str = str->next )
7796
	{
7797
		int status = str->data.ToInt();
7798
		if ( status == 1 )
7799
		{
7800
			if ( !newCat.WriteFromCat(mod1, str->str) )
7801
			{
7802
				if ( cantMerge )
7803
					cantMerge->PushBack(str->str, "1");
7804
			}
7805
		}
7806
		else if ( status == 2 )
7807
		{
7808
			if ( !newCat.WriteFromCat(mod2, str->str) )
7809
			{
7810
				if ( cantMerge )
7811
					cantMerge->PushBack(str->str, "2");
7812
			}
7813
		}
7814
		else if ( status == -1 )
7815
		{
7816
			if ( this->NeedToMerge(str->str) )
7817
			{
7818
				if ( this->CanWeMerge(str->str) )
7819
					conflicts.PushBack(str->str);
7820
				else if ( cantMerge )
7821
					cantMerge->PushBack(str->str, "-1");
7822
			}
7823
			else
7824
			{
7825
				if ( !newCat.WriteFromCat(mod1, str->str) )
7826
				{
7827
					if ( cantMerge )
7828
						cantMerge->PushBack(str->str, "1");
7829
				}
7830
			}
7831
		}
7832
	}
7833
 
7834
	delete list;
7835
 
7836
	/* 
7837
		Merging Files
7838
 
7839
		* Text Files: Join all text entries into a single file (excluding page 17)
7840
		* Weapons: TBullets and TLaser (grab matching entrys from text files and adjust ids)
7841
	*/
7842
	// new merge the conflicting files
7843
	// first the text files
7844
//	CyStringList *text = this->MergeTextFiles(conflicts, mod1, mod2);
7845
//	delete text;
7846
 
7847
	// write the cat file when we're done
7848
	if ( !newCat.WriteCatFile() )
7849
		return false;
7850
 
7851
	return true;
7852
}
7853
 
7854
/**
7855
 * Gets the file list from a mod that might have compatability problems
7856
 *
7857
 * This includes all types and text files
7858
 *
7859
 * Returns true if it finds any files
7860
 */
7861
bool CPackages::GetModCompatabilityList(C_File *file, CyStringList *list)
7862
{
7863
	// not a valid file
7864
	if ( !file ) return false;
7865
	if ( file->GetFileType() != FILETYPE_MOD ) return false;
7866
	if ( !file->GetFileExt().Compare("cat") ) return false;
7867
 
7868
	// we need to read the file list for the mod
7869
	CCatFile cat;
7870
	if ( cat.Open(file->GetFilePointer(), this->GetAddonDir(), CATREAD_JUSTCONTENTS, false) == CATERR_NONE )
7871
	{
7872
		for ( int i = 0; i < cat.GetNumFiles(); i++ )
7873
		{
7874
			SInCatFile *f = cat.GetFile(i);
7875
			CyString filename = f->sFile;
7876
			filename = filename.FindReplace("\\", "/");
7877
			if ( filename.Left(2).Compare("t/") || filename.Left(6).Compare("types/") )
7878
			{
7879
				if ( list )
7880
					list->PushBack(filename, CyString(f->lSize));
7881
				else
7882
					return true;
7883
			}
7884
		}
7885
	}
7886
 
7887
	if ( list && !list->Empty() )
7888
		return true;
7889
 
7890
	return false;
7891
}
7892
 
7893
/**
7894
 * Gets the files that are not compatable with each other
7895
 *
7896
 * Returns true if theres any files that are not compatable
7897
 *
7898
 * If list is specified, fills up with all files that were found
7899
 */
7900
bool CPackages::CheckCompatabilityBetweenModFiles(C_File *from, C_File *to, CyStringList *list)
7901
{
7902
	// not a valid file
7903
	if ( !from || !to ) return false;
7904
	if ( from->GetFileType() != FILETYPE_MOD ) return false;
7905
	if ( to->GetFileType() != FILETYPE_MOD ) return false;
7906
	if ( !from->GetFileExt().Compare("cat") ) return false;
7907
	if ( !to->GetFileExt().Compare("cat") ) return false;
7908
 
7909
	// get file lists from each file
7910
	CyStringList fromList;
7911
	if ( GetModCompatabilityList(from, &fromList) )
7912
	{
7913
		CyStringList toList;
7914
		if ( GetModCompatabilityList(to, &toList) )
7915
		{
7916
			// both have files we need to check, compare them
7917
			for ( SStringList *str = fromList.Head(); str; str = str->next )
7918
			{
7919
				CyString fromFile = str->str;
7920
				fromFile = fromFile.FindReplace("\\", "/");
7921
				fromFile = fromFile.FindReplace("//", "/");
7922
				for ( SStringList *toStr = toList.Head(); toStr; toStr = toStr->next )
7923
				{
7924
					CyString toFile = toStr->str;
7925
					toFile = toFile.FindReplace("\\", "/");
7926
					toFile = toFile.FindReplace("//", "/");
7927
					if ( fromFile.Compare(toFile) )
7928
					{
7929
						if ( list )
7930
							list->PushBack(from->GetFilename() + "::" + str->str, to->GetFilename() + "::" + toStr->str);
7931
						else
7932
							return true;
7933
					}
7934
				}
7935
			}
7936
		}
7937
	}
7938
 
7939
	if ( list && !list->Empty() )
7940
		return true;
7941
 
7942
	return false;
7943
}
7944
 
7945
bool CPackages::CheckCompatabilityBetweenMods(CBaseFile *from, CBaseFile *to, CyStringList *list)
7946
{
7947
	if ( !from || !to ) return false;
7948
	if ( !from->IsEnabled() || !to->IsEnabled() ) return false;
7949
	if ( !from->AnyFileType(FILETYPE_MOD) ) return false;
7950
	if ( !to->AnyFileType(FILETYPE_MOD) ) return false;
7951
 
7952
	if ( from == to ) return false; // cant have incompatabilities to itself
7953
 
7954
	int count = 0;
7955
	for ( C_File *f = from->GetFirstFile(FILETYPE_MOD); f; f = from->GetNextFile(f) )
7956
	{
7957
		if ( !f->IsFakePatch() ) continue;
7958
		if ( f->GetFileExt().Compare("dat") ) continue;
7959
 
7960
		for ( C_File *compareFile = to->GetFirstFile(FILETYPE_MOD); compareFile; compareFile = to->GetNextFile(compareFile) )
7961
		{
7962
			if ( compareFile == f ) continue; // same file we're checking against
7963
			if ( !compareFile->IsFakePatch() ) continue;
7964
			if ( compareFile->GetFileExt().Compare("dat") ) continue;
7965
 
7966
			// now we have to files to compare
7967
			if ( CheckCompatabilityBetweenModFiles(f, compareFile, list) )
7968
				++count;
7969
		}
7970
	}
7971
 
7972
	if ( count )
7973
		return true;
7974
 
7975
	return false;
7976
}
7977
 
7978
int CPackages::CheckCompatabilityAgainstPackages(CBaseFile *newFile, CyStringList *list, CLinkList<CBaseFile> *packages)
7979
{
7980
	if ( !newFile->IsEnabled() ) return 0;
7981
	if ( !newFile->AnyFileType(FILETYPE_MOD) ) return 0;
7982
 
7983
	// we need to extract all mod files
7984
	for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
7985
	{
7986
		C_File *f = fNode->Data();
7987
		if ( f->GetFileType() != FILETYPE_MOD ) continue;
7988
		if ( !f->IsFakePatch() ) continue;
7989
		if ( !f->CheckFileExt("cat") ) continue;
7990
 
7991
		if ( newFile->ExtractFile(f, m_sTempDir) )
7992
			f->SetFullDir(m_sTempDir);
7993
	}
7994
 
7995
	// compare mod files against all installed packages
7996
	int count = 0;
7997
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
7998
	{
7999
		if ( !node->Data() ) continue;
8000
		CBaseFile *p = node->Data();
8001
		if ( !p->IsEnabled() ) continue;
8002
		if ( !p->AnyFileType(FILETYPE_MOD) ) continue;
8003
 
8004
		if ( this->IsSamePackage(p, newFile) ) continue; // dont include self
8005
 
8006
		if ( CheckCompatabilityBetweenMods(newFile, p, list) )
8007
		{
8008
			++count;
8009
			if ( packages && !packages->FindData(p) )
8010
				packages->push_back(p);
8011
		}
8012
	}
8013
 
8014
	for ( CListNode<C_File> *fNode = newFile->GetFileList()->Front(); fNode; fNode = fNode->next() )
8015
	{
8016
		C_File *f = fNode->Data();
8017
		CFileIO(f->GetFilePointer()).Remove();
8018
		f->SetFullDir("");
8019
	}
8020
 
8021
	return count;
8022
}
8023
 
8024
bool CPackages::IsSamePackage(CBaseFile *p1, CBaseFile *p2)
8025
{
8026
	if ( !p1 || !p2 ) return false;
8027
	if ( p1 == p2 ) return true;
8028
 
8029
	if ( p1->GetName().Compare(p2->GetName()) && p1->GetAuthor().Compare(p2->GetAuthor()) )
8030
		return true;
8031
	return false;
8032
}
8033
 
8034
void CPackages::ApplyFakePatchOrder(CyStringList *list)
8035
{
8036
	if ( !list ) return;
8037
	m_lFakePatchOrder.Clear();
8038
	for ( SStringList *str = list->Head(); str; str = str->next )
8039
		m_lFakePatchOrder.PushBack(str->str, str->data);
8040
}
8041
 
8042
SAvailablePackage *CPackages::CreateAvailablePackageData(CBaseFile *package)
8043
{
8044
	if ( !package ) return NULL;
8045
 
8046
	SAvailablePackage *p = new SAvailablePackage;
8047
 
8048
	for ( CListNode<SGameCompat> *node = package->GetGameCompatabilityList()->Front(); node; node = node->next() ) {
8049
		SGameCompat *gc = new SGameCompat;
8050
		gc->iGame = node->Data()->iGame;
8051
		gc->iVersion = node->Data()->iVersion;
8052
		gc->sVersion = node->Data()->sVersion;
8053
		p->lGames.push_back(gc);
8054
	}
8055
	p->bSigned = package->IsSigned();
8056
	p->iChanging = package->GetGameChanging();
8057
	p->iEase = package->GetEaseOfUse();
8058
	p->iPluginType = package->GetPluginType();
8059
	p->iRec = package->GetRecommended();
8060
	p->iScriptType = -1;
8061
	if ( package->GetType() == TYPE_XSP )
8062
		p->iType = PACKAGETYPE_SHIP;
8063
	else if ( package->IsMod() )
8064
		p->iType = PACKAGETYPE_MOD;
8065
	else 
8066
	{
8067
		p->iType = ((CSpkFile *)package)->GetPackageType();
8068
		p->iScriptType = ((CSpkFile *)package)->GetScriptType();
8069
	}
8070
	p->sAuthor = package->GetAuthor();
8071
	p->sDesc = package->GetDescription().findreplace("\n", "::newline::");
8072
	p->sName = package->GetName();
8073
	p->sUpdated = package->GetCreationDate();
8074
	p->sVersion = package->GetVersion();
8075
	p->sFilename = CFileIO(package->GetFilename()).GetFilename();
8076
 
8077
	return p;
8078
}
8079
 
8080
CyString CPackages::FormatAvailablePackageData(CBaseFile *package)
8081
{
8082
	SAvailablePackage *p = CPackages::CreateAvailablePackageData(package);
8083
	CyString ret = CPackages::FormatAvailablePackageData(p);
8084
	delete p;
8085
	return ret;
8086
}
8087
 
8088
CyString CPackages::FormatAvailablePackageData(SAvailablePackage *package)
8089
{
8090
	CyString ret = (long)package->iType;
8091
 
8092
	CyString gameCompat;
8093
	for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
8094
		if ( !gameCompat.Empty() )
8095
			gameCompat += "!";
8096
		gameCompat += CyString::Number(node->Data()->iGame);
8097
	}
8098
 
8099
	if ( gameCompat.Empty() )
8100
		gameCompat = "0";
8101
 
8102
	ret.AddToken("::", gameCompat);
8103
	ret.AddToken("::", package->sName);
8104
	ret.AddToken("::", package->sAuthor);
8105
	ret.AddToken("::", package->sVersion);
8106
	ret.AddToken("::", package->sUpdated);
8107
	ret.AddToken("::", package->sFilename);
8108
	ret.AddToken("::", CyString::Number(package->iEase));
8109
	ret.AddToken("::", CyString::Number(package->iChanging));
8110
	ret.AddToken("::", CyString::Number(package->iRec));
8111
	ret.AddToken("::", CyString::Number(package->iPluginType));
8112
	ret.AddToken("::", CyString::Number(package->iScriptType));
8113
	ret.AddToken("::", (package->bSigned) ? "1" : "0");
8114
	ret.AddToken("::", package->sDesc);
8115
 
8116
	return ret;
8117
}
8118
 
8119
void CPackages::ParseAvailablePackage(CyString str, CyString webaddress)
8120
{
8121
	// first check game
8122
	int game = str.GetToken("::", 2, 2).ToInt();
8123
	if ( game && m_iGame && m_iGame != game )
8124
		return;
8125
 
8126
	int num = 0;
8127
	CyString *tok = str.SplitToken("::", &num);
8128
	if ( !num || !tok ) return;
8129
	if ( num < 7 ) { CLEANSPLIT(tok, num); return; }
8130
 
8131
	SAvailablePackage *p = new SAvailablePackage;
8132
	p->iType = tok[0].ToInt();
8133
	p->bSigned = false;
8134
 
8135
	CyString gameCompat = tok[1];
8136
	if ( gameCompat.IsIn("!") ) {
8137
		int maxSplit = 0;
8138
		CyString *games = gameCompat.SplitToken("!", &maxSplit);
8139
		if ( games && maxSplit ) {
8140
			for ( int i = 0; i < maxSplit; i++ ) {
8141
				SGameCompat *gc = new SGameCompat;
8142
				gc->iVersion = 0;
8143
				gc->iGame = games[i].ToInt();
8144
				p->lGames.push_back(gc);
8145
			}
8146
		}
8147
		CLEANSPLIT(games, maxSplit);
8148
	}
8149
	else {
8150
		SGameCompat *gc = new SGameCompat;
8151
		gc->iVersion = 0;
8152
		gc->iGame = gameCompat.ToInt();
8153
		p->lGames.push_back(gc);
8154
	}
8155
	p->sName = tok[2];
8156
	p->sAuthor = tok[3];
8157
	p->sVersion = tok[4];
8158
	p->sUpdated = tok[5];
8159
	p->sFilename = tok[6];
8160
 
8161
	if ( !webaddress.Empty() )
8162
		p->sFilename = webaddress + "/" + p->sFilename;
8163
 
8164
	p->iChanging = p->iEase = p->iPluginType = p->iRec = p->iScriptType = -1;
8165
 
8166
	if ( num >= 12 )
8167
	{
8168
		p->iEase = tok[7].ToInt();
8169
		p->iChanging = tok[8].ToInt();
8170
		p->iRec = tok[9].ToInt();
8171
		p->iPluginType = tok[10].ToInt();
8172
		p->iScriptType = tok[11].ToInt();
8173
		if ( num > 12 ) {
8174
			if ( num > 13 ) {
8175
				p->sDesc = str.GetToken("::", 14); 
8176
				p->bSigned = str.GetToken("::", 13).ToBool();
8177
			}
8178
			else
8179
				p->sDesc = str.GetToken("::", 13); 
8180
		}
8181
	}
8182
	else if ( num > 7 )
8183
		p->sDesc = str.GetToken("::", 8); 
8184
 
8185
	if ( !p->sDesc.Empty() )
8186
		p->sDesc.FindReplace("::newline::", "\\n");
8187
 
8188
	AddAvailablePackage(p);
8189
 
8190
	CLEANSPLIT(tok, num);
8191
}
8192
 
8193
SAvailablePackage *CPackages::FindAvailablePackage(CyString &filename)
8194
{
8195
	for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
8196
	{
8197
		if ( node->Data()->sFilename.Compare(filename) )
8198
			return node->Data();
8199
	}
8200
	return NULL;
8201
}
8202
 
8203
bool CPackages::AddAvailablePackage(SAvailablePackage *package)	
8204
{ 
8205
	if ( !package->lGames.empty() ) {
8206
		bool found = false;
8207
		for ( CListNode<SGameCompat> *node = package->lGames.Front(); node; node = node->next() ) {
8208
			if ( !node->Data()->iGame || node->Data()->iGame == m_iGame ) {
8209
				found = true;
8210
				break;
8211
			}
8212
		}
8213
 
8214
		if ( !found )
8215
			return false;
8216
	}
8217
 
8218
	SAvailablePackage *p = FindAvailablePackage(package->sFilename);
8219
	if ( p )
8220
		m_lAvailablePackages.remove(p);
8221
	m_lAvailablePackages.push_back(package); 
8222
 
8223
	return true;
8224
}
8225
 
8226
bool CPackages::AnyAvailablePackages(int type)
8227
{
8228
	if ( type == -1 ) return !m_lAvailablePackages.empty();
8229
 
8230
	for ( CListNode<SAvailablePackage> *node = m_lAvailablePackages.Front(); node; node = node->next() )
8231
	{
8232
		if ( node->Data()->iType == type )
8233
			return true;
8234
	}
8235
 
8236
	return false;
8237
}
8238
 
8239
int CPackages::FindAllServers(CyStringList *list)
8240
{
8241
	for ( CListNode<CBaseFile> *node = m_lPackages.Front(); node; node = node->next() )
8242
	{
8243
		if ( !node->Data()->GetWebAddress().Empty() )
8244
			list->PushBack(node->Data()->GetWebAddress(), true);
8245
		if ( node->Data()->AnyWebMirrors() )
8246
		{
8247
			for ( SStringList *str = node->Data()->GetWebMirrors()->Head(); str; str = str->next )
8248
				list->PushBack(str->str, true);
8249
		}
8250
	}
8251
 
8252
	return list->Count();
8253
}
8254
 
8255
void CPackages::ReadArchiveData(CyString filename, CBaseFile *archive)
8256
{
8257
	size_t size;
8258
	char *data = CFileIO(filename).ReadToData(&size);
8259
	if ( size && data )
8260
		ReadArchiveData(data, size, archive);
8261
}
8262
 
8263
void CPackages::ReadArchiveData(const char *buf, size_t len, CBaseFile *archive)
8264
{
8265
	CyString data(buf);
8266
	int max;
8267
	CyString *str = data.SplitToken("\n", &max);
8268
	if ( str && max )
8269
	{
8270
		for ( int i = 0; i < max; i++ )
8271
		{
8272
			CyString line = str[i];
8273
			if ( line.Empty() )
8274
				continue;
8275
 
8276
			// filter out any spaces, tabs in front
8277
			line.RemoveChar('\t');
8278
			line.RemoveChar('\r');
8279
			CyString linenospace = line;
8280
			linenospace.RemoveFirstSpace();
8281
			if ( linenospace.Empty() )
8282
				continue;
8283
 
8284
			// check for any comments
8285
			if ( linenospace.Left(2) == "//" )
8286
				continue;
8287
			if ( linenospace[0] == '#' )
8288
				continue;
8289
 
8290
			// all commands start with a keyword followed by a colon, if one doesn't exist, it cant be a valid line
8291
			if ( !line.IsIn(':') )
8292
				continue;
8293
 
8294
			CyString first = line.GetToken(":", 1, 1);
8295
			CyString rest = line.GetToken(":", 2).RemoveFirstSpace();
8296
 
8297
			CyString checkType = first;
8298
			bool shared = false;
8299
			if ( checkType.Left(6).Compare("Shared") )
8300
			{
8301
				checkType = first.Right(-6);
8302
				shared = true;
8303
			}
8304
 
8305
			// now check type name
8306
			int filetype = GetFileTypeFromString(checkType);
8307
			if ( filetype == -1 )
8308
				archive->LoadPackageData(first, rest);
8309
		}
8310
	}
8311
 
8312
	CLEANSPLIT(str, max)
8313
}
8314
 
8315
CBaseFile *CPackages::CreateFromArchive(CyString filename, bool toInstall )
8316
{
8317
	// make sure we can open the zip file
8318
	CBaseFile *archive = NULL;
8319
	if ( CFileIO(filename).CheckFileExtension("rar") )
8320
	{
8321
#ifdef _RAR
8322
		HANDLE hArcData;
8323
		int RHCode,PFCode;
8324
		char CmtBuf[16384];
8325
		struct RARHeaderDataEx HeaderData;
8326
		struct RAROpenArchiveDataEx OpenArchiveData;
8327
 
8328
		if ( toInstall )
8329
		{
8330
			memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
8331
			OpenArchiveData.ArcName=(char *)filename.c_str();
8332
			OpenArchiveData.CmtBuf=CmtBuf;
8333
			OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
8334
			OpenArchiveData.OpenMode=RAR_OM_LIST;
8335
	//		OpenArchiveData.UserData=LIST;
8336
			hArcData=RAROpenArchiveEx(&OpenArchiveData);
8337
 
8338
			if (OpenArchiveData.OpenResult!=0)
8339
				return NULL;
8340
 
8341
			HeaderData.CmtBuf=NULL;
8342
			memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));
8343
 
8344
			while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
8345
			{
8346
				if ( CyString(HeaderData.FileName).Compare("pluginmanager.txt") )
8347
				{
8348
					toInstall = false;
8349
					break;
8350
				}
8351
			}
8352
			RARCloseArchive(hArcData);
8353
		}
8354
 
8355
		if ( toInstall )
8356
			archive = new CArchiveFile(); // just installing an archive file
8357
		else
8358
			archive = new CSpkFile(); // converting to a spk file
8359
 
8360
		memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
8361
		OpenArchiveData.ArcName=(char *)filename.c_str();
8362
		OpenArchiveData.CmtBuf=CmtBuf;
8363
		OpenArchiveData.CmtBufSize=sizeof(CmtBuf);
8364
		OpenArchiveData.OpenMode=RAR_OM_EXTRACT;
8365
		OpenArchiveData.UserData=EXTRACT;
8366
		hArcData=RAROpenArchiveEx(&OpenArchiveData);
8367
 
8368
		if (OpenArchiveData.OpenResult!=0)
8369
			return NULL;
8370
 
8371
		HeaderData.CmtBuf=NULL;
8372
		memset(&OpenArchiveData.Reserved,0,sizeof(OpenArchiveData.Reserved));
8373
 
8374
		bool error = false;
8375
		CyString extractedFile = CDirIO(m_sTempDir).File("extracted.tst").findreplace("/", "\\").findreplace("\\\\", "\\");
8376
		while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
8377
		{
8378
			CyString fileName = HeaderData.FileName;
8379
 
8380
			if ( HeaderData.FileAttr == 16 )
8381
				continue;
8382
			wchar_t wText[200];
8383
			::MultiByteToWideChar(CP_ACP, NULL, (char *)extractedFile.c_str(), -1, wText, extractedFile.Length() + 1);
8384
			PFCode=RARProcessFileW(hArcData, RAR_EXTRACT, NULL, NULL);
8385
			if (PFCode!=0)
8386
			{
8387
				error = true;
8388
				break;
8389
			}
8390
 
8391
			CFileIO File(fileName);
8392
			if ( File.Exists() )
8393
			{
8394
				if ( fileName.Compare("pluginmanager.txt") )
8395
					this->ReadArchiveData(File.GetFullFilename(), archive);
8396
				else
8397
				{
8398
					CyString extradir;
8399
					int type = SPK::GetAutomaticFiletype(fileName, &extradir);
8400
 
8401
					C_File *f = NULL;
8402
					if ( type == -1 )
8403
						f = archive->AddFile(CFileIO(fileName).GetFilename(), CFileIO(fileName).GetDir(), FILETYPE_EXTRA);
8404
					else
8405
						f = archive->AddFile(CFileIO(fileName).GetFilename(), extradir, type);
8406
					f->ReadFromFile(File.GetFullFilename());
8407
				}
8408
 
8409
				File.Remove();
8410
			}
8411
		}
8412
 
8413
		RARCloseArchive(hArcData);
8414
 
8415
		if ( error )
8416
		{
8417
			delete archive;
8418
			archive = NULL;
8419
		}
8420
#endif
8421
	}
8422
	else if ( CFileIO(filename).CheckFileExtension("zip") )
8423
	{
8424
		TCHAR buf[5000];
8425
		wsprintf(buf, L"%hs", filename.c_str());
8426
		HZIP hz = OpenZip(buf, 0);
8427
		if ( !hz ) 
8428
			return NULL;
8429
 
8430
		int index;
8431
		// move the files from the zip to the package
8432
		ZIPENTRY ze;
8433
		if ( FindZipItem(hz, L"pluginmanager.txt", true, &index, &ze) == Z_OK )
8434
			toInstall = false;
8435
		CloseZip(hz);
8436
 
8437
		hz = OpenZip(buf, 0);
8438
		if ( !hz ) 
8439
			return NULL;
8440
 
8441
		// create the correct package
8442
		if ( toInstall )
8443
			archive = new CArchiveFile(); // just installing an archive file
8444
		else
8445
			archive = new CSpkFile(); // converting to a spk file
8446
 
8447
		GetZipItem(hz, -1, &ze);
8448
		int numitems = ze.index;
8449
 
8450
		bool error = false;
8451
		for ( int zi = 0; zi < numitems; zi++ )
8452
		{
8453
			ZIPENTRY ze;
8454
			if ( GetZipItem(hz, zi, &ze) != Z_OK )
8455
			{
8456
				error = true;
8457
				break;
8458
			}
8459
 
8460
			if ( ze.attr & FILE_ATTRIBUTE_DIRECTORY )
8461
				continue; // dont do directories
8462
 
8463
 
8464
			char *iBuf = new char[ze.unc_size];
8465
			UnzipItem(hz, zi, iBuf, ze.unc_size);
8466
 
8467
			CyString Name(ze.name);
8468
 
8469
			// if its the data file, dont add it, but extract to get settings from
8470
			if ( Name.Compare("pluginmanager.txt") )
8471
			{
8472
				this->ReadArchiveData(iBuf, ze.unc_size, archive);
8473
				delete[] iBuf;
8474
			}
8475
			else
8476
			{
8477
				CyString extradir;
8478
				int type = SPK::GetAutomaticFiletype(Name, &extradir);
8479
 
8480
				C_File *f = NULL;
8481
				if ( type == -1 )
8482
					f = archive->AddFile(CFileIO(Name).GetFilename(), CFileIO(Name).GetDir(), FILETYPE_EXTRA);
8483
				else
8484
					f = archive->AddFile(CFileIO(Name).GetFilename(), extradir, type);
8485
 
8486
				if ( f )
8487
					f->SetData((const unsigned char *)iBuf, ze.unc_size);
8488
				else
8489
					delete[] iBuf;
8490
			}
8491
		}
8492
 
8493
		CloseZip(hz);
8494
 
8495
		if ( error )
8496
		{
8497
			delete archive;
8498
			archive = NULL;
8499
		}
8500
	}
8501
 
8502
	if ( archive )
8503
	{
8504
		archive->SetFilename(CFileIO(filename).ChangeFileExtension("spk"));
8505
		if ( toInstall )
8506
			archive->SetName(CFileIO(filename).GetFilename());
8507
		else
8508
			archive->SetName(CFileIO(filename).GetBaseName());
8509
	}
8510
 
8511
	return archive;
8512
}
8513
 
8514
CyString CPackages::CreateFromPackagerScript(CyString filename)
8515
{
8516
	CyString curDir = CFileIO(filename).GetDir();
8517
	CyStringList variables;
8518
	variables.PushBack("$PATH", curDir);
8519
	CPackages p;
8520
	CBaseFile *package = p.LoadPackagerScript(filename, NULL, NULL, NULL, &variables);
8521
 
8522
	if ( !package )
8523
		return NullString;
8524
 
8525
	CyString saveto = package->GetFilename();
8526
	saveto = saveto.FindReplace("$DEFAULTDIR", curDir + "/");
8527
	saveto = saveto.FindReplace("$PATH", curDir);
8528
	saveto = saveto.FindReplace("\\", "/");
8529
	saveto = saveto.FindReplace("//", "/");
8530
	if ( !saveto.Right(4).Compare(".spk") && package->GetType() != TYPE_XSP )
8531
		saveto += ".spk";
8532
	else if ( !saveto.Right(4).Compare(".xsp") && package->GetType() == TYPE_XSP )
8533
		saveto += ".xsp";
8534
 
8535
	// write script
8536
	if ( package->WriteFile(saveto) )
8537
	{
8538
		if ( package->AutoGenerateUpdateFile() )
8539
			package->CreateUpdateFile(CFileIO(saveto).GetDir());
8540
		return saveto;
8541
	}
8542
 
8543
	return NullString;
8544
}
8545
 
8546
int CPackages::GeneratePackageUpdateData(CyString dir, bool includeSingle)
8547
{
8548
	CyStringList filedata;
8549
 
8550
	CPackages packages;
8551
 
8552
	CDirIO Dir(dir);
8553
	for ( int i = 0; i < 2; i++ )
8554
	{
8555
		CyString pattern;
8556
		if ( i == 0 ) pattern = "*.spk";
8557
		else if ( i == 1 ) pattern = ".xsp";
8558
		else break;
8559
 
8560
		CyStringList *files = Dir.DirList("", pattern);
8561
		if ( files )
8562
		{
8563
			for ( SStringList *node = files->Head(); node; node = node->next )
8564
			{
8565
				int error = 0;
8566
				CBaseFile *p = packages.OpenPackage(Dir.File(node->str), &error, 0, SPKREAD_NODATA);
8567
				if ( !p )
8568
					continue;
8569
 
8570
				if ( includeSingle )
8571
					p->CreateUpdateFile(dir);
8572
				filedata.PushBack(CPackages::FormatAvailablePackageData(p));
8573
				delete p;
8574
			}
8575
			delete files;
8576
		}
8577
	}
8578
 
8579
	if ( !filedata.Empty() )
8580
	{
8581
		CFileIO File(dir + "/xpackagedata.dat");
8582
		if ( File.WriteFile(&filedata) )
8583
			return filedata.Count();
8584
	}
8585
 
8586
	return 0;
8587
}
8588
 
8589
int CPackages::VerifyInstalledFiles(CyStringList *missingFiles, bool getPackages)
8590
{
8591
	int count = 0;
8592
	for ( CListNode<C_File> *fn = m_lFiles.Front(); fn; fn = fn->next() )
8593
	{
8594
		C_File *f = fn->Data();
8595
		bool exists = false;
8596
		if ( f->GetFilePointer().IsIn("::") ) {
8597
			CyString modFile = f->GetFilePointer().GetToken("::", 1, 1);
8598
			CyString file = f->GetFilePointer().GetToken("::", 2, 2);
8599
 
8600
			if ( CFileIO(modFile).Exists() ) {
8601
				CCatFile catFile;
8602
				if ( catFile.Open(modFile, "", CATREAD_CATDECRYPT, false) == CATERR_NONE ) {
8603
					if ( catFile.FindData(file) )
8604
						exists = true;
8605
				}
8606
			}
8607
		}
8608
		else {
8609
			exists = CFileIO(f->GetFilePointer()).Exists();
8610
		}
8611
 
8612
		if ( !exists )
8613
		{
8614
			++count;
8615
			if ( missingFiles )
8616
			{
8617
				CyString packages;
8618
				if ( getPackages )
8619
				{
8620
					for ( CListNode<CBaseFile> *p = m_lPackages.Front(); p; p = p->next() )
8621
					{
8622
						CBaseFile *package = p->Data();
8623
						if ( package->IsFileAdded(f) )
8624
						{
8625
							if ( !packages.Empty() )
8626
								packages += "\n";
8627
							packages += package->GetFullPackageName(m_iLanguage);
8628
						}
8629
					}
8630
				}
8631
				CyString filename = f->GetFilePointer();
8632
				filename = filename.Remove(m_sCurrentDir);
8633
				missingFiles->PushBack(filename, packages, false);
8634
			}
8635
		}
8636
	}
8637
	return count;
8638
}