From 5635909e2049450ddde450dac2fca3ce1bdaa120 Mon Sep 17 00:00:00 2001 From: Sude Date: Fri, 19 Sep 2014 22:46:03 +0300 Subject: [PATCH] Add support for setting subdirectories Allows user to specify subdirectories for games, installers, extras, patches, language packs and dlc --- include/config.h | 6 ++++ include/util.h | 6 +++- main.cpp | 11 ++++++- src/downloader.cpp | 74 +++++++++++++++++++++++++++++++-------------- src/gamedetails.cpp | 23 +++++++++----- src/util.cpp | 38 +++++++++++++++++++++-- 6 files changed, 124 insertions(+), 34 deletions(-) diff --git a/include/config.h b/include/config.h index f8ada98..e9062cf 100644 --- a/include/config.h +++ b/include/config.h @@ -54,6 +54,12 @@ class Config std::string sOrphanRegex; std::string sCoverList; std::string sReportFilePath; + std::string sInstallersSubdir; + std::string sExtrasSubdir; + std::string sPatchesSubdir; + std::string sLanguagePackSubdir; + std::string sDLCSubdir; + std::string sGameSubdir; unsigned int iInstallerType; unsigned int iInstallerLanguage; int iRetries; diff --git a/include/util.h b/include/util.h index b1c8e05..a027a80 100644 --- a/include/util.h +++ b/include/util.h @@ -7,6 +7,8 @@ #ifndef UTIL_H #define UTIL_H +#include "globalconstants.h" + #include #include #include @@ -24,12 +26,14 @@ struct gameSpecificConfig namespace Util { - std::string makeFilepath(const std::string& directory, const std::string& path, const std::string& gamename, std::string subdirectory = ""); + std::string makeFilepath(const std::string& directory, const std::string& path, const std::string& gamename, std::string subdirectory = "", const unsigned int& platformId = 0, const std::string& dlcname = ""); std::string makeRelativeFilepath(const std::string& path, const std::string& gamename, std::string subdirectory = ""); std::string getFileHash(const std::string& filename, unsigned hash_id); std::string getChunkHash(unsigned char* chunk, size_t chunk_size, unsigned hash_id); int createXML(std::string filepath, size_t chunk_size, std::string xml_dir = std::string()); int getGameSpecificConfig(std::string gamename, gameSpecificConfig* conf, std::string directory = std::string()); + int replaceString(std::string& str, const std::string& to_replace, const std::string& replace_with); + void filepathReplaceReservedStrings(std::string& str, const std::string& gamename, const unsigned int& platformId = 0, const std::string& dlcname = ""); } #endif // UTIL_H diff --git a/main.cpp b/main.cpp index b57b99a..b136d1a 100644 --- a/main.cpp +++ b/main.cpp @@ -59,6 +59,9 @@ int main(int argc, char *argv[]) std::string orphans_regex_default = ".*\\.(zip|exe|bin|dmg|old|deb|tar\\.gz|pkg)$"; // 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 + "'"; + // Help text for subdir options + std::string subdir_help_text = "\nTemplates:\n- %platform%\n- %gamename%\n- %dlcname%"; + std::vector unrecognized_options_cfg; bpo::variables_map vm; bpo::options_description options_cli_all("Options"); @@ -104,7 +107,7 @@ int main(int argc, char *argv[]) ; // Commandline options (config file) options_cli_cfg.add_options() - ("directory", bpo::value(&config.sDirectory)->default_value(""), "Set download directory") + ("directory", bpo::value(&config.sDirectory)->default_value("."), "Set download directory") ("limit-rate", bpo::value(&config.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited") ("xml-directory", bpo::value(&config.sXMLDirectory), "Set directory for GOG XML files") ("chunk-size", bpo::value(&config.iChunkSize)->default_value(10), "Chunk size (in MB) when creating XML") @@ -129,6 +132,12 @@ int main(int argc, char *argv[]) ("retries", bpo::value(&config.iRetries)->default_value(3), "Set maximum number of retries on failed download") ("wait", bpo::value(&config.iWait)->default_value(0), "Time to wait between requests (milliseconds)") ("cover-list", bpo::value(&config.sCoverList)->default_value("https://sites.google.com/site/gogdownloader/covers.xml"), "Set URL for cover list") + ("subdir-installers", bpo::value(&config.sInstallersSubdir)->default_value(""), ("Set subdirectory for extras" + subdir_help_text).c_str()) + ("subdir-extras", bpo::value(&config.sExtrasSubdir)->default_value("extras"), ("Set subdirectory for extras" + subdir_help_text).c_str()) + ("subdir-patches", bpo::value(&config.sPatchesSubdir)->default_value("patches"), ("Set subdirectory for patches" + subdir_help_text).c_str()) + ("subdir-language-packs", bpo::value(&config.sLanguagePackSubdir)->default_value("languagepacks"), ("Set subdirectory for language packs" + subdir_help_text).c_str()) + ("subdir-dlc", bpo::value(&config.sDLCSubdir)->default_value("dlc/%dlcname%"), ("Set subdirectory for dlc" + subdir_help_text).c_str()) + ("subdir-game", bpo::value(&config.sGameSubdir)->default_value("%gamename%"), ("Set subdirectory for game" + subdir_help_text).c_str()) ; // Options read from config file options_cfg_only.add_options() diff --git a/src/downloader.cpp b/src/downloader.cpp index 4cd2e4c..2546a5b 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -2232,40 +2232,70 @@ void Downloader::checkOrphans() for (unsigned int i = 0; i < games.size(); ++i) { std::cout << "Checking for orphaned files " << i+1 << " / " << games.size() << "\r" << std::flush; - boost::filesystem::path path (config.sDirectory + games[i].gamename); std::vector filepath_vector; - std::size_t pathlen = config.sDirectory.length(); try { - if (boost::filesystem::exists(path)) + std::vector paths; + std::vector platformIds; + platformIds.push_back(0); + for (unsigned int j = 0; j < GlobalConstants::PLATFORMS.size(); ++j) { - if (boost::filesystem::is_directory(path)) + platformIds.push_back(GlobalConstants::PLATFORMS[j].platformId); + } + for (unsigned int j = 0; j < platformIds.size(); ++j) + { + std::string directory = config.sDirectory + "/" + config.sGameSubdir + "/"; + Util::filepathReplaceReservedStrings(directory, games[i].gamename, platformIds[j]); + boost::filesystem::path path (directory); + if (boost::filesystem::exists(path)) { - // Recursively iterate over files in directory - boost::filesystem::recursive_directory_iterator end_iter; - boost::filesystem::recursive_directory_iterator dir_iter(path); - while (dir_iter != end_iter) + bool bDuplicate = false; + for (unsigned int k = 0; k < paths.size(); ++k) { - if (boost::filesystem::is_regular_file(dir_iter->status())) + if (path == paths[k]) { - std::string filepath = dir_iter->path().string(); - if (config.blacklist.isBlacklisted(filepath.substr(pathlen))) { - if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; - } else { - boost::regex expression(config.sOrphanRegex); // Limit to files matching the regex - boost::match_results what; - if (boost::regex_search(filepath, what, expression)) - filepath_vector.push_back(dir_iter->path()); - } + bDuplicate = true; + break; } - dir_iter++; } + if (!bDuplicate) + paths.push_back(path); } } - else - std::cout << path << " does not exist" << std::endl; + + for (unsigned int j = 0; j < paths.size(); ++j) + { + std::size_t pathlen = config.sDirectory.length(); + if (boost::filesystem::exists(paths[j])) + { + if (boost::filesystem::is_directory(paths[j])) + { + // Recursively iterate over files in directory + boost::filesystem::recursive_directory_iterator end_iter; + boost::filesystem::recursive_directory_iterator dir_iter(paths[j]); + while (dir_iter != end_iter) + { + if (boost::filesystem::is_regular_file(dir_iter->status())) + { + std::string filepath = dir_iter->path().string(); + if (config.blacklist.isBlacklisted(filepath.substr(pathlen))) { + if (config.bVerbose) + std::cout << "skipped blacklisted file " << filepath << std::endl; + } else { + boost::regex expression(config.sOrphanRegex); // Limit to files matching the regex + boost::match_results what; + if (boost::regex_search(filepath, what, expression)) + filepath_vector.push_back(dir_iter->path()); + } + } + dir_iter++; + } + } + } + else + std::cout << paths[j] << " does not exist" << std::endl; + } } catch (const boost::filesystem::filesystem_error& ex) { diff --git a/src/gamedetails.cpp b/src/gamedetails.cpp index ce79d3c..8d61e92 100644 --- a/src/gamedetails.cpp +++ b/src/gamedetails.cpp @@ -14,47 +14,56 @@ gameDetails::~gameDetails() void gameDetails::makeFilepaths(const Config& config) { std::string filepath; + std::string directory = config.sDirectory + "/" + config.sGameSubdir + "/"; + std::string subdir; for (unsigned int i = 0; i < this->installers.size(); ++i) { - filepath = Util::makeFilepath(config.sDirectory, this->installers[i].path, this->gamename); + subdir = config.bSubDirectories ? config.sInstallersSubdir : ""; + filepath = Util::makeFilepath(directory, this->installers[i].path, this->gamename, subdir, this->installers[i].platform); this->installers[i].setFilepath(filepath); } for (unsigned int i = 0; i < this->extras.size(); ++i) { - filepath = Util::makeFilepath(config.sDirectory, this->extras[i].path, this->gamename, config.bSubDirectories ? "extras" : ""); + subdir = config.bSubDirectories ? config.sExtrasSubdir : ""; + filepath = Util::makeFilepath(directory, this->extras[i].path, this->gamename, subdir, 0); this->extras[i].setFilepath(filepath); } for (unsigned int i = 0; i < this->patches.size(); ++i) { - filepath = Util::makeFilepath(config.sDirectory, this->patches[i].path, this->gamename, config.bSubDirectories ? "patches" : ""); + subdir = config.bSubDirectories ? config.sPatchesSubdir : ""; + filepath = Util::makeFilepath(directory, this->patches[i].path, this->gamename, subdir, this->patches[i].platform); this->patches[i].setFilepath(filepath); } for (unsigned int i = 0; i < this->languagepacks.size(); ++i) { - filepath = Util::makeFilepath(config.sDirectory, this->languagepacks[i].path, this->gamename, config.bSubDirectories ? "languagepacks" : ""); + subdir = config.bSubDirectories ? config.sLanguagePackSubdir : ""; + filepath = Util::makeFilepath(directory, this->languagepacks[i].path, this->gamename, subdir, 0); } for (unsigned int i = 0; i < this->dlcs.size(); ++i) { for (unsigned int j = 0; j < this->dlcs[i].installers.size(); ++j) { - filepath = Util::makeFilepath(config.sDirectory, this->dlcs[i].installers[j].path, this->gamename, config.bSubDirectories ? "dlc/" + this->dlcs[i].gamename : ""); + subdir = config.bSubDirectories ? config.sDLCSubdir + "/" + config.sInstallersSubdir : ""; + filepath = Util::makeFilepath(directory, this->dlcs[i].installers[j].path, this->gamename, subdir, this->dlcs[i].installers[j].platform, this->dlcs[i].gamename); this->dlcs[i].installers[j].setFilepath(filepath); } for (unsigned int j = 0; j < this->dlcs[i].patches.size(); ++j) { - filepath = Util::makeFilepath(config.sDirectory, this->dlcs[i].patches[j].path, this->gamename, config.bSubDirectories ? "dlc/" + this->dlcs[i].gamename + "/patches" : ""); + subdir = config.bSubDirectories ? config.sDLCSubdir + "/" + config.sPatchesSubdir : ""; + filepath = Util::makeFilepath(directory, this->dlcs[i].patches[j].path, this->gamename, subdir, this->dlcs[i].patches[j].platform, this->dlcs[i].gamename); this->dlcs[i].patches[j].setFilepath(filepath); } for (unsigned int j = 0; j < this->dlcs[i].extras.size(); ++j) { - filepath = Util::makeFilepath(config.sDirectory, this->dlcs[i].extras[j].path, this->gamename, config.bSubDirectories ? "dlc/" + this->dlcs[i].gamename + "/extras" : ""); + subdir = config.bSubDirectories ? config.sDLCSubdir + "/" + config.sExtrasSubdir : ""; + filepath = Util::makeFilepath(directory, this->dlcs[i].extras[j].path, this->gamename, subdir, 0, this->dlcs[i].gamename); this->dlcs[i].extras[j].setFilepath(filepath); } } diff --git a/src/util.cpp b/src/util.cpp index e1579a8..8bbd6a0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -7,6 +7,7 @@ #include "util.h" #include +#include #include #include #include @@ -16,9 +17,11 @@ Remove the leading slash from path if needed Use gamename as base directory if specified */ -std::string Util::makeFilepath(const std::string& directory, const std::string& path, const std::string& gamename, std::string subdirectory) +std::string Util::makeFilepath(const std::string& directory, const std::string& path, const std::string& gamename, std::string subdirectory, const unsigned int& platformId, const std::string& dlcname) { - return directory + makeRelativeFilepath(path, gamename, subdirectory); + std::string dir = directory + makeRelativeFilepath(path, gamename, subdirectory); + Util::filepathReplaceReservedStrings(dir, gamename, platformId, dlcname); + return dir; } /* Create filepath relative to download base directory specified in config. @@ -46,7 +49,7 @@ std::string Util::makeRelativeFilepath(const std::string& path, const std::strin { subdirectory = "/" + subdirectory; } - filepath = gamename + subdirectory + "/" + filename; + filepath = subdirectory + "/" + filename; } return filepath; @@ -275,3 +278,32 @@ int Util::getGameSpecificConfig(std::string gamename, gameSpecificConfig* conf, return res; } + +int Util::replaceString(std::string& str, const std::string& to_replace, const std::string& replace_with) +{ + size_t pos = str.find(to_replace); + if (pos == std::string::npos) + { + return 0; + } + str.replace(str.begin()+pos, str.begin()+pos+to_replace.length(), replace_with); + return 1; +} + +void Util::filepathReplaceReservedStrings(std::string& str, const std::string& gamename, const unsigned int& platformId, const std::string& dlcname) +{ + std::string platform; + while (Util::replaceString(str, "%gamename%", gamename)); + while (Util::replaceString(str, "%dlcname%", dlcname)); + for (unsigned int i = 0; i < GlobalConstants::PLATFORMS.size(); ++i) + { + if ((platformId & GlobalConstants::PLATFORMS[i].platformId) == GlobalConstants::PLATFORMS[i].platformId) + { + platform = boost::algorithm::to_lower_copy(GlobalConstants::PLATFORMS[i].platformString); + break; + } + } + + while (Util::replaceString(str, "%platform%", platform)); + while (Util::replaceString(str, "//", "/")); // Replace any double slashes with single slash +}