Skip to content

Commit

Permalink
Add support for setting subdirectories
Browse files Browse the repository at this point in the history
Allows user to specify subdirectories for games, installers, extras, patches, language packs and dlc
  • Loading branch information
Sude- committed Sep 19, 2014
1 parent 810506b commit 5635909
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 33 deletions.
6 changes: 6 additions & 0 deletions include/config.h
Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion include/util.h
Expand Up @@ -7,6 +7,8 @@
#ifndef UTIL_H
#define UTIL_H

#include "globalconstants.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
Expand All @@ -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
11 changes: 10 additions & 1 deletion main.cpp
Expand Up @@ -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<std::string> unrecognized_options_cfg;
bpo::variables_map vm;
bpo::options_description options_cli_all("Options");
Expand Down Expand Up @@ -104,7 +107,7 @@ int main(int argc, char *argv[])
;
// Commandline options (config file)
options_cli_cfg.add_options()
("directory", bpo::value<std::string>(&config.sDirectory)->default_value(""), "Set download directory")
("directory", bpo::value<std::string>(&config.sDirectory)->default_value("."), "Set download directory")
("limit-rate", bpo::value<curl_off_t>(&config.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited")
("xml-directory", bpo::value<std::string>(&config.sXMLDirectory), "Set directory for GOG XML files")
("chunk-size", bpo::value<size_t>(&config.iChunkSize)->default_value(10), "Chunk size (in MB) when creating XML")
Expand All @@ -129,6 +132,12 @@ int main(int argc, char *argv[])
("retries", bpo::value<int>(&config.iRetries)->default_value(3), "Set maximum number of retries on failed download")
("wait", bpo::value<int>(&config.iWait)->default_value(0), "Time to wait between requests (milliseconds)")
("cover-list", bpo::value<std::string>(&config.sCoverList)->default_value("https://sites.google.com/site/gogdownloader/covers.xml"), "Set URL for cover list")
("subdir-installers", bpo::value<std::string>(&config.sInstallersSubdir)->default_value(""), ("Set subdirectory for extras" + subdir_help_text).c_str())
("subdir-extras", bpo::value<std::string>(&config.sExtrasSubdir)->default_value("extras"), ("Set subdirectory for extras" + subdir_help_text).c_str())
("subdir-patches", bpo::value<std::string>(&config.sPatchesSubdir)->default_value("patches"), ("Set subdirectory for patches" + subdir_help_text).c_str())
("subdir-language-packs", bpo::value<std::string>(&config.sLanguagePackSubdir)->default_value("languagepacks"), ("Set subdirectory for language packs" + subdir_help_text).c_str())
("subdir-dlc", bpo::value<std::string>(&config.sDLCSubdir)->default_value("dlc/%dlcname%"), ("Set subdirectory for dlc" + subdir_help_text).c_str())
("subdir-game", bpo::value<std::string>(&config.sGameSubdir)->default_value("%gamename%"), ("Set subdirectory for game" + subdir_help_text).c_str())
;
// Options read from config file
options_cfg_only.add_options()
Expand Down
72 changes: 51 additions & 21 deletions src/downloader.cpp
Expand Up @@ -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<boost::filesystem::path> filepath_vector;
std::size_t pathlen = config.sDirectory.length();

try
{
if (boost::filesystem::exists(path))
std::vector<boost::filesystem::path> paths;
std::vector<unsigned int> platformIds;
platformIds.push_back(0);
for (unsigned int j = 0; j < GlobalConstants::PLATFORMS.size(); ++j)
{
platformIds.push_back(GlobalConstants::PLATFORMS[j].platformId);
}
for (unsigned int j = 0; j < platformIds.size(); ++j)
{
if (boost::filesystem::is_directory(path))
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<std::string::const_iterator> what;
if (boost::regex_search(filepath, what, expression))
filepath_vector.push_back(dir_iter->path());
bDuplicate = true;
break;
}
}
if (!bDuplicate)
paths.push_back(path);
}
}

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<std::string::const_iterator> what;
if (boost::regex_search(filepath, what, expression))
filepath_vector.push_back(dir_iter->path());
}
}
dir_iter++;
}
dir_iter++;
}
}
else
std::cout << paths[j] << " does not exist" << std::endl;
}
else
std::cout << path << " does not exist" << std::endl;
}
catch (const boost::filesystem::filesystem_error& ex)
{
Expand Down
23 changes: 16 additions & 7 deletions src/gamedetails.cpp
Expand Up @@ -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);
}
}
Expand Down
38 changes: 35 additions & 3 deletions src/util.cpp
Expand Up @@ -7,6 +7,7 @@
#include "util.h"

#include <boost/filesystem.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <tinyxml.h>
#include <jsoncpp/json/json.h>
#include <fstream>
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
}

0 comments on commit 5635909

Please sign in to comment.