Rev 222 | Rev 298 | 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>
#include <StringList.h>
#else
#include "../spk/spk.h"
#endif
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include <shlobj.h>
#endif
#include <Package/InstallText.h>
#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
*/
Utils::WString GetInput ()
{
// g_read = true;
Utils::WString line;
char c = getchar();
while ( (c != '\n') && (c != '\0') )
{
line += Utils::WString(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)
{
Utils::WString version = p->version();
if ( version.lower()[0] != 'v' )
version.prepend(L"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] ");
wprintf(L"%s %s by %s\n", p->name(language).c_str(), version.c_str(), p->author().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);
}
}
size_t SplitArguments(char **argv, int argc, int start, Utils::WStringList *argList)
{
for ( int i = start; i < argc; i++ )
{
Utils::WString arg = argv[i];
if (!arg.empty())
argList->pushBack(arg);
}
return argList->size();
}
Utils::WString InstallPackage(const Utils::WStringList &lArguments, Utils::WStringList *errors, CPackages *packages, bool disabled)
{
CProgressConsole progress;
int error;
if (lArguments.empty())
return Utils::WString::Null();
// append spk on the end
for (auto itr = lArguments.begin(); itr != lArguments.end(); itr++)
{
if (!CFileIO::Exists((*itr)->str))
{
if (CFileIO((*itr)->str).extension().lower() != L"spk")
(*itr)->str += L".spk";
}
}
CLinkList<CBaseFile> lPackages;
// open the package file
printf ( " - Opening Package \n" );
for (auto itr = lArguments.begin(); itr != lArguments.end(); itr++)
{
Utils::WString spkFilename = (*itr)->str;
if ( spkFilename.length() > 30 )
wprintf(L" ..%28s ", spkFilename.right(28).c_str() );
else
wprintf(L" %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
Utils::WString 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:
wprintf(L"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:
wprintf ( L"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:
wprintf ( L"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->installText()->any() )
{
Utils::WString installtext = packages->getInstallBeforeText(package);
if ( !installtext.empty() )
{
installtext = installtext.stripHtml();
wprintf(L"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
{
Utils::WString input = GetInput().lower();
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();
wprintf(L"\t%s V%s by %s (Requires: %s by %s)\n", spk->name(packages->GetLanguage()).c_str(), spk->version().c_str(), spk->author().c_str(), spk->otherName().c_str(), spk->otherAuthor().c_str());
}
}
// no packages will be installed
if ( packages->GetNumPackagesInQueue() < 1 )
return Utils::WString::Null();
// install the package file
printf ( " - Installing " );
CLinkList<CBaseFile> erroredPackages;
if ( packages->installPreparedPackages(errors, &progress, &erroredPackages) )
progress.Finish();
else
{
printf ( "ERROR!\n" );
return Utils::WString::Null();
}
// delete any errored packages
for ( CListNode<CBaseFile> *pNode = erroredPackages.Front(); pNode; pNode = pNode->next() )
{
lPackages.remove(pNode->Data());
pNode->DeleteData();
}
// now display the packages
Utils::WString retStr = L"Packages Installed:\n";
for ( CListNode<CBaseFile> *pNode = lPackages.Front(); pNode; pNode = pNode->next() )
{
CBaseFile *package = pNode->Data();
retStr += L" <> ";
retStr += package->getFullPackageName(packages->GetLanguage());
retStr += L"\n";
if ( !packages->getInstallAfterText(package).empty() )
{
Utils::WString afterText = packages->getInstallAfterText(package).stripHtml();
afterText = afterText.findReplace(L"\n", L"\n\t");
retStr += L"\t";
retStr += afterText;
retStr += L"\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, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
// lets check for the uninstall text
Utils::WString uText = packages->getUninstallBeforeText(p).stripHtml();
if (!uText.empty())
{
wprintf(L"Uninstalling: %s\n", uText.c_str() );
printf ( "Do you wish to continue with the uninstall? " );
if ( g_force )
printf ( "(FORCED)\n" );
else
{
Utils::WString input = GetInput().lower();
if ( input != "y" && input != "yes" )
{
printf ( "\nUninstallation has been aborted!!\n" );
return false;
}
}
}
CProgressConsole progress;
endMessage = L"Uninstalled: " + p->getFullPackageName(packages->GetLanguage());
// add the uninstall after text
uText = packages->getUninstallAfterText(p).stripHtml();
if (!uText.empty())
{
endMessage += L"\n\n";
endMessage += uText;
endMessage += L"\n";
}
printf ( " - Unistalling " );
packages->PrepareUninstallPackage(p);
if ( packages->uninstallPreparedPackages(errors, &progress) )
{
progress.Finish();
}
else
{
endMessage = L"";
printf ( "ERROR!\n" );
return false;
}
return true;
}
bool EnablePackage(int uninstallNum, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
CProgressConsole progress;
endMessage = L"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, Utils::WStringList *errors, CPackages *packages, Utils::WString &endMessage)
{
CBaseFile *p = FindPackage(uninstallNum, packages);
if ( !p )
return false;
CProgressConsole progress;
endMessage = L"Disabled: " + p->getFullPackageName(packages->GetLanguage());
printf ( " - Disabling " );
if ( packages->disablePackage(p, errors, &progress) )
{
progress.Finish();
}
else
{
endMessage = L"";
printf ( "ERROR!\n" );
return false;
}
return true;
}
void RemoveUninstallScripts(CPackages *packages, Utils::WStringList *errors)
{
CProgressConsole progress;
printf ( " - Removing uninstall scripts " );
packages->removeUninstallScripts(errors, &progress);
progress.Finish();
}
void RemoveUnusedShared(CPackages *packages, Utils::WStringList *errors)
{
CProgressConsole progress;
printf ( " - Removing unused shared files " );
packages->removeUnusedSharedFiles(errors, &progress);
progress.Finish();
}
void PrintDebug(Utils::WStringList *errors)
{
if ( !g_debug )
return;
if (errors && errors->size())
printf ( "\n");
for(auto itr = errors->begin(); itr != errors->end(); itr++)
{
int errornum = (*itr)->data.toInt();
Utils::WString rest = (*itr)->str.tokens(L" ", 2);
Utils::WString err = FormatErrorString(errornum, rest);
wprintf(L" * %s\n", err.c_str());
}
if (errors && errors->size())
printf ( "\n");
}
void Pause()
{
#ifdef _DEBUG
char pause;
scanf ( "%s", &pause );
#endif
}
int ParseCommandSwitchs(char c, Utils::String &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 );
}
Utils::String destination = g_dir.ToString();
Utils::WStringList lErrors;
// 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.c_str(), g_dir.c_str(), myDoc.c_str());
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' )
{
Utils::WString 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);
Utils::WString gameName = packages.getGameName();
if ( packages.GetLanguage() || !gameName.empty())
{
printf ( "\n\t" );
if ( !gameName.empty() )
wprintf ( L"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();
Utils::WStringList 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
{
Utils::WString aftertext = InstallPackage(lArguments, &lErrors, &packages, disabled);
if(!aftertext.empty())
{
prepare = true;
endMessage = aftertext;
}
PrintDebug(&lErrors);
}
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(), &lErrors, &packages, endMessage);
PrintDebug(&lErrors);
}
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(), &lErrors, &packages, endMessage);
PrintDebug(&lErrors);
}
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(), &lErrors, &packages, endMessage);
PrintDebug(&lErrors);
}
break;
case 'l':
ListPackages(&packages);
break;
case 'r':
RemoveUninstallScripts(&packages, &lErrors);
endMessage = "Removed all unused uninstall scripts";
break;
case 's':
RemoveUnusedShared(&packages, &lErrors);
endMessage = "Removed all unused shared files";
break;
}
if ( prepare )
{
lErrors.clear();
printf ( " - Preparing game directory " );
if ( packages.closeDir(&lErrors, &progress, true) )
progress.Finish();
else
{
printf ( "ERROR!\n" );
Pause();
exit(1);
}
PrintDebug(&lErrors);
}
else
packages.RestoreFakePatch();
printf ( "\nDone!\n" );
if ( !endMessage.empty() )
wprintf(L"\n%s\n", endMessage.c_str() );
}
else
printf ( "Unknown Command: %c\n", c );
}
Pause();
return 0;
}