Subversion Repositories spk

Rev

Rev 331 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 cycrow 1
/*
2
 SPKInstall V1.00 Created by Cycrow (Matthew Gravestock)
3
*/
4
 
5
// Main Spk File Library Include
6
#ifdef _WIN32
7
#include <spk.h>
218 cycrow 8
#include <StringList.h>
1 cycrow 9
#else
10
#include "../spk/spk.h"
11
#endif
12
#include <time.h>
13
 
14
#ifdef _WIN32
15
#include <windows.h>
16
#include <direct.h>
17
#include <shlobj.h>
18
#endif
19
 
46 cycrow 20
#include <Package/InstallText.h>
329 cycrow 21
#include "Utils/CommandLine.h"	
46 cycrow 22
 
1 cycrow 23
#define BETA 1
24
 
25
class CProgressConsole : public CProgressInfo
26
{
27
public:
28
	CProgressConsole() : CProgressInfo()
29
	{
30
		m_iCount = 0;
31
		m_fNextPercent = 0.0f;
32
	}
33
 
34
	void Finish(bool newline = true)
35
	{
36
		for ( int i = m_iCount; i < 20; i++ )
341 cycrow 37
			wprintf(L"*" );
1 cycrow 38
		if ( newline )
341 cycrow 39
			wprintf(L"\n");
1 cycrow 40
		m_fNextPercent = 0.0f;
41
		m_iCount = 0;
42
	}
43
 
44
protected:
45
	virtual void ProgressUpdated ( const long cur, const long max )
46
	{
47
		float percent = ((float)cur / (float)max) * 100.0f;
48
		if ( m_bDoHalf )
49
		{
50
			percent /= 2.0f;
51
			if ( m_bSecondHalf )
52
				percent += 50.0f;
53
			else if ( percent > 50.0f )
54
				percent = 50.0f;
55
		}
56
		if ( (percent - m_fNextPercent) >= 5.0f )
57
		{
341 cycrow 58
			wprintf(L"*");
1 cycrow 59
			m_fNextPercent += 5.0f;
60
			++m_iCount;
61
		}
62
	}
63
	virtual void DoingFile ( C_File *file )
64
	{
65
	}
66
 
67
private:
68
	float m_fNextPercent;
69
	int   m_iCount;
70
};
71
 
329 cycrow 72
#define FLAGS L"[-voctm][--dir:destination][--lang:langid]"
1 cycrow 73
 
298 cycrow 74
Utils::WString	g_dir;
1 cycrow 75
bool	g_debug = false;
76
bool	g_force = false;
77
int		g_indent = 0;
78
/*
79
	Func:	GetInput
80
	Desc:	Gets an input from the user, ie, any settings required to be typed in
81
*/
206 cycrow 82
Utils::WString GetInput ()
1 cycrow 83
{
84
//	g_read = true;
85
 
206 cycrow 86
	Utils::WString line;
1 cycrow 87
	char c = getchar();
88
 
89
	while ( (c != '\n') && (c != '\0') )
90
	{
206 cycrow 91
		line += Utils::WString(c);
1 cycrow 92
		c = getchar();
93
	}
94
 
95
	return line;
96
}
97
 
331 cycrow 98
bool OpenDestination(Utils::CommandLine& cmd, const Utils::WString& destination)
99
{
100
	Utils::WString endMessage;
101
 
102
	wprintf(L"                                   |0              100|\n\n");
103
	wprintf(L"  - Reading game directory         ");
104
 
105
	CProgressConsole progress;
106
 
107
	auto& packages = cmd.packages();
108
	if (packages.read(destination, &progress))
109
	{
110
		packages.UpdatePackages();
111
		packages.ReadGameLanguage(false);
112
		progress.Finish(false);
113
 
114
		Utils::WString gameName = packages.getGameName();
115
		if (packages.GetLanguage() || !gameName.empty())
116
		{
117
			wprintf(L"\n\t");
118
			if (!gameName.empty())
119
				wprintf(L"Game: %s ", gameName.c_str());
120
			if (packages.GetLanguage())
121
				wprintf(L"(Language: %d)", packages.GetLanguage());
122
		}
123
		wprintf(L"\n");
124
		packages.AssignPackageNumbers();
125
		return true;
126
	}
127
	else
128
	{
129
		wprintf(L"ERROR!\n");
130
		wprintf(L"\nFailed to open destination directory: %s\n", destination.c_str());
131
		exit(1);
132
	}
133
 
134
	return false;
135
}
136
 
137
bool CloseDestination(Utils::CommandLine& cmd, bool isPrepare)
138
{
139
	CProgressConsole progress;
140
	Utils::WStringList lErrors;
141
 
142
	if (isPrepare)
143
		wprintf(L"  - Preparing game directory       ");
144
	else
145
		wprintf(L"  - Closing game directory         ");
146
 
147
	if (cmd.packages().closeDir(&lErrors, &progress, true))
148
		progress.Finish();
149
	else
150
	{
151
		wprintf(L"ERROR!\n");
152
		exit(1);
153
	}
154
 
155
	cmd.packages().RestoreFakePatch();
156
 
157
	wprintf(L"\nDone!\n");
158
 
159
	return true;
160
}
161
 
162
 
