diff --git a/include/config.h b/include/config.h index 980efe3..8205405 100644 --- a/include/config.h +++ b/include/config.h @@ -25,6 +25,7 @@ struct DirectoryConfig std::string sPatchesSubdir; std::string sLanguagePackSubdir; std::string sDLCSubdir; + std::string sGalaxyInstallSubdir; }; struct DownloadConfig diff --git a/include/util.h b/include/util.h index 57a5f3b..982564d 100644 --- a/include/util.h +++ b/include/util.h @@ -72,6 +72,7 @@ namespace Util std::string getLocalFileHash(const std::string& xml_dir, const std::string& filepath, const std::string& gamename = std::string()); void shortenStringToTerminalWidth(std::string& str); std::string getJsonUIntValueAsString(const Json::Value& json); + std::string getStrippedString(std::string str); template std::string formattedString(const std::string& format, Args ... args) { diff --git a/main.cpp b/main.cpp index 49f9c79..1305f4b 100644 --- a/main.cpp +++ b/main.cpp @@ -103,6 +103,20 @@ int main(int argc, char *argv[]) galaxy_arch_text += GlobalConstants::GALAXY_ARCHS[i].str + " = " + GlobalConstants::GALAXY_ARCHS[i].regexp + "\n"; } + // Create help text for --subdir-galaxy-install option + std::string galaxy_install_subdir_text = "Set subdirectory for galaxy install\n" + "\nTemplates:\n" + "- %install_dir% = Installation directory from Galaxy API response\n" + "- %gamename% = Game name\n" + "- %title% = Title of the game\n" + "- %product_id% = Product id of the game\n" + "- %install_dir_stripped% = %install_dir% with some characters stripped\n" + "- %title_stripped% = %title% with some characters stripped\n" + "\n\"stripped\" means that every character that doesn't match the following list is removed:\n" + "> alphanumeric\n" + "> space\n" + "> - _ . ( ) [ ] { }"; + // Create help text for --check-orphans std::string orphans_regex_default = ".*\\.(zip|exe|bin|dmg|old|deb|tar\\.gz|pkg|sh)$"; // Limit to files with these extensions (".old" is for renamed older version files) std::string check_orphans_text = "Check for orphaned files (files found on local filesystem that are not found on GOG servers). Sets regular expression filter (Perl syntax) for files to check. If no argument is given then the regex defaults to '" + orphans_regex_default + "'"; @@ -240,6 +254,7 @@ int main(int argc, char *argv[]) ("galaxy-language", bpo::value(&sGalaxyLanguage)->default_value("en"), galaxy_language_text.c_str()) ("galaxy-arch", bpo::value(&sGalaxyArch)->default_value("x64"), galaxy_arch_text.c_str()) ("galaxy-no-dependencies", bpo::value(&bNoGalaxyDependencies)->zero_tokens()->default_value(false), "Don't download dependencies during --galaxy-install") + ("subdir-galaxy-install", bpo::value(&Globals::globalConfig.dirConf.sGalaxyInstallSubdir)->default_value("%install_dir%"), galaxy_install_subdir_text.c_str()) ; options_cli_all.add(options_cli_no_cfg).add(options_cli_cfg).add(options_cli_experimental); diff --git a/src/downloader.cpp b/src/downloader.cpp index 279721b..7885b0a 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -3314,9 +3314,51 @@ void Downloader::galaxyInstallGame(const std::string& product_id, int build_inde json = gogGalaxy->getManifestV2(buildHash); std::string game_title = json["products"][0]["name"].asString(); - std::string install_directory = json["installDirectory"].asString(); - if (install_directory.empty()) - install_directory = product_id; + std::string install_directory; + + if (Globals::globalConfig.dirConf.bSubDirectories) + { + install_directory = Globals::globalConfig.dirConf.sGalaxyInstallSubdir; + + // Templates for installation subdir + std::map templates; + templates["%install_dir%"] = json["installDirectory"].asString(); + templates["%product_id%"] = product_id; + + std::vector templates_need_info = + { + "%gamename%", + "%title%", + "%title_stripped%" + }; + + if (std::any_of(templates_need_info.begin(), templates_need_info.end(), [install_directory](std::string template_dir){return template_dir == install_directory;})) + { + Json::Value productInfo = gogGalaxy->getProductInfo(product_id); + std::string gamename = productInfo["slug"].asString(); + std::string title = productInfo["title"].asString(); + + if (!gamename.empty()) + templates["%gamename%"] = productInfo["slug"].asString(); + if (!title.empty()) + templates["%title%"] = productInfo["title"].asString(); + } + + if (templates.count("%install_dir%")) + { + templates["%install_dir_stripped%"] = Util::getStrippedString(templates["%install_dir%"]); + } + + if (templates.count("%title%")) + { + templates["%title_stripped%"] = Util::getStrippedString(templates["%title%"]);; + } + + if (templates.count(install_directory)) + { + install_directory = templates[install_directory]; + } + } std::string install_path = Globals::globalConfig.dirConf.sDirectory + install_directory; diff --git a/src/util.cpp b/src/util.cpp index ebf06f5..6cbf9d3 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -608,3 +608,24 @@ std::string Util::getJsonUIntValueAsString(const Json::Value& json_value) return value; } + +std::string Util::getStrippedString(std::string str) +{ + str.erase( + std::remove_if(str.begin(), str.end(), + [](unsigned char c) + { + bool bIsValid = false; + bIsValid = (std::isspace(c) && std::isprint(c)) || std::isalnum(c); + std::vector validChars = { '-', '_', '.', '(', ')', '[', ']', '{', '}' }; + if (std::any_of(validChars.begin(), validChars.end(), [c](unsigned char x){return x == c;})) + { + bIsValid = true; + } + return !bIsValid; + } + ), + str.end() + ); + return str; +}