diff --git a/include/config.h b/include/config.h index 3238b4b..5c9465a 100644 --- a/include/config.h +++ b/include/config.h @@ -19,6 +19,7 @@ struct DirectoryConfig { bool bSubDirectories; std::string sDirectory; + std::string sWinePrefix; std::string sGameSubdir; std::string sInstallersSubdir; std::string sExtrasSubdir; diff --git a/include/downloader.h b/include/downloader.h index 5df5f42..da2cdc5 100644 --- a/include/downloader.h +++ b/include/downloader.h @@ -113,6 +113,7 @@ class Downloader void galaxyInstallGame(const std::string& product_id, int build_index = -1, const unsigned int& iGalaxyArch = GlobalConstants::ARCH_X64); void galaxyInstallGameById(const std::string& product_id, int build_index = -1, const unsigned int& iGalaxyArch = GlobalConstants::ARCH_X64); void galaxyShowBuilds(const std::string& product_id, int build_index = -1); + void galaxyShowCloudSaves(const std::string& product_id, int build_index = -1); void galaxyShowBuildsById(const std::string& product_id, int build_index = -1); void galaxyShowCloudSavesById(const std::string& product_id, int build_index = -1); protected: diff --git a/include/util.h b/include/util.h index b1d9007..a698cbf 100644 --- a/include/util.h +++ b/include/util.h @@ -66,6 +66,7 @@ namespace Util int createXML(std::string filepath, uintmax_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); + int replaceAllString(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 = ""); void setFilePermissions(const boost::filesystem::path& path, const boost::filesystem::perms& permissions); int getTerminalWidth(); diff --git a/main.cpp b/main.cpp index 4608130..9e8b4d7 100644 --- a/main.cpp +++ b/main.cpp @@ -24,6 +24,18 @@ template void set_vm_value(std::map(&Globals::globalConfig.dirConf.sDirectory)->default_value("."), "Set download directory") + ("wine-prefix", bpo::value(&Globals::globalConfig.dirConf.sWinePrefix)->default_value("."), "Set wineprefix directory") ("limit-rate", bpo::value(&Globals::globalConfig.curlConf.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited") ("xml-directory", bpo::value(&Globals::globalConfig.sXMLDirectory), "Set directory for GOG XML files") ("chunk-size", bpo::value(&Globals::globalConfig.iChunkSize)->default_value(10), "Chunk size (in MB) when creating XML") @@ -580,15 +593,8 @@ int main(int argc, char *argv[]) } // Make sure that directory has trailing slash - if (!Globals::globalConfig.dirConf.sDirectory.empty()) - { - if (Globals::globalConfig.dirConf.sDirectory.at(Globals::globalConfig.dirConf.sDirectory.length()-1)!='/') - Globals::globalConfig.dirConf.sDirectory += "/"; - } - else - { - Globals::globalConfig.dirConf.sDirectory = "./"; // Directory wasn't specified, use current directory - } + ensure_trailing_slash(Globals::globalConfig.dirConf.sDirectory, "./"); + ensure_trailing_slash(Globals::globalConfig.dirConf.sWinePrefix, "./"); // CA certificate bundle if (Globals::globalConfig.curlConf.sCACertPath.empty()) @@ -788,7 +794,7 @@ int main(int argc, char *argv[]) { build_index = std::stoi(tokens[1]); } - downloader.galaxyShowCloudSavesById(product_id, build_index); + downloader.galaxyShowCloudSaves(product_id, build_index); } else if (!galaxy_product_id_install.empty()) { diff --git a/src/downloader.cpp b/src/downloader.cpp index d80f4f8..f3ab5a6 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -50,6 +50,11 @@ ThreadSafeQueue dlQueueGalaxy_MojoSetupHack; std::mutex mtx_create_directories; // Mutex for creating directories in Downloader::processDownloadQueue std::atomic iTotalRemainingBytes(0); +std::string username() { + auto user = std::getenv("USER"); + return user ? user : std::string(); +} + Downloader::Downloader() { if (Globals::globalConfig.bLogin) @@ -4096,13 +4101,54 @@ void Downloader::galaxyShowBuildsById(const std::string& product_id, int build_i return; } +std::string parseLocationHelper(const std::string &location, const std::map &var) { + char search_arg[2] {'?', '>'}; + auto it = std::search(std::begin(location), std::end(location), std::begin(search_arg), std::end(search_arg)); + + if(it == std::end(location)) { + return location; + } + + std::string var_name { std::begin(location) + 2, it }; + auto relative_path = it + 2; + + auto var_value = var.find(var_name); + if(var_value == std::end(var)) { + return location; + } + + std::string parsedLocation; + parsedLocation.insert(std::end(parsedLocation), std::begin(var_value->second), std::end(var_value->second)); + parsedLocation.insert(std::end(parsedLocation), relative_path, std::end(location)); + + return parsedLocation; +} +std::string parseLocation(const std::string &location, const std::map &var) { + auto parsedLocation = parseLocationHelper(location, var); + Util::replaceAllString(parsedLocation, "\\", "/"); + + return parsedLocation; +} + +void Downloader::galaxyShowCloudSaves(const std::string& product_id, int build_index) +{ + std::string id; + if(this->galaxySelectProductIdHelper(product_id, id)) + { + if (!id.empty()) + this->galaxyShowCloudSavesById(id, build_index); + } +} + void Downloader::galaxyShowCloudSavesById(const std::string& product_id, int build_index) { std::string sPlatform; unsigned int iPlatform = Globals::globalConfig.dlConf.iGalaxyPlatform; - if (iPlatform == GlobalConstants::PLATFORM_LINUX) + if (iPlatform == GlobalConstants::PLATFORM_LINUX) { // Linux is not yet supported for cloud saves + std::cout << "Cloud saves for Linux builds not yet supported" << std::endl; return; + } else if (iPlatform == GlobalConstants::PLATFORM_MAC) sPlatform = "osx"; else @@ -4157,11 +4203,12 @@ void Downloader::galaxyShowCloudSavesById(const std::string& product_id, int bui std::string link = json["items"][build_index]["link"].asString(); + Json::Value manifest; if (json["items"][build_index]["generation"].asInt() == 2) { std::string buildHash; buildHash.assign(link.begin()+link.find_last_of("/")+1, link.end()); - json = gogGalaxy->getManifestV2(buildHash); + manifest = gogGalaxy->getManifestV2(buildHash); } else { @@ -4169,7 +4216,18 @@ void Downloader::galaxyShowCloudSavesById(const std::string& product_id, int bui return; } - json = gogGalaxy->getCloudPathAsJson(json["clientId"].asString()); + std::string install_directory; + if (Globals::globalConfig.dirConf.bSubDirectories) + { + install_directory = this->getGalaxyInstallDirectory(gogGalaxy, json); + } + std::string install_path = Globals::globalConfig.dirConf.sDirectory + install_directory; + std::string document_path = Globals::globalConfig.dirConf.sWinePrefix + "drive_c/users/" + username() + "/Documents/"; + std::string appdata_roaming = Globals::globalConfig.dirConf.sWinePrefix + "drive_c/users/" + username() + "/AppData/Roaming/"; + std::string appdata_local_path = Globals::globalConfig.dirConf.sWinePrefix + "drive_c/users/" + username() + "/AppData/Local/"; + std::string appdata_local_low_path = Globals::globalConfig.dirConf.sWinePrefix + "drive_c/users/" + username() + "/AppData/LocalLow/"; + std::string saved_games = Globals::globalConfig.dirConf.sWinePrefix + "drive_c/users/" + username() + "/Save Games/"; + std::string platform; switch(iPlatform) { @@ -4180,8 +4238,37 @@ void Downloader::galaxyShowCloudSavesById(const std::string& product_id, int bui platform = "Windows"; } + json = gogGalaxy->getCloudPathAsJson(manifest["clientId"].asString()); json = json["content"][platform]["cloudStorage"]["locations"]; - Json::StyledStreamWriter().write(std::cout, json); + + struct cloud_save_t { + std::string name; + std::string location; + }; + + std::map vars { + { "INSTALL", std::move(install_path) }, + { "DOCUMENTS", std::move(document_path) }, + { "APPLICATION_DATA_ROAMING", std::move(appdata_roaming)}, + { "APPLICATION_DATA_LOCAL", std::move(appdata_local_path) }, + { "APPLICATION_DATA_LOCAL_LOW", std::move(appdata_local_low_path) }, + { "SAVED_GAMES", std::move(saved_games) }, + }; + + std::vector cloud_saves; + for(auto &cloud_save : json) { + std::string location = parseLocation(cloud_save["location"].asString(), vars); + std::string name = cloud_save["name"].asString(); + cloud_saves.emplace_back(cloud_save_t { std::move(name), std::move(location) }); + } + + if(cloud_saves.empty()) { + std::cout << "No cloud save locations found" << std::endl; + } + + for(auto &cloud_save : cloud_saves) { + std::cout << cloud_save.name << "::" << cloud_save.location << std::endl; + } return; } diff --git a/src/util.cpp b/src/util.cpp index 5fd1c35..0ab87f9 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -391,6 +391,22 @@ int Util::replaceString(std::string& str, const std::string& to_replace, const s return 1; } +int Util::replaceAllString(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; + } + + do { + str.replace(str.begin()+pos, str.begin()+pos+to_replace.length(), replace_with); + + pos = str.find(to_replace, pos + to_replace.length()); + } while(pos != std::string::npos); + + return 1; +} + void Util::filepathReplaceReservedStrings(std::string& str, const std::string& gamename, const unsigned int& platformId, const std::string& dlcname) { std::string platform;