298 cycrow 163
void PrintSyntax(const Utils::WString &cmd)
1 cycrow 164
{
329 cycrow 165
	wprintf(L"Syntax: %s %s <command> [arguments]\n", cmd.c_str(), FLAGS );
298 cycrow 166
	wprintf(L"\nCommands:\n" );
329 cycrow 167
	wprintf(L"   list\n\t- Lists installed packages\n");
168
	wprintf(L"   info <package#>\n\t- Info about an installed package, use id number from list\n");
169
	wprintf(L"   install <file>\n\t- Installs a package file\n" );
170
	wprintf(L"   uninstall <package#>\n\t- Uninstalls a package, use id number from list\n" );
171
	wprintf(L"   enable <package#>\n\t- Enables an installed package\n" );
172
	wprintf(L"   disable <package#>\n\t- Disables an installed package\n" );
173
	wprintf(L"   removeuninstall\n\t- Removes uninstall scripts\n" );
174
	wprintf(L"   removeshared\n\t- Removes unused sharded files\n" );
175
	wprintf(L"\nSwitchs:\n");
176
	wprintf(L"   --dir:<directory>\n\tChange destination directory, otherwise uses current\n\n" );
177
	wprintf(L"   --lang:<language>\n\tSets the language id for text file renaming\n\totheriwse reads game lang.dat\n\n");
178
	wprintf(L"\nFlags:\n");
179
	wprintf(L"   -v (--verbose)\n\tTurns on verbose mode\n\n");
180
	wprintf(L"   -o (--override)\n\tOverride install warnings\n\tIE. allows you to install older scripts\n\n");
181
	wprintf(L"   -t (--textrename)\n\tForces text file renaming for installed packages\n\n");
182
	wprintf(L"   -c (enablechild)\n\tAuto Enabled all children when parent is enabled\n\n");
183
	wprintf(L"   -m (forcemod)\n\tForces a mod enabled even if theres one already enabled\n\n");
1 cycrow 184
}
185
 
186
void DisplayPackage(CBaseFile *p, int indent, int language)
187
{
204 cycrow 188
	Utils::WString version = p->version();
1 cycrow 189
	if ( version.lower()[0] != 'v' )
204 cycrow 190
		version.prepend(L"v");
1 cycrow 191
 
341 cycrow 192
	wprintf(L"  [%5d] ", p->GetNum() + 1 );
1 cycrow 193
 
194
	if ( indent > g_indent )
341 cycrow 195
		wprintf(L" \\->");
1 cycrow 196
	for ( int i = 0; i < indent; i++ )
341 cycrow 197
		wprintf(L"\t");
1 cycrow 198
 
199
	g_indent = indent;
200
 
201
	if ( !p->IsEnabled() )
341 cycrow 202
		wprintf(L"[D] ");
1 cycrow 203
 
329 cycrow 204
	wprintf(L"%s %s by %s", p->name(language).c_str(), version.c_str(), p->author().c_str());
205
 
206
	switch (p->GetType())
207
	{
208
	case TYPE_XSP:
209
		wprintf(L" (Ship)");
210
		break;
211
	case TYPE_SPK:
212
	{
213
		CSpkFile* spk = dynamic_cast<CSpkFile*>(p);
214
		wprintf(L" (%s)", spk->scriptTypeString(language).c_str());
215
		break;
216
	}
217
	}
218
 
219
	wprintf(L"\n");
1 cycrow 220
}
221
 
222
void DoAllChildren(CPackages *packages, CBaseFile *p, CLinkList<CBaseFile> *doneList, int indent)
223
{
224
	CLinkList<CBaseFile> children;
225
	if ( packages->GetChildPackages(p, &children) )
226
	{
227
		for ( CBaseFile *child = children.First(); p; p = children.Next() )
228
		{
229
			DisplayPackage(child, indent, packages->GetLanguage());
230
			doneList->push_back(child);
231
 
232
			DoAllChildren(packages, child, doneList, indent + 1);
233
		}
234
	}
235
}
236
 
237
void ListPackages(CPackages *packages)
238
{
239
	CProgressConsole progress;
240
 
241
	CLinkList<CBaseFile> doneList;
242
 
243
	g_indent = 0;
329 cycrow 244
	wprintf(L"\nPackages:\n");
1 cycrow 245
	for ( CBaseFile *p = packages->PackageList()->First(); p; p = packages->PackageList()->Next() )
246
	{
247
		// already done?
248
		if ( doneList.FindData(p) )
249
			continue;
250
 
251
		if ( p->GetType() != TYPE_SPK )
252
			continue;
253
 
254
		DisplayPackage(p, 0, packages->GetLanguage());
255
		doneList.push_back(p);
256
 
257
		// find all children
258
		DoAllChildren(packages, p, &doneList, 1);
259
	}
329 cycrow 260
	wprintf(L"\n");
1 cycrow 261
}
262
 
222 cycrow 263
size_t SplitArguments(char **argv, int argc, int start, Utils::WStringList *argList)
1 cycrow 264
{
265
	for ( int i = start; i < argc; i++ )
266
	{
222 cycrow 267
		Utils::WString arg = argv[i];
268
		if (!arg.empty())
269
			argList->pushBack(arg);
1 cycrow 270
	}
271
 
222 cycrow 272
	return argList->size();
1 cycrow 273
}
274
 
331 cycrow 275
bool GetAllFiles(const Utils::CommandLine& cmd, int start, Utils::WStringList &files, const Utils::WString &ext, bool showError)
1 cycrow 276
{
341 cycrow 277
	for (size_t i = start; i < cmd.argCount(); i++)
331 cycrow 278
	{
279
		if (cmd.arg(i).containsAny(L"?*"))
280
		{
281
			CFileIO File(cmd.fullFilename(cmd.arg(i)));
282
			if (File.dirIO().exists())
283
			{
284
				Utils::WStringList fileList;
285
				if (File.dirIO().dirList(fileList, Utils::WString::Null(), File.filename(), true))
286
				{
287
					for (auto itr = fileList.begin(); itr != fileList.end(); itr++)
288
					{
289
						// check if the file exists
290
						if (CFileIO::Exists((*itr)->str))						
291
							files.pushBack((*itr)->str);
292
					}
293
				}
294
			}
295
		}
296
		else
297
		{
298
			// check if the file exists
299
			if (CFileIO::Exists(cmd.fullFilename(cmd.arg(i))))
300
			{
301
				files.pushBack(cmd.fullFilename(cmd.arg(i)));
302
				continue;
303
			}
304
 
305
			// otherwise, try to append extension to the filename
306
			if (!ext.empty())
307
			{
308
				if (CFileIO(cmd.fullFilename(cmd.arg(i))).extension().lower() != ext.lower())
309
				{
310
					Utils::WString filename = cmd.fullFilename(cmd.arg(i)) + L"." + ext;
311
					if (CFileIO::Exists(filename))
312
					{
313
						files.pushBack(filename);
314
						continue;
315
					}
316
				}
317
			}
318
 
319
			if (showError)
320
				wprintf(L"Error: Unable to find package file %s\n", cmd.arg(i).c_str());
321
		}
322
 
323
	}
324
 
325
	return !files.empty();
326
}
327
 
