Subversion Repositories spk

Rev

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