From b8aaa11bc466562d50b1e2797c69c1f55c77987f Mon Sep 17 00:00:00 2001 From: Rosalie Wanders Date: Mon, 30 Oct 2023 00:12:15 +0100 Subject: [PATCH] SporeModManager: add commandline options --- SporeModManager/SporeModManager.cpp | 20 +- SporeModManager/SporeModManager.hpp | 2 +- SporeModManager/SporeModManagerHelpers.hpp | 10 + .../SporeModManagerHelpers/Path.cpp | 17 ++ SporeModManager/SporeModManagerHelpers/UI.cpp | 36 ++++ SporeModManager/main.cpp | 193 +++++++++++++++--- 6 files changed, 238 insertions(+), 40 deletions(-) diff --git a/SporeModManager/SporeModManager.cpp b/SporeModManager/SporeModManager.cpp index ab6707f..8c1e01b 100644 --- a/SporeModManager/SporeModManager.cpp +++ b/SporeModManager/SporeModManager.cpp @@ -66,7 +66,7 @@ bool SporeModManager::InstallMod(std::filesystem::path path) return true; } -bool SporeModManager::UpdateMod(std::filesystem::path path) +bool SporeModManager::UpdateMod(std::filesystem::path path, bool requiresInstalled) { Zip::ZipFile zipFile; std::vector modInfoFileBuffer; @@ -126,12 +126,22 @@ bool SporeModManager::UpdateMod(std::filesystem::path path) if (!SporeMod::FindInstalledMod(installedSporeModUniqueName, installedSporeModId)) { - std::cerr << "No mod found with the same unique name" << std::endl - << "Did you mean install?" << std::endl; - return false; + if (requiresInstalled) + { + std::cerr << "No mod found with the same unique name" << std::endl + << "Did you mean install?" << std::endl; + return false; + } + } + else + { + if (!UninstallMods({ installedSporeModId })) + { + return false; + } } - return UninstallMods({ installedSporeModId }) && InstallMod(path); + return InstallMod(path); } bool SporeModManager::UninstallMods(std::vector ids) diff --git a/SporeModManager/SporeModManager.hpp b/SporeModManager/SporeModManager.hpp index c5093c0..3ef1f53 100644 --- a/SporeModManager/SporeModManager.hpp +++ b/SporeModManager/SporeModManager.hpp @@ -28,7 +28,7 @@ namespace SporeModManager /// /// Updates mod /// - bool UpdateMod(std::filesystem::path path); + bool UpdateMod(std::filesystem::path path, bool requiresInstalled = true); /// /// Uninstalls mods with ids diff --git a/SporeModManager/SporeModManagerHelpers.hpp b/SporeModManager/SporeModManagerHelpers.hpp index 9b82901..5504b11 100644 --- a/SporeModManager/SporeModManagerHelpers.hpp +++ b/SporeModManager/SporeModManagerHelpers.hpp @@ -198,6 +198,11 @@ namespace SporeModManagerHelpers namespace Path { + /// + /// Sets directories for current session + /// > + void SetDirectories(std::filesystem::path coreLibsPath, std::filesystem::path modLibsPath, std::filesystem::path galacticAdventuresDataPath, std::filesystem::path coreSporeDataPath); + /// /// Returns wether all required paths exist /// @@ -231,6 +236,11 @@ namespace SporeModManagerHelpers namespace UI { + /// + /// Sets no input mode + /// + void SetNoInputMode(bool value); + /// /// Asks user for an integer /// diff --git a/SporeModManager/SporeModManagerHelpers/Path.cpp b/SporeModManager/SporeModManagerHelpers/Path.cpp index eb36031..1b763fc 100644 --- a/SporeModManager/SporeModManagerHelpers/Path.cpp +++ b/SporeModManager/SporeModManagerHelpers/Path.cpp @@ -60,8 +60,25 @@ std::filesystem::path MakeAbsolutePath(std::filesystem::path path) // Exported Functions // +void Path::SetDirectories(std::filesystem::path coreLibsPath, std::filesystem::path modLibsPath, std::filesystem::path galacticAdventuresDataPath, std::filesystem::path coreSporeDataPath) +{ + l_CoreLibsPath = coreLibsPath; + l_ModLibsPath = modLibsPath; + l_GalacticAdventuresDataPath = galacticAdventuresDataPath; + l_CoreSporeDataPath = coreSporeDataPath; +} + bool Path::CheckIfPathsExist(void) { + // when the paths have been set for the current session, + // we know that those directories already exist, so + // we can safely return true here + if (!l_CoreLibsPath.empty() && !l_ModLibsPath.empty() && + !l_GalacticAdventuresDataPath.empty() && !l_CoreSporeDataPath.empty()) + { + return true; + } + if (!SporeMod::Xml::GetDirectories(l_CoreLibsPath, l_ModLibsPath, l_GalacticAdventuresDataPath, l_CoreSporeDataPath)) { std::cerr << "SporeMod::Xml::GetDirectories() Failed!" << std::endl; diff --git a/SporeModManager/SporeModManagerHelpers/UI.cpp b/SporeModManager/SporeModManagerHelpers/UI.cpp index 721779c..5433a98 100644 --- a/SporeModManager/SporeModManagerHelpers/UI.cpp +++ b/SporeModManager/SporeModManagerHelpers/UI.cpp @@ -14,6 +14,21 @@ using namespace SporeModManagerHelpers; +// +// Local Variables +// + +static bool l_NoInputMode = false; + +// +// Exported Functions +// + +void UI::SetNoInputMode(bool value) +{ + l_NoInputMode = value; +} + void UI::AskUserInput(std::string text, int& number, std::optional defaultNumber, int min, int max) { std::string inputLine; @@ -22,6 +37,13 @@ void UI::AskUserInput(std::string text, int& number, std::optional defaultN { std::cout << text; + if (l_NoInputMode) + { + std::cout << std::endl; + number = defaultNumber.has_value() ? defaultNumber.value() : 0; + return; + } + std::getline(std::cin, inputLine); if (inputLine.empty() && defaultNumber.has_value()) { @@ -58,6 +80,13 @@ void UI::AskUserInput(std::string text, char delimiter, std::vector& number std::cout << text; + if (l_NoInputMode) + { + std::cout << std::endl; + numbers = defaultNumbers; + return; + } + std::getline(std::cin, inputLine); if (inputLine.empty()) { @@ -103,6 +132,13 @@ void UI::AskUserInput(std::string text, bool& boolValue, bool defaultValue) { std::cout << text; + if (l_NoInputMode) + { + std::cout << std::endl; + boolValue = true; + return; + } + std::getline(std::cin, inputLine); inputLine = String::Lowercase(inputLine); if (inputLine.empty()) diff --git a/SporeModManager/main.cpp b/SporeModManager/main.cpp index aa85417..5ecc39f 100644 --- a/SporeModManager/main.cpp +++ b/SporeModManager/main.cpp @@ -10,60 +10,160 @@ #include "SporeModManager.hpp" #include "SporeModManagerHelpers.hpp" +#include +#include #include #include using namespace SporeModManagerHelpers; +// +// Local Defines +// + +#ifdef _WIN32 +#define arg_str(str) L##str +#define arg_str_type std::wstring +#else +#define arg_str(str) str +#define arg_str_type std::string +#endif // _WIN32 + +// +// Local Structures +// + +struct option_argument +{ + arg_str_type shortArgument; + arg_str_type longArgument; + bool& value; +}; + +struct path_argument +{ + arg_str_type argument; + std::filesystem::path& path; +}; + +// +// Local Functions +// + static void ShowUsage() { - std::cout << "Usage: SporeModManager [OPTION]" << std::endl + std::cout << "SporeModManager is a commandline mod manager for Spore" << std::endl + << std::endl + << "Usage: SporeModManager [OPTION] [COMMAND]" << std::endl << std::endl - << "Options:" << std::endl + << "Commands:" << std::endl << " list-installed lists installed mod(s) with id(s)" << std::endl << " install file(s) installs file(s)" << std::endl << " update file(s) updates mod(s) using file(s)" << std::endl << " uninstall id(s) uninstalls mod with id(s)" << std::endl << std::endl - << " help display this help and exit" << std::endl; + << " help display this help and exit" << std::endl + << std::endl + << "Options: " << std::endl + << " -n, --no-input disables user input during installation of mods" << std::endl + << " -u, --update-needed updates mod when mod is already installed" << std::endl + << " --corelibs-path sets corelibs path" << std::endl + << " --modlibs-path sets modlibs path" << std::endl + << " --data-path sets data path" << std::endl + << " --ep1-path sets ep1 data path" << std::endl + << std::endl; } +// +// Exported Functions +// + #ifdef _WIN32 int wmain(int argc, wchar_t** argv) #else int main(int argc, char** argv) #endif // _WIN32 { - if (!Path::CheckIfPathsExist()) + std::vector args(argv, argv + argc); + + // parse options + bool hasNoInputOption = false; + bool hasUpdateOption = false; + std::filesystem::path coreLibsPath; + std::filesystem::path modLibsPath; + std::filesystem::path dataPath; + std::filesystem::path ep1Path; + + struct option_argument optionArgs[] = { - return 1; + { arg_str("-n"), arg_str("--no-input"), hasNoInputOption }, + { arg_str("-u"), arg_str("--update-needed"), hasUpdateOption } + }; + + struct path_argument pathArgs[] = + { + { arg_str("--corelibs-path"), coreLibsPath }, + { arg_str("--modlibs-path"), modLibsPath }, + { arg_str("--data-path"), dataPath }, + { arg_str("--ep1-path"), ep1Path }, + }; + + for (int i = 0; i < args.size(); i++) + { + arg_str_type arg = args.at(i); + + for (const auto& optionArg : optionArgs) + { + if (arg == optionArg.shortArgument || + arg == optionArg.longArgument) + { + optionArg.value = true; + args.erase(args.begin() + i); + i -= 1; + } + } + + for (const auto& pathArg : pathArgs) + { + if (arg == pathArg.argument) + { + if (i != (args.size() - 1)) + { + pathArg.path = args.at(i + 1); + if (!std::filesystem::is_directory(pathArg.path)) + { + ShowUsage(); + return 1; + } + } + args.erase(args.begin() + i + 1); + args.erase(args.begin() + i); + i -= 1; + } + } } - if (argc < 2) + // apply options + UI::SetNoInputMode(hasNoInputOption); + Path::SetDirectories(coreLibsPath, modLibsPath, ep1Path, dataPath); + + // ensure we have a command + if (args.size() < 2) { ShowUsage(); return 1; } -#ifdef _WIN32 - // we know that wstring -> string conversion - // will possibly lose data, but that doesn't - // matter for the first argument, so we can - // safely disable the warning -#pragma warning(disable : 4244) - std::wstring waction = std::wstring(argv[1]); - std::string action = std::string(waction.begin(), waction.end()); -#else - std::string action = std::string(argv[1]); -#endif // _WIN32 - if (action == "help") + // parse commands + arg_str_type command = args.at(1); + if (command == arg_str("help")) { ShowUsage(); return 0; } - else if (action == "list-installed") + else if (command == arg_str("list-installed")) { - if (argc != 2) + if (args.size() != 2) { ShowUsage(); return 1; @@ -72,41 +172,66 @@ int main(int argc, char** argv) SporeModManager::ListInstalledMods(); return 0; } - else if (action == "install") + else if (command == arg_str("install")) { - if (argc < 3) + if (!Path::CheckIfPathsExist()) + { + return 1; + } + + if (args.size() < 3) { ShowUsage(); return 1; } - - for (int i = 2; i < argc; i++) + + for (size_t i = 2; i < args.size(); i++) { - if (!SporeModManager::InstallMod(argv[i])) + if (hasUpdateOption) { - return 1; + if (!SporeModManager::UpdateMod(args[i], false)) + { + return 1; + } + } + else + { + if (!SporeModManager::InstallMod(args[i])) + { + return 1; + } } } } - else if (action == "update") + else if (command == arg_str("update")) { - if (argc < 3) + if (!Path::CheckIfPathsExist()) + { + return 1; + } + + if (args.size() < 3) { ShowUsage(); return 1; } - for (int i = 2; i < argc; i++) + for (size_t i = 2; i < args.size(); i++) { - if (!SporeModManager::UpdateMod(argv[i])) + if (!SporeModManager::UpdateMod(args[i])) { return 1; } } } - else if (action == "uninstall") + else if (command == arg_str("uninstall")) { - if (argc < 3) + if (!Path::CheckIfPathsExist()) + { + return 1; + } + + if (args.size() < 3) { ShowUsage(); return 1; @@ -114,11 +239,11 @@ int main(int argc, char** argv) std::vector ids; - for (int i = 2; i < argc; i++) + for (size_t i = 2; i < args.size(); i++) { try { - ids.push_back(std::stoi(argv[i])); + ids.push_back(std::stoi(args[i])); } catch (...) {