328
Utils::WString InstallPackage(Utils::CommandLine &cmd, const Utils::WString &destination, Utils::WStringList *errors, CPackages *packages, bool disabled)
329
{
330
	if (cmd.argCount() < 2)
331
	{
332
		wprintf(L"Syntax: %s [flags] install <file>\n\tInstalls a package to the destination\n", cmd.cmdName().c_str());
333
		return Utils::WString::Null();
334
	}
335
 
1 cycrow 336
	CProgressConsole progress;
337
 
338
	int error;
339
 
331 cycrow 340
	Utils::WStringList files;
341
	if(!GetAllFiles(cmd, 1, files, L"spk", true))
342
	{
343
		wprintf(L"Error: No package files specified to install\n");
203 cycrow 344
		return Utils::WString::Null();
1 cycrow 345
	}
346
 
331 cycrow 347
	if (!OpenDestination(cmd, destination))
348
		return Utils::WString::Null();
349
 
1 cycrow 350
	CLinkList<CBaseFile> lPackages;
351
 
352
	// open the package file
353
	printf ( "  - Opening Package                \n" );
331 cycrow 354
	for (auto itr = files.begin(); itr != files.end(); itr++)
1 cycrow 355
	{
331 cycrow 356
		CFileIO spkFile((*itr)->str);
1 cycrow 357
 
331 cycrow 358
		if(spkFile.filename().length() > 30 )
359
			wprintf(L"    %28s.. ", spkFile.filename().left(28).c_str() );
1 cycrow 360
		else
331 cycrow 361
			wprintf(L"    %30s ", spkFile.filename().c_str() );
1 cycrow 362
 
363
		CLinkList<CBaseFile> lPackageList;
331 cycrow 364
		CBaseFile *package = packages->openPackage(spkFile.fullFilename(), &error, &progress);
1 cycrow 365
 
366
		// multi packages
367
		if ( !package && error == INSTALLERR_NOMULTI )
368
		{
331 cycrow 369
			if ( !packages->openMultiPackage(spkFile.fullFilename(), &lPackageList, &error, &progress))
1 cycrow 370
			{
341 cycrow 371
				wprintf(L"Error!\n");
1 cycrow 372
				continue;
373
			}
374
			progress.Finish();
375
		}
376
		else if ( package )
377
		{
378
			progress.Finish();
379
			lPackageList.push_back(package);
380
		}
381
		else
382
		{
341 cycrow 383
			wprintf(L"ERROR!  " );
1 cycrow 384
			switch ( error )
385
			{
386
				case INSTALLERR_OLD:
341 cycrow 387
					wprintf(L"File is in old format no longer supported" );
1 cycrow 388
					break;
389
				case INSTALLERR_NOEXIST:
341 cycrow 390
					wprintf(L"file doesn't exist" );
1 cycrow 391
					break;
392
				case INSTALLERR_INVALID:
341 cycrow 393
					wprintf(L"Invalid package file" );
1 cycrow 394
					break;
395
				case INSTALLERR_NOSHIP:
341 cycrow 396
					wprintf(L"Ship Packages are currently not supported" );
1 cycrow 397
					break;
398
				case INSTALLERR_VERSION:
341 cycrow 399
					wprintf(L"Package file was created in a newer version, unable to open" );
1 cycrow 400
					break;
401
			}
341 cycrow 402
			wprintf(L"\n");
1 cycrow 403
			continue;
404
		}
405
 
406
		for ( CListNode<CBaseFile> *pNode = lPackageList.Front(); pNode; pNode = pNode->next() )
407
		{
408
			CBaseFile *package = pNode->Data();
409
 
410
			// compare versions
203 cycrow 411
			Utils::WString packageName = package->getFullPackageName(packages->GetLanguage());
1 cycrow 412
 
413
			int checkFlags = IC_ALL;
414
			int check = packages->PrepareInstallPackage(package, (disabled) ? true : !package->IsEnabled(), false, checkFlags);
415
 
416
			switch (check)
417
			{
418
				case INSTALLCHECK_OLDVERSION:
203 cycrow 419
					wprintf(L"Newer version of \"%s\" already installed", packageName.c_str() );
1 cycrow 420
					if ( !g_force )
421
					{
341 cycrow 422
						wprintf(L", Unable to install older, use -o to force installation\n" );
1 cycrow 423
						continue;
424
					}
425
					else
426
					{
341 cycrow 427
						wprintf(L", Overriding install\n" );
1 cycrow 428
						if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
429
							continue;
430
						break;
431
					}
432
					break;
433
					// wait for the rest to be added
434
				case INSTALLCHECK_WRONGGAME:
203 cycrow 435
					wprintf ( L"ERROR! \"%s\" Wrong Game (Requires: %s)", packageName.c_str(), packages->getGameTypesString(package, false).c_str() );
1 cycrow 436
					if ( g_force )
437
					{
341 cycrow 438
						wprintf(L" [FORCED]\n" );
1 cycrow 439
						if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
440
							continue;
441
					}
442
					else
443
					{
341 cycrow 444
						wprintf(L"\n");
1 cycrow 445
						continue;
446
					}
447
					break;
448
 
449
				case INSTALLCHECK_WRONGVERSION:
203 cycrow 450
					wprintf ( L"ERROR! \"%s\" Wrong Game Version (Requires: %s)\n", packageName.c_str(), packages->getGameVersionString(package).c_str() );
1 cycrow 451
					if ( g_force )
452
					{
341 cycrow 453
						wprintf(L" [FORCED]\n" );
1 cycrow 454
						if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
455
							continue;
456
					}
457
					else
458
					{
341 cycrow 459
						wprintf(L"\n");
1 cycrow 460
						continue;
461
					}
462
					break;
463
			}
464
 
46 cycrow 465
			if ( package->installText()->any() )
1 cycrow 466
			{
206 cycrow 467
				Utils::WString installtext = packages->getInstallBeforeText(package);
182 cycrow 468
				if ( !installtext.empty() )
1 cycrow 469
				{
182 cycrow 470
					installtext = installtext.stripHtml();
206 cycrow 471
					wprintf(L"Installing %s: %s\n", packageName.c_str(), installtext.c_str() );
341 cycrow 472
					wprintf(L"Do you want to continue with the install? " );
1 cycrow 473
 
474
					if ( g_force )
341 cycrow 475
						wprintf(L"(FORCED)\n" );
1 cycrow 476
					else
477
					{
206 cycrow 478
						Utils::WString input = GetInput().lower();
1 cycrow 479
 
480
						if ( input != "y" && input != "yes" )
481
						{
341 cycrow 482
							wprintf(L"\nInstallion aborted!!\n\n" );
1 cycrow 483
							packages->RemovePreparedInstall(package);
484
							continue;
485
						}
486
					}
487
				}
488
			}
489
 
490
			lPackages.push_back(package);
491
		}
492
	}
493
 
494
	// is there any that couldn't install
495
	CLinkList<CBaseFile> lCheckPackages;
496
	if ( packages->CheckPreparedInstallRequired(&lCheckPackages) )
497
	{
341 cycrow 498
		wprintf(L"\nError! Some packages are missing dependacies:\n" );
1 cycrow 499
		for ( CListNode<CBaseFile> *pNode = lCheckPackages.Front(); pNode; pNode = pNode->next() )
500
		{
501
			CSpkFile *spk = (CSpkFile *)pNode->Data();
331 cycrow 502
			wprintf(L"\t%s V%s by %s, Requires:\n", spk->name(packages->GetLanguage()).c_str(), spk->version().c_str(), spk->author().c_str());
503
			if(!spk->otherName().empty())
504
				wprintf(L"\t\t%s by %s\n", spk->otherName().c_str(), spk->otherAuthor().c_str());
505
			for (auto lNode = spk->GetNeededLibraries()->Front(); lNode; lNode = lNode->next())
506
				wprintf(L"\t\t%s by %s (Minimum Version: %s)\n", lNode->Data()->sName.c_str(), lNode->Data()->sAuthor.c_str(), lNode->Data()->sMinVersion.c_str());
1 cycrow 507
		}
508
	}
509
 
510
	// no packages will be installed
511
	if ( packages->GetNumPackagesInQueue() < 1 )
203 cycrow 512
		return Utils::WString::Null();
1 cycrow 513
 
514
	// install the package file
341 cycrow 515
	wprintf(L"  - Installing                     " );
1 cycrow 516
	CLinkList<CBaseFile> erroredPackages;
183 cycrow 517
	if ( packages->installPreparedPackages(errors, &progress, &erroredPackages) )
1 cycrow 518
		progress.Finish();
519
	else
520
	{
341 cycrow 521
		wprintf(L"ERROR!\n" );
331 cycrow 522
		CloseDestination(cmd, false);
203 cycrow 523
		return Utils::WString::Null();
1 cycrow 524
	}
525
 
526
	// delete any errored packages
527
	for ( CListNode<CBaseFile> *pNode = erroredPackages.Front(); pNode; pNode = pNode->next() )
528
	{
529
		lPackages.remove(pNode->Data());
530
		pNode->DeleteData();
531
	}
532
 
331 cycrow 533
	CloseDestination(cmd, true);
534
 
1 cycrow 535
	// now display the packages
203 cycrow 536
	Utils::WString retStr = L"Packages Installed:\n";
1 cycrow 537
	for ( CListNode<CBaseFile> *pNode = lPackages.Front(); pNode; pNode = pNode->next() )
538
	{
539
		CBaseFile *package = pNode->Data();
540
 
203 cycrow 541
		retStr += L"  <> ";
170 cycrow 542
		retStr += package->getFullPackageName(packages->GetLanguage());
203 cycrow 543
		retStr += L"\n";
1 cycrow 544
 
182 cycrow 545
		if ( !packages->getInstallAfterText(package).empty() )
1 cycrow 546
		{
206 cycrow 547
			Utils::WString afterText = packages->getInstallAfterText(package).stripHtml();
548
			afterText = afterText.findReplace(L"\n", L"\n\t");
203 cycrow 549
			retStr += L"\t";
206 cycrow 550
			retStr += afterText;
203 cycrow 551
			retStr += L"\n";
1 cycrow 552
		}
553
	}
554
 
555
	return retStr;
556
}
557
 
