Rev 50 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
SPKInstall V1.00 Created by Cycrow (Matthew Gravestock)
*/
// Main Spk File Library Include
#ifdef _WIN32
#include <spk.h>
#else
#include "../spk/spk.h"
#endif
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include <shlobj.h>
#endif
#define BETA 1
class CProgressConsole : public CProgressInfo
{
public:
CProgressConsole() : CProgressInfo()
{
m_iCount = 0;
m_fNextPercent = 0.0f;
}
void Finish(bool newline = true)
{
for ( int i = m_iCount; i < 20; i++ )
printf ( "*" );
if ( newline )
printf ( "\n");
m_fNextPercent = 0.0f;
m_iCount = 0;
}
protected:
virtual void ProgressUpdated ( const long cur, const long max )
{
float percent = ((float)cur / (float)max) * 100.0f;
if ( m_bDoHalf )
{
percent /= 2.0f;
if ( m_bSecondHalf )
percent += 50.0f;
else if ( percent > 50.0f )
percent = 50.0f;
}
if ( (percent - m_fNextPercent) >= 5.0f )
{
printf ( "*" );
m_fNextPercent += 5.0f;
++m_iCount;
}
}
virtual void DoingFile ( C_File *file )
{
}
private:
float m_fNextPercent;
int m_iCount;
};
#define FLAGS "[-voctm][-d destination][-l langid]"
CyString g_dir;
bool g_debug = false;
bool g_force = false;
int g_indent = 0;
/*
Func: GetInput
Desc: Gets an input from the user, ie, any settings required to be typed in
*/
CyString GetInput ()
{
// g_read = true;
CyString line;
char c = getchar();
while ( (c != '\n') && (c != '\0') )
{
line += c;
c = getchar();
}
return line;
}
void PrintSyntax ( CyString cmd )
{
printf ( "Syntax: %s %s </command> [arguments]\n", cmd.c_str(), FLAGS );
printf ( "\nCommands:\n" );
printf ( " /list\n\t- Lists installed packages\n" );
printf ( " /install <file>\n\t- Installs a package file\n" );
printf ( " /uninstall <package#>\n\t- Uninstalls a package, use id number from list\n" );
printf ( " /enable <package#>\n\t- Enables an installed package\n" );
printf ( " /disable <package#>\n\t- Disables an installed package\n" );
printf ( " /removeuninstall\n\t- Removes uninstall scripts\n" );
printf ( " /removeshared\n\t- Removes unused sharded files\n" );
printf ( "\nSwitchs:\n" );
printf ( " -d (--directory) [directory]\n\tChange destination directory, otherwise uses current\n\n" );
printf ( " -v (--verbose)\n\tTurns on verbose mode\n\n" );
printf ( " -o (--override)\n\tOverride install warnings\n\tIE. allows you to install older scripts\n\n" );
printf ( " -t (--textrename)\n\tForces text file renaming for installed packages\n\n" );
printf ( " -l (--language) <language>\n\tSets the language id for text file renaming\n\totheriwse reads game lang.dat\n\n" );
printf ( " -c (--enablechild)\n\tAuto Enabled all children when parent is enabled\n\n" );
printf ( " -m (--forcemod\n\tForces a mod enabled even if theres one already enabled\n\n" );
}
void DisplayPackage(CBaseFile *p, int indent, int language)
{
CyString version = p->GetVersion();
if ( version.lower()[0] != 'v' )
version.Prepend("v");
printf ( " [%5d] ", p->GetNum() + 1 );
if ( indent > g_indent )
{
printf (" \\->");
}
for ( int i = 0; i < indent; i++ )
printf("\t");
g_indent = indent;
if ( !p->IsEnabled() )
printf("[D] ");
printf ( "%s %s by %s\n", p->GetLanguageName(language).c_str(), version.c_str(), p->GetAuthor().c_str() );
}
void DoAllChildren(CPackages *packages, CBaseFile *p, CLinkList<CBaseFile> *doneList, int indent)
{
CLinkList<CBaseFile> children;
if ( packages->GetChildPackages(p, &children) )
{
for ( CBaseFile *child = children.First(); p; p = children.Next() )
{
DisplayPackage(child, indent, packages->GetLanguage());
doneList->push_back(child);
DoAllChildren(packages, child, doneList, indent + 1);
}
}
}
void ListPackages(CPackages *packages)
{
CProgressConsole progress;
CLinkList<CBaseFile> doneList;
g_indent = 0;
printf ( "\nPackages:\n" );
for ( CBaseFile *p = packages->PackageList()->First(); p; p = packages->PackageList()->Next() )
{
// already done?
if ( doneList.FindData(p) )
continue;
if ( p->GetType() != TYPE_SPK )
continue;
DisplayPackage(p, 0, packages->GetLanguage());
doneList.push_back(p);
// find all children
DoAllChildren(packages, p, &doneList, 1);
}
}
int SplitArguments(char **argv, int argc, int start, CyStringList *argList)
{
for ( int i = start; i < argc; i++ )
{
CyString arg = argv[i];
if ( !arg.Empty() )
argList->PushBack(arg);
}
return argList->Count();
}
CyString InstallPackage(CyStringList *lArguments, CyStringList *errors, CPackages *packages, bool disabled)
{
CProgressConsole progress;
int error;
if ( !lArguments || !lArguments->Count() )
return NullString;
// append spk on the end
for ( SStringList *node = lArguments->Head(); node; node = node->next )
{
if ( !CFileIO(node->str).Exists() )
{
if ( CFileIO(node->str).GetFileExtension().lower() != "spk" )
node->str += ".spk";
}
}
CLinkList<CBaseFile> lPackages;
// open the package file
printf ( " - Opening Package \n" );
for ( SStringList *node = lArguments->Head(); node; node = node->next )
{
CyString spkFilename = node->str;
if ( spkFilename.Length() > 30 )
printf ( " ..%28s ", spkFilename.Right(28).c_str() );
else
printf ( " %30s ", spkFilename.Right(30).c_str() );
CLinkList<CBaseFile> lPackageList;
CBaseFile *package = packages->OpenPackage(spkFilename, &error, &progress);
// multi packages
if ( !package && error == INSTALLERR_NOMULTI )
{
if ( !packages->OpenMultiPackage(spkFilename, &lPackageList, &error, &progress) )
{
printf ( "Error!\n");
continue;
}
progress.Finish();
}
else if ( package )
{
progress.Finish();
lPackageList.push_back(package);
}
else
{
printf ( "ERROR! " );
switch ( error )
{
case INSTALLERR_OLD:
printf ( "File is in old format no longer supported" );
break;
case INSTALLERR_NOEXIST:
printf ( "file doesn't exist" );
break;
case INSTALLERR_INVALID:
printf ( "Invalid package file" );
break;
case INSTALLERR_NOSHIP:
printf ( "Ship Packages are currently not supported" );
break;
case INSTALLERR_VERSION:
printf ( "Package file was created in a newer version, unable to open" );
break;
}
printf ( "\n");
continue;
}
for ( CListNode<CBaseFile> *pNode = lPackageList.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *package = pNode->Data();
// compare versions
CyString packageName = package->GetFullPackageName(packages->GetLanguage());
int checkFlags = IC_ALL;
int check = packages->PrepareInstallPackage(package, (disabled) ? true : !package->IsEnabled(), false, checkFlags);
switch (check)
{
case INSTALLCHECK_OLDVERSION:
printf ( "Newer version of \"%s\" already installed", packageName.c_str() );
if ( !g_force )
{
printf ( ", Unable to install older, use -o to force installation\n" );
continue;
}
else
{
printf ( ", Overriding install\n" );
if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
continue;
break;
}
break;
// wait for the rest to be added
case INSTALLCHECK_WRONGGAME:
printf ( "ERROR! \"%s\" Wrong Game (Requires: %s)", packageName.c_str(), packages->GetGameTypesString(package, false).c_str() );
if ( g_force )
{
printf ( " [FORCED]\n" );
if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
continue;
}
else
{
printf("\n");
continue;
}
break;
case INSTALLCHECK_WRONGVERSION:
printf ( "ERROR! \"%s\" Wrong Game Version (Requires: %s)\n", packageName.c_str(), packages->GetGameVersionString(package).c_str() );
if ( g_force )
{
printf ( " [FORCED]\n" );
if ( packages->PrepareInstallPackage(package, disabled, true) != INSTALLCHECK_OK )
continue;
}
else
{
printf("\n");
continue;
}
break;
}
if ( package->IsThereInstallText() )
{
CyString installtext = packages->GetInstallBeforeText(package);
if ( !installtext.Empty() )
{
installtext.StripHTML();
printf ( "Installing %s: %s\n", packageName.c_str(), installtext.c_str() );
printf ( "Do you want to continue with the install? " );
if ( g_force )
printf ( "(FORCED)\n" );
else
{
CyString input = GetInput().ToLower();
if ( input != "y" && input != "yes" )
{
printf ( "\nInstallion aborted!!\n\n" );
packages->RemovePreparedInstall(package);
continue;
}
}
}
}
lPackages.push_back(package);
}
}
// is there any that couldn't install
CLinkList<CBaseFile> lCheckPackages;
if ( packages->CheckPreparedInstallRequired(&lCheckPackages) )
{
printf ( "\nError! Some packages are missing dependacies:\n" );
for ( CListNode<CBaseFile> *pNode = lCheckPackages.Front(); pNode; pNode = pNode->next() )
{
CSpkFile *spk = (CSpkFile *)pNode->Data();
printf ( "\t%s V%s by %s (Requires: %s by %s)\n", spk->GetLanguageName(packages->GetLanguage()).c_str(), spk->GetVersion().c_str(), spk->GetAuthor().c_str(), spk->GetOtherName().c_str(), spk->GetOtherAuthor().c_str());
}
}
// no packages will be installed
if ( packages->GetNumPackagesInQueue() < 1 )
return NullString;
// install the package file
printf ( " - Installing " );
CLinkList<CBaseFile> erroredPackages;
if ( packages->InstallPreparedPackages(errors, &progress, &erroredPackages) )
progress.Finish();
else
{
printf ( "ERROR!\n" );
return NullString;
}
// delete any errored packages
for ( CListNode<CBaseFile> *pNode = erroredPackages.Front(); pNode; pNode = pNode->next() )
{
lPackages.remove(pNode->Data());
pNode->DeleteData();
}
// now display the packages
CyString retStr = "Packages Installed:\n";
for ( CListNode<CBaseFile> *pNode = lPackages.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *package = pNode->Data();
retStr += " <> ";
retStr += package->GetFullPackageName(packages->GetLanguage());
retStr += "\n";
if ( !packages->GetInstallAfterText(package).Empty() )
{
CyString afterText = packages->GetInstallAfterText(package).StripHTML();
afterText = afterText.FindReplace("\n", "\n\t");
retStr += "\t";
retStr += afterText;
retStr += "\n";
}
}
return retStr;
}
CBaseFile *FindPackage(int packageNum, CPackages *packages)
{
for ( CBaseFile *p = packages->PackageList()->First(); p; p = packages->PackageList()->Next() )
{
if ( p->GetNum() == (packageNum - 1) )
return p;
}
return 0;
}
bool UninstallPackage(int uninstallNum, CyStringList *errors, CPackages *packages, CyString *endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
// lets check for the uninstall text
CyString uText = packages->GetUninstallBeforeText(p).StripHTML();
if ( !uText.Empty() )
{
printf ( "Uninstalling: %s\n", uText.c_str() );
printf ( "Do you wish to continue with the uninstall? " );
if ( g_force )
printf ( "(FORCED)\n" );
else
{
CyString input = GetInput().ToLower();
if ( input != "y" && input != "yes" )
{
printf ( "\nUninstallation has been aborted!!\n" );
return false;
}
}
}
CProgressConsole progress;
(*endMessage) = CyString("Uninstalled: ") + p->GetFullPackageName(packages->GetLanguage());
// add the uninstall after text
uText = packages->GetUninstallAfterText(p).StripHTML();
if ( !uText.Empty() )
{
(*endMessage) += "\n\n";
(*endMessage) += uText;
(*endMessage) += "\n";
}
printf ( " - Unistalling " );
packages->PrepareUninstallPackage(p);
if ( packages->UninstallPreparedPackages(errors, &progress) )
{
progress.Finish();
}
else
{
(*endMessage) = "";
printf ( "ERROR!\n" );
return false;
}
return true;
}
bool EnablePackage(int uninstallNum, CyStringList *errors, CPackages *packages, CyString *endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
CProgressConsole progress;
(*endMessage) = CyString("Enabled: ") + p->GetFullPackageName(packages->GetLanguage());
printf ( " - Enabling " );
if ( packages->EnablePackage(p, errors, &progress) )
{
progress.Finish();
}
else
{
int error = packages->GetError();
(*endMessage) = "";
printf ( "ERROR! " );
if ( error == PKERR_NOPARENT )
printf ( "Parent package is disabled" );
printf ( "\n" );
return false;
}
return true;
}
bool DisablePackage(int uninstallNum, CyStringList *errors, CPackages *packages, CyString *endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
CProgressConsole progress;
(*endMessage) = CyString("Disabled: ") + p->GetFullPackageName(packages->GetLanguage());
printf ( " - Disabling " );
if ( packages->DisablePackage(p, errors, &progress) )
{
progress.Finish();
}
else
{
(*endMessage) = "";
printf ( "ERROR!\n" );
return false;
}
return true;
}
void RemoveUninstallScripts(CPackages *packages, CyStringList *errors)
{
CProgressConsole progress;
printf ( " - Removing uninstall scripts " );
packages->RemoveUninstallScripts(errors, &progress);
progress.Finish();
}
void RemoveUnusedShared(CPackages *packages, CyStringList *errors)
{
CProgressConsole progress;
printf ( " - Removing unused shared files " );
packages->RemoveUnusedSharedFiles(errors, &progress);
progress.Finish();
}
void PrintDebug(CyStringList *errors)
{
if ( !g_debug )
return;
if ( errors && errors->Count() )
printf ( "\n");
for ( SStringList *str = errors->Head(); str; str = str->next )
{
int errornum = str->str.ToInt();
CyString rest = str->str.GetToken(" ", 2);
CyString err = FormatErrorString(errornum, rest);
printf ( " * %s\n", err.c_str() );
}
if ( errors && errors->Count() )
printf ( "\n");
}
void Pause()
{
#ifdef _DEBUG
char pause;
scanf ( "%s", &pause );
#endif
}
int ParseCommandSwitchs(char c, CyString *destination, CPackages *packages, int start, int *arg, char **argv)
{
switch ( c )
{
case 'o':
g_force = true;
break;
case 'v':
g_debug = true;
break;
case 'd':
*destination = argv[*arg];
*destination = destination->FindReplace("\\", "/");
++(*arg);
++start;
break;
case 't':
packages->SetRenameText(true);
break;
case 'l':
packages->SetLanguage(CyString(argv[*arg]).ToInt());
++(*arg);
++start;
break;
case 'c':
packages->SetAutoEnable(true);
break;
case 'm':
packages->SetForceModInstall(true);
break;
}
return start;
}
/*
TODO:
Additional Features
Patch Mods
Update Packages
*/
/*
Main entry point to program
*/
int main ( int argc, char **argv )
{
// display program header to command prompt
printf ( "\nSPKInstall V0.90 (SPK Library Version %.2f) 18/10/2009 Created by Cycrow\n\n", GetLibraryVersion() );
// parse the cmd name
CyString cmd (argv[0]);
cmd = cmd.FindReplace ( "\\", "/" );
g_dir = cmd.GetToken ( 0, cmd.NumToken('/') - 1, '/' );
cmd = cmd.GetToken ( cmd.NumToken('/'), '/' );
if ( g_dir.Empty() )
{
#ifdef _WIN32
g_dir = CyString(_getcwd(NULL, 0));
#else
g_dir = CyString(getcwd(NULL, 0));
#endif
if ( g_dir.Empty() )
g_dir = "./";
}
// not enough arguments, display the syntax and exit
if ( argc < 2 )
{
PrintSyntax(cmd);
Pause();
exit ( 1 );
}
CyString destination = g_dir;
CyStringList errors;
// get the command flag
CyString command(argv[1]);
command = command.lower();
CPackages packages;
CyString myDoc = g_dir;
#ifdef _WIN32
TCHAR pszPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pszPath)))
myDoc = (char *)pszPath;
#endif
packages.Startup(g_dir, g_dir, myDoc);
int start = 2;
// check for switchs
while ( command[0] == '-' )
{
if ( argc < (start + 1) )
{
PrintSyntax(cmd);
Pause();
exit ( 1 );
}
++start;
// single long commands
int arg = 2;
if ( command.Left(2) == "--" )
{
CyString cmd = command.Right(-2);
cmd = cmd.ToLower();
char c = 0;
if ( cmd == "override" )
c = 'o';
else if ( cmd == "verbose" )
c = 'v';
else if ( cmd == "directory" )
c = 'd';
else if ( cmd == "textrename" )
c = 't';
else if ( cmd == "enablechild" )
c = 'c';
else if ( cmd == "language" )
c = 'l';
else if ( cmd == "forcemod" )
c = 'm';
if ( c )
start = ParseCommandSwitchs(c, &destination, &packages, start, &arg, argv);
}
else
{
// now parse arguments
for ( int i = 1; i < (int)command.Length(); i++ )
start = ParseCommandSwitchs(command[i], &destination, &packages, start, &arg, argv);
}
if ( argc < start )
{
PrintSyntax(cmd);
exit( 1 );
}
command = argv[start - 1];
}
if ( command[0] == '/' )
{
char c = command[1];
bool disabled = false;
CyString checkCmd = command.ToLower();
if ( checkCmd == "/install" )
c = 'i';
else if ( checkCmd == "/installdisable" )
{
c = 'i';
disabled = true;
}
else if ( checkCmd == "/uninstall" )
c = 'u';
else if ( checkCmd == "/enable" )
c = 'e';
else if ( checkCmd == "/disable" )
c = 'd';
else if ( checkCmd == "/list" )
c = 'l';
else if ( checkCmd == "/removeshared" )
c = 's';
else if ( checkCmd == "/removeunisntall" )
c = 'r';
if ( c == 'i' || c == 'l' || c == 'u' || c == 'e' || c == 'd' || c == 'r' || c == 's' )
{
CyString endMessage;
printf ( " |0 100|\n\n");
printf ( " - Reading game directory " );
CProgressConsole progress;
if ( packages.Read(destination, &progress) )
{
packages.UpdatePackages();
packages.ReadGameLanguage(false);
progress.Finish(false);
CyString gameName = packages.GetGameName();
if ( packages.GetLanguage() || !gameName.Empty())
{
printf ( "\n\t" );
if ( !gameName.Empty() )
printf ( "Game: %s ", gameName.c_str());
if ( packages.GetLanguage() )
printf ( "(Language: %d)", packages.GetLanguage() );
}
printf ( "\n" );
}
else
{
printf ( "ERROR!\n" );
exit(1);
}
bool prepare = false;
packages.AssignPackageNumbers();
CyStringList lArguments;
SplitArguments(argv, argc, start, &lArguments);
switch ( c )
{
case 'i':
if ( argc <= start )
printf ( "Syntax: %s [flags] /i <file>\n\tInstalls a package to the destination\n", cmd.c_str() );
else
{
CyString aftertext = InstallPackage(&lArguments, &errors, &packages, disabled);
if ( !aftertext.Empty() )
{
prepare = true;
endMessage = aftertext;
}
PrintDebug(&errors);
}
break;
case 'u':
if ( argc <= start )
printf ( "Syntax: %s [flags] /u <package#\n\tUninstalls a package, package# is from the /l list command\n", cmd.c_str() );
else
{
prepare = UninstallPackage(CyString(argv[start]).ToInt(), &errors, &packages, &endMessage);
PrintDebug(&errors);
}
break;
case 'e':
if ( argc <= start )
printf ( "Syntax: %s [flags] /u <package#\n\tEnabled an installed package thats been disabled\n", cmd.c_str() );
else
{
prepare = EnablePackage(CyString(argv[start]).ToInt(), &errors, &packages, &endMessage);
PrintDebug(&errors);
}
break;
case 'd':
if ( argc <= start )
printf ( "Syntax: %s [flags] /u <package#\n\tDisabled an installed package\n", cmd.c_str() );
else
{
prepare = DisablePackage(CyString(argv[start]).ToInt(), &errors, &packages, &endMessage);
PrintDebug(&errors);
}
break;
case 'l':
ListPackages(&packages);
break;
case 'r':
RemoveUninstallScripts(&packages, &errors);
endMessage = "Removed all unused uninstall scripts";
break;
case 's':
RemoveUnusedShared(&packages, &errors);
endMessage = "Removed all unused shared files";
break;
}
if ( prepare )
{
errors.Clear();
printf ( " - Preparing game directory " );
if ( packages.CloseDir(&errors, &progress, true) )
progress.Finish();
else
{
printf ( "ERROR!\n" );
Pause();
exit(1);
}
PrintDebug(&errors);
}
else
packages.RestoreFakePatch();
printf ( "\nDone!\n" );
if ( !endMessage.Empty() )
printf ( "\n%s\n", endMessage.c_str() );
}
else
printf ( "Unknown Command: %c\n", c );
}
Pause();
return 0;
}