331 cycrow 558
CBaseFile *FindPackage(int packageNum, const CPackages *packages)
1 cycrow 559
{
331 cycrow 560
	for(auto node = packages->packageList()->Front(); node; node = node->next())
1 cycrow 561
	{
331 cycrow 562
		if (node->Data() && node->Data()->GetNum() == (packageNum - 1))
563
			return node->Data();
1 cycrow 564
	}
565
 
331 cycrow 566
	return nullptr;
1 cycrow 567
}
568
 
206 cycrow 569
bool UninstallPackage(int uninstallNum, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
1 cycrow 570
{
571
	CBaseFile *p = FindPackage(uninstallNum, packages);
572
	if ( !p )
573
		return false;
574
 
575
	// lets check for the uninstall text
206 cycrow 576
	Utils::WString uText = packages->getUninstallBeforeText(p).stripHtml();
182 cycrow 577
	if (!uText.empty())
1 cycrow 578
	{
206 cycrow 579
		wprintf(L"Uninstalling: %s\n", uText.c_str() );
341 cycrow 580
		wprintf(L"Do you wish to continue with the uninstall? " );
1 cycrow 581
		if ( g_force )
341 cycrow 582
			wprintf(L"(FORCED)\n" );
1 cycrow 583
		else
584
		{
206 cycrow 585
			Utils::WString input = GetInput().lower();
1 cycrow 586
			if ( input != "y" && input != "yes" )
587
			{
341 cycrow 588
				wprintf(L"\nUninstallation has been aborted!!\n" );
1 cycrow 589
				return false;
590
			}
591
		}
592
	}
593
 
594
	CProgressConsole progress;
595
 
206 cycrow 596
	endMessage = L"Uninstalled: " + p->getFullPackageName(packages->GetLanguage());
1 cycrow 597
	// add the uninstall after text
182 cycrow 598
	uText = packages->getUninstallAfterText(p).stripHtml();
599
	if (!uText.empty())
1 cycrow 600
	{
206 cycrow 601
		endMessage += L"\n\n";
182 cycrow 602
		endMessage += uText;
206 cycrow 603
		endMessage += L"\n";
1 cycrow 604
	}
605
 
341 cycrow 606
	wprintf(L"  - Unistalling                    " );
1 cycrow 607
	packages->PrepareUninstallPackage(p);
183 cycrow 608
	if ( packages->uninstallPreparedPackages(errors, &progress) )
1 cycrow 609
	{
610
		progress.Finish();
611
	}
612
	else
613
	{
206 cycrow 614
		endMessage = L"";
341 cycrow 615
		wprintf(L"ERROR!\n" );
1 cycrow 616
		return false;
617
	}
618
 
619
 
620
	return true;
621
}
622
 
206 cycrow 623
bool EnablePackage(int uninstallNum, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
1 cycrow 624
{
625
	CBaseFile *p = FindPackage(uninstallNum, packages);
626
	if ( !p )
627
		return false;
628
 
629
	CProgressConsole progress;
630
 
206 cycrow 631
	endMessage = L"Enabled: " + p->getFullPackageName(packages->GetLanguage());
1 cycrow 632
 
341 cycrow 633
	wprintf(L"  - Enabling                       " );
183 cycrow 634
	if ( packages->enablePackage(p, errors, &progress) )
1 cycrow 635
	{
636
		progress.Finish();
637
	}
638
	else
639
	{
640
		int error = packages->GetError();
182 cycrow 641
		endMessage = "";
341 cycrow 642
		wprintf(L"ERROR! " );
1 cycrow 643
		if ( error == PKERR_NOPARENT )
341 cycrow 644
			wprintf(L"Parent package is disabled" );
645
		wprintf(L"\n" );
1 cycrow 646
		return false;
647
	}
648
	return true;
649
}
650
 
206 cycrow 651
bool DisablePackage(int uninstallNum, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
1 cycrow 652
{
653
	CBaseFile *p = FindPackage(uninstallNum, packages);
654
	if ( !p )
655
		return false;
656
 
657
	CProgressConsole progress;
658
 
206 cycrow 659
	endMessage = L"Disabled: " + p->getFullPackageName(packages->GetLanguage());
1 cycrow 660
 
341 cycrow 661
	wprintf(L"  - Disabling                      " );
183 cycrow 662
	if ( packages->disablePackage(p, errors, &progress) )
1 cycrow 663
	{
664
		progress.Finish();
665
	}
666
	else
667
	{
206 cycrow 668
		endMessage = L"";
341 cycrow 669
		wprintf(L"ERROR!\n" );
1 cycrow 670
		return false;
671
	}
672
	return true;
673
}
674
 
197 cycrow 675
void RemoveUninstallScripts(CPackages *packages, Utils::WStringList *errors)
1 cycrow 676
{
677
	CProgressConsole progress;
341 cycrow 678
	wprintf(L"  - Removing uninstall scripts     " );
183 cycrow 679
	packages->removeUninstallScripts(errors, &progress);
1 cycrow 680
	progress.Finish();
681
}
682
 
197 cycrow 683
void RemoveUnusedShared(CPackages *packages, Utils::WStringList *errors)
1 cycrow 684
{
685
	CProgressConsole progress;
341 cycrow 686
	wprintf(L"  - Removing unused shared files   " );
183 cycrow 687
	packages->removeUnusedSharedFiles(errors, &progress);
1 cycrow 688
	progress.Finish();
689
}
690
 
691
 
222 cycrow 692
void PrintDebug(Utils::WStringList *errors)
1 cycrow 693
{
694
	if ( !g_debug )
695
		return;
696
 
222 cycrow 697
	if (errors && errors->size())
341 cycrow 698
		wprintf(L"\n");
1 cycrow 699
 
222 cycrow 700
	for(auto itr = errors->begin(); itr != errors->end(); itr++)
1 cycrow 701
	{
222 cycrow 702
		int errornum = (*itr)->data.toInt();
331 cycrow 703
		//Check this?
704
		//Utils::WString rest = (*itr)->str.tokens(L" ", 2);
705
		Utils::WString rest = (*itr)->str;
1 cycrow 706
 
222 cycrow 707
		Utils::WString err = FormatErrorString(errornum, rest);
200 cycrow 708
		wprintf(L"  * %s\n", err.c_str());
1 cycrow 709
	}
710
 
222 cycrow 711
	if (errors && errors->size())
341 cycrow 712
		wprintf(L"\n");
1 cycrow 713
}
714
 
331 cycrow 715
void ShowInfo(const Utils::CommandLine& cmd)
716
{
717
	int package = Utils::WString(cmd.arg(1)).toInt();
718
	CBaseFile* p = FindPackage(package, &cmd.packages());
719
	if (!p)
720
	{
721
		wprintf(L"Error: Unable to find package #%d\n", package);
722
		return;
723
	}
724
	wprintf(L"\n============================\n");
725
	wprintf(L"Package #%d: %s\n", package, p->name(cmd.packages().language()).c_str());
726
	wprintf(L"\t%15s: %s\n", L"Author", p->author().c_str());
727
	wprintf(L"\t%15s: %s\n", L"Version", p->version().c_str());
728
	wprintf(L"\t%15s: %s\n", L"Description", p->description().c_str());
729
	if (p->GetType() == TYPE_SPK)
730
	{
731
		CSpkFile* spk = dynamic_cast<CSpkFile*>(p);
732
		wprintf(L"\t%15s: %s\n", L"Type", spk->scriptTypeString(cmd.packages().language()).c_str());
733
	}
734
	else if (p->GetType() == TYPE_XSP)
735
		wprintf(L"\t%15s: %s\n", L"Type", L"Ship");
1 cycrow 736
 
331 cycrow 737
	wprintf(L"\t%15s: %s\n", L"Status", p->isEnabled() ? L"Enabled" : L"Disabled");
1 cycrow 738
 
331 cycrow 739
	if (p->anyDependacies())
740
	{
741
		wprintf(L"\t%15s:\n", L"Dependancies");
742
		for (auto needed = p->GetNeededLibraries()->First(); needed; needed = p->GetNeededLibraries()->Next())
743
			wprintf(L"\t\t%s by %s (Min Version: %s)", needed->sName.c_str(), needed->sAuthor.c_str(), needed->sMinVersion.c_str());
744
	}
1 cycrow 745
 
331 cycrow 746
	wprintf(L"\n============================\n\b");
747
}
748
 
749
 
1 cycrow 750
void Pause()
751
{
752
#ifdef _DEBUG
753
	char pause;
754
	scanf ( "%s", &pause );
755
#endif
756
}
757
 
758
 
298 cycrow 759
int ParseCommandSwitchs(wchar_t c, Utils::WString &destination, CPackages *packages, int start, int *arg, char **argv)
1 cycrow 760
{
761
	switch ( c )
762
	{
298 cycrow 763
		case L'o':
1 cycrow 764
			g_force = true;
765
			break;
298 cycrow 766
		case L'v':
1 cycrow 767
			g_debug = true;
768
			break;
298 cycrow 769
		case L'd':
182 cycrow 770
			destination = argv[*arg];
771
			destination = destination.findReplace("\\", "/");
1 cycrow 772
			++(*arg);
773
			++start;
774
			break;
298 cycrow 775
		case L't':
1 cycrow 776
			packages->SetRenameText(true);
777
			break;
298 cycrow 778
		case L'l':
779
			packages->SetLanguage(Utils::WString(argv[*arg]).toInt());
1 cycrow 780
			++(*arg);
781
			++start;
782
			break;
298 cycrow 783
		case L'c':
1 cycrow 784
			packages->SetAutoEnable(true);
785
			break;
298 cycrow 786
		case L'm':
1 cycrow 787
			packages->SetForceModInstall(true);
788
			break;
789
	}
790
 
791
	return start;
792
}
793
 
329 cycrow 794
 
795
 
1 cycrow 796
/*
797
TODO:
798
Additional Features
799
	Patch Mods
800
	Update Packages
801
*/
802
 
803
/*
804
	Main entry point to program
805
*/
806
int main ( int argc, char **argv )
807
{
808
 	// display program header to command prompt
341 cycrow 809
	wprintf(L"\nSPKInstall V1.00 (SPK Library Version %.2f) 31/05/2025 Created by Cycrow\n\n", GetLibraryVersion() );
1 cycrow 810
 
329 cycrow 811
	Utils::CommandLine cmd(argc, argv, true);
1 cycrow 812
 
813
	// not enough arguments, display the syntax and exit
329 cycrow 814
	if (cmd.argCount() < 1 )
1 cycrow 815
	{
329 cycrow 816
		PrintSyntax(cmd.cmdName());
1 cycrow 817
		Pause();
818
		exit ( 1 );
819
	}
820
 
329 cycrow 821
	Utils::WString destination = cmd.cmdDir();
197 cycrow 822
	Utils::WStringList lErrors;
1 cycrow 823
 
329 cycrow 824
	// do the flags and switchs
825
	// get the destination directory
826
	if(cmd.hasSwitch(L"dir"))
827
		destination = cmd.switchData(L"dir").findReplace("\\", "/");
828
	else if (cmd.hasSwitch(L"directory"))
829
		destination = cmd.switchData(L"directory").findReplace("\\", "/");
830
	else if (cmd.hasSwitch(L"destination"))
831
		destination = cmd.switchData(L"destination").findReplace("\\", "/");
1 cycrow 832
 
329 cycrow 833
	// get the game language
834
	auto& packages = cmd.packages();
835
	if (cmd.hasSwitch(L"lang"))
836
		packages.SetLanguage(cmd.switchData(L"lang").toInt());
837
	else if (cmd.hasSwitch(L"language"))
838
		packages.SetLanguage(cmd.switchData(L"language").toInt());
1 cycrow 839
 
329 cycrow 840
	// check for verbose mode
331 cycrow 841
	g_debug = cmd.hasSwitch(L"verbose") || cmd.hasFlag('v');
842
	g_force = cmd.hasSwitch(L"override") || cmd.hasFlag('o');
329 cycrow 843
 
331 cycrow 844
	if (cmd.hasSwitch(L"textrename") || cmd.hasFlag('t'))
845
		packages.SetRenameText(true);
846
 
847
	if (cmd.hasSwitch(L"enablechild") || cmd.hasFlag('c'))
848
		packages.SetAutoEnable(true);
849
	if (cmd.hasSwitch(L"forcemod") || cmd.hasFlag('m'))
850
		packages.SetForceModInstall(true);
851
 
1 cycrow 852
	int start = 2;
329 cycrow 853
	/*
1 cycrow 854
	// check for switchs
855
	while ( command[0] == '-' )
856
	{
857
		if ( argc < (start + 1) )
858
		{
859
			PrintSyntax(cmd);
860
			Pause();
861
			exit ( 1 );
862
		}
863
		++start;
864
 
865
		// single long commands
866
		int arg = 2;
298 cycrow 867
		if ( command.left(2) == L"--" )
1 cycrow 868
		{
298 cycrow 869
			Utils::WString cmd = command.right(-2);
870
			cmd.toLower();
1 cycrow 871
 
872
			char c = 0;
298 cycrow 873
			if ( cmd == L"override" )
1 cycrow 874
				c = 'o';
298 cycrow 875
			else if ( cmd == L"verbose" )
1 cycrow 876
				c = 'v';
298 cycrow 877
			else if ( cmd == L"directory" )
1 cycrow 878
				c = 'd';
298 cycrow 879
			else if ( cmd == L"textrename" )
1 cycrow 880
				c = 't';
298 cycrow 881
			else if ( cmd == L"enablechild" )
1 cycrow 882
				c = 'c';
298 cycrow 883
			else if ( cmd == L"language" )
1 cycrow 884
				c = 'l';
298 cycrow 885
			else if ( cmd == L"forcemod" )
1 cycrow 886
				c = 'm';
887
 
888
			if ( c )
182 cycrow 889
				start = ParseCommandSwitchs(c, destination, &packages, start, &arg, argv);
1 cycrow 890
		}
891
		else
892
		{
893
			// now parse arguments
298 cycrow 894
			for ( int i = 1; i < (int)command.length(); i++ )
182 cycrow 895
				start = ParseCommandSwitchs(command[i], destination, &packages, start, &arg, argv);
1 cycrow 896
		}
897
 
898
		if ( argc < start )
899
		{
900
			PrintSyntax(cmd);
901
			exit( 1 );
902
		}
903
		command = argv[start - 1];
904
	}
329 cycrow 905
	*/
1 cycrow 906
 
329 cycrow 907
	// check for valid command
908
	Utils::WString command = cmd.arg(0).toLower();
909
	if (command.Compare(L"list"))
910
	{
911
		if (OpenDestination(cmd, destination))
912
		{
913
			ListPackages(&packages);
914
			CloseDestination(cmd, false);
915
		}
916
	}
331 cycrow 917
	else if (command.Compare(L"info"))
918
	{
919
		if (cmd.argCount() < 2)
920
		{
921
			wprintf(L"Syntax: %s [flags] info <package#>\n\tDisplays info about an installed package\n", cmd.cmdName().c_str());
922
			wprintf(L"\tUse the <list> command to get the package number to use");
923
		}
924
		else
925
		{
926
			if (OpenDestination(cmd, destination))
927
			{
928
				ShowInfo(cmd);
929
				CloseDestination(cmd, false);
930
			}
931
		}
932
	}
933
	else if (command.Compare(L"install"))
934
	{
935
		Utils::WString aftertext = InstallPackage(cmd, destination, &lErrors, &packages, false);
936
		PrintDebug(&lErrors);
937
		if (!aftertext.empty())
938
			wprintf(L"\n%s\n", aftertext.c_str());
939
	}
940
	else if (command.Compare(L"uninstall"))
941
	{
942
		if (cmd.argCount() < 2)
943
		{
944
			wprintf(L"Syntax: %s [flags] uninstall <package#>\n\tUninstalls a package, package# is from the /l list command\n", cmd.cmdName().c_str());
945
			Pause();
946
			exit(1);
947
		}
948
		else
949
		{
950
			if (OpenDestination(cmd, destination))
951
			{
952
				bool prepare = UninstallPackage(Utils::WString(cmd.arg(1)).toInt(), &lErrors, &packages, command);
953
				PrintDebug(&lErrors);
954
				if (prepare)
955
					CloseDestination(cmd, true);
956
				else
957
					packages.RestoreFakePatch();
958
			}
959
		}
960
	}
961
	else if (command.Compare(L"enable"))
962
	{
963
		if (cmd.argCount() < 2)
964
		{
965
			wprintf(L"Syntax: %s [flags] enable <package#>\n\tEnables an installed package thats been disabled\n", cmd.cmdName().c_str());
966
			Pause();
967
			exit(1);
968
		}
969
		else
970
		{
971
			if (OpenDestination(cmd, destination))
972
			{
973
				bool prepare = EnablePackage(Utils::WString(cmd.arg(1)).toInt(), &lErrors, &packages, command);
974
				PrintDebug(&lErrors);
975
				if (prepare)
976
					CloseDestination(cmd, true);
977
				else
978
					packages.RestoreFakePatch();
979
			}
980
		}
981
	}
329 cycrow 982
 
1 cycrow 983
	if ( command[0] == '/' )
984
	{
298 cycrow 985
		wchar_t c = command[1];
1 cycrow 986
 
987
		bool disabled = false;
988
 
298 cycrow 989
		Utils::WString checkCmd = command.lower();
990
		if ( checkCmd == L"/install" )
991
			c = L'i';
992
		else if ( checkCmd == L"/installdisable" )
1 cycrow 993
		{
298 cycrow 994
			c = L'i';
1 cycrow 995
			disabled = true;
996
		}
298 cycrow 997
		else if ( checkCmd == L"/uninstall" )
998
			c = L'u';
999
		else if ( checkCmd == L"/enable" )
1000
			c = L'e';
1001
		else if ( checkCmd == L"/disable" )
1002
			c = L'd';
1003
		else if ( checkCmd == L"/list" )
1004
			c = L'l';
1005
		else if ( checkCmd == L"/removeshared" )
1006
			c = L's';
1007
		else if ( checkCmd == L"/removeunisntall" )
1008
			c = L'r';
1 cycrow 1009
 
298 cycrow 1010
		if ( c == L'i' || c == L'l' || c == L'u' || c == L'e' || c == L'd' || c == L'r' || c == L's' )
1 cycrow 1011
		{
206 cycrow 1012
			Utils::WString endMessage;
1 cycrow 1013
 
298 cycrow 1014
			wprintf(L"                                   |0              100|\n\n");
1015
			wprintf(L"  - Reading game directory         ");
1 cycrow 1016
 
1017
			CProgressConsole progress;
1018
 
182 cycrow 1019
			if ( packages.read(destination, &progress) )
1 cycrow 1020
			{
1021
				packages.UpdatePackages();
1022
				packages.ReadGameLanguage(false);
1023
				progress.Finish(false);
1024
 
197 cycrow 1025
				Utils::WString gameName = packages.getGameName();
182 cycrow 1026
				if ( packages.GetLanguage() || !gameName.empty())
1 cycrow 1027
				{
341 cycrow 1028
					wprintf(L"\n\t" );
182 cycrow 1029
					if ( !gameName.empty() )
197 cycrow 1030
						wprintf ( L"Game: %s ", gameName.c_str());
1 cycrow 1031
					if ( packages.GetLanguage() )
341 cycrow 1032
						wprintf(L"(Language: %d)", packages.GetLanguage() );
1 cycrow 1033
				}
341 cycrow 1034
				wprintf(L"\n" );
1 cycrow 1035
			}
1036
			else
1037
			{
341 cycrow 1038
				wprintf(L"ERROR!\n" );
1 cycrow 1039
				exit(1);
1040
			}
1041
 
1042
			bool prepare = false;
1043
 
1044
			packages.AssignPackageNumbers();
1045
 
222 cycrow 1046
			Utils::WStringList lArguments;
1 cycrow 1047
			SplitArguments(argv, argc, start, &lArguments);
1048
 
1049
			switch ( c )
1050
			{
1051
 
1052
				case 'u':
1053
					if ( argc <= start )
329 cycrow 1054
						wprintf(L"Syntax: %s [flags] /u <package#>\n\tUninstalls a package, package# is from the /l list command\n", cmd.cmdName().c_str() );
1 cycrow 1055
					else
1056
					{
298 cycrow 1057
						prepare = UninstallPackage(Utils::WString(argv[start]).toInt(), &lErrors, &packages, endMessage);
222 cycrow 1058
						PrintDebug(&lErrors);
1 cycrow 1059
					}
1060
					break;
1061
 
1062
				case 'e':
1063
					if ( argc <= start )
329 cycrow 1064
						wprintf(L"Syntax: %s [flags] /u <package#>\n\tEnabled an installed package thats been disabled\n", cmd.cmdName().c_str() );
1 cycrow 1065
					else
1066
					{
298 cycrow 1067
						prepare = EnablePackage(Utils::WString(argv[start]).toInt(), &lErrors, &packages, endMessage);
222 cycrow 1068
						PrintDebug(&lErrors);
1 cycrow 1069
					}
1070
					break;
1071
 
1072
				case 'd':
1073
					if ( argc <= start )
329 cycrow 1074
						wprintf(L"Syntax: %s [flags] /u <package#>\n\tDisabled an installed package\n", cmd.cmdName().c_str() );
1 cycrow 1075
					else
1076
					{
298 cycrow 1077
						prepare = DisablePackage(Utils::WString(argv[start]).toInt(), &lErrors, &packages, endMessage);
222 cycrow 1078
						PrintDebug(&lErrors);
1 cycrow 1079
					}
1080
					break;
1081
 
1082
				case 'l':
1083
					ListPackages(&packages);
1084
					break;
1085
 
1086
				case 'r':
183 cycrow 1087
					RemoveUninstallScripts(&packages, &lErrors);
1 cycrow 1088
					endMessage = "Removed all unused uninstall scripts";
1089
					break;
1090
 
1091
				case 's':
183 cycrow 1092
					RemoveUnusedShared(&packages, &lErrors);
1 cycrow 1093
					endMessage = "Removed all unused shared files";
1094
					break;
1095
			}
1096
 
1097
			if ( prepare )
1098
			{
183 cycrow 1099
				lErrors.clear();
341 cycrow 1100
				wprintf(L"  - Preparing game directory       " );
183 cycrow 1101
				if ( packages.closeDir(&lErrors, &progress, true) )
1 cycrow 1102
					progress.Finish();
1103
				else
1104
				{
341 cycrow 1105
					wprintf(L"ERROR!\n" );
1 cycrow 1106
					Pause();
1107
					exit(1);
1108
				}
1109
 
222 cycrow 1110
				PrintDebug(&lErrors);
1 cycrow 1111
			}
1112
			else
1113
				packages.RestoreFakePatch();
1114
 
341 cycrow 1115
			wprintf(L"\nDone!\n" );
182 cycrow 1116
			if ( !endMessage.empty() )
206 cycrow 1117
				wprintf(L"\n%s\n", endMessage.c_str() );
1 cycrow 1118
		}
1119
		else
341 cycrow 1120
			wprintf(L"Unknown Command: %c\n", c );
1 cycrow 1121
	}
1122
 
1123
	Pause();
1124
 
1125
	return 0;
1126
}