From 0d9c27d20a3b94ca612d9eb68412126469742c8d Mon Sep 17 00:00:00 2001 From: Sude Date: Thu, 14 Nov 2013 15:40:59 +0200 Subject: [PATCH] Added --status command and made createXML output easier to read Version check before download now works without local XML data. However it is much slower without the XML data because it calculates MD5 for the files Some changes to config.h - Removed "bHelp" from config because it was not needed - Changed "bNoColor" to "bColor" - Changed "bNoUnicode" to "bUnicode" --- include/config.h | 6 +- include/downloader.h | 3 + main.cpp | 11 ++- src/downloader.cpp | 173 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 173 insertions(+), 20 deletions(-) diff --git a/include/config.h b/include/config.h index c848176..e266794 100644 --- a/include/config.h +++ b/include/config.h @@ -19,7 +19,6 @@ class Config bool bNoRemoteXML; bool bNoCover; bool bUpdateCheck; - bool bHelp; bool bDownload; bool bList; bool bListDetails; @@ -29,10 +28,11 @@ class Config bool bNoExtras; bool bNoPatches; bool bNoLanguagePacks; - bool bNoUnicode; // don't use Unicode in console output - bool bNoColor; // don't use colors + bool bUnicode; // use Unicode in console output + bool bColor; // use colors bool bVerifyPeer; bool bCheckOrphans; + bool bCheckStatus; std::string sGameRegex; std::string sDirectory; std::string sXMLFile; diff --git a/include/downloader.h b/include/downloader.h index d6dd25a..5431d33 100644 --- a/include/downloader.h +++ b/include/downloader.h @@ -49,6 +49,7 @@ class Downloader void repair(); void download(); void checkOrphans(); + void checkStatus(); CURL* curlhandle; Timer timer; Config config; @@ -64,6 +65,8 @@ class Downloader size_t getResumePosition(); CURLcode beginDownload(); std::string getResponse(const std::string& url); + std::string getLocalFileHash(const std::string& filepath); + std::string getRemoteFileHash(const std::string& gamename, const std::string& id); int HTTP_Login(const std::string& email, const std::string& password); std::vector< std::pair > getGames(); diff --git a/main.cpp b/main.cpp index 6276054..4912296 100644 --- a/main.cpp +++ b/main.cpp @@ -76,6 +76,8 @@ int main(int argc, char *argv[]) try { bool bInsecure = false; + bool bNoColor = false; + bool bNoUnicode = false; desc.add_options() ("help,h", "Print help message") ("login", bpo::value(&config.bLogin)->zero_tokens()->default_value(false), "Login") @@ -98,12 +100,13 @@ int main(int argc, char *argv[]) ("no-language-packs", bpo::value(&config.bNoLanguagePacks)->zero_tokens()->default_value(false), "Don't download/list/repair language packs") ("no-cover", bpo::value(&config.bNoCover)->zero_tokens()->default_value(false), "Don't download cover images") ("no-remote-xml", bpo::value(&config.bNoRemoteXML)->zero_tokens()->default_value(false), "Don't use remote XML for repair") - ("no-unicode", bpo::value(&config.bNoUnicode)->zero_tokens()->default_value(false), "Don't use Unicode in the progress bar") - ("no-color", bpo::value(&config.bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar") + ("no-unicode", bpo::value(&bNoUnicode)->zero_tokens()->default_value(false), "Don't use Unicode in the progress bar") + ("no-color", bpo::value(&bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar") ("verbose", bpo::value(&config.bVerbose)->zero_tokens()->default_value(false), "Print lots of information") ("insecure", bpo::value(&bInsecure)->zero_tokens()->default_value(false), "Don't verify authenticity of SSL certificates") ("timeout", bpo::value(&config.iTimeout)->default_value(10), "Set timeout for connection\nMaximum time in seconds that connection phase is allowed to take") ("check-orphans", bpo::value(&config.bCheckOrphans)->zero_tokens()->default_value(false), "Check for orphaned files (files found on local filesystem that are not found on GOG servers)") + ("status", bpo::value(&config.bCheckStatus)->zero_tokens()->default_value(false), "Show status of files\n\nOutput format:\nstatuscode gamename filename filesize filehash\n\nStatus codes:\nOK - File is OK\nND - File is not downloaded\nMD5 - MD5 mismatch, different version") ; bpo::store(bpo::parse_command_line(argc, argv, desc), vm); @@ -145,6 +148,8 @@ int main(int argc, char *argv[]) config.iDownloadRate <<= 10; // Convert download rate from bytes to kilobytes config.bVerifyPeer = !bInsecure; + config.bColor = !bNoColor; + config.bUnicode = !bNoUnicode; } catch (std::exception& e) { @@ -203,6 +208,8 @@ int main(int argc, char *argv[]) downloader.listGames(); else if (config.bCheckOrphans) downloader.checkOrphans(); + else if (config.bCheckStatus) + downloader.checkStatus(); else { // Show help message std::cout << config.sVersionString << std::endl diff --git a/src/downloader.cpp b/src/downloader.cpp index e01f54b..0e63d27 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -69,7 +69,7 @@ int Downloader::init() gogAPI->curlSetOpt(CURLOPT_SSL_VERIFYPEER, config.bVerifyPeer); gogAPI->curlSetOpt(CURLOPT_CONNECTTIMEOUT, config.iTimeout); - progressbar = new ProgressBar(!config.bNoUnicode, !config.bNoColor); + progressbar = new ProgressBar(config.bUnicode, config.bColor); bool bInitOK = gogAPI->init(); if (config.bLogin || !bInitOK) @@ -209,7 +209,7 @@ void Downloader::getGameList() } } - if (config.bListDetails || config.bDownload || config.bRepair || config.bCheckOrphans) + if (config.bListDetails || config.bDownload || config.bRepair || config.bCheckOrphans || config.bCheckStatus) this->getGameDetails(); } @@ -520,6 +520,7 @@ void Downloader::download() { std::cout << "Starting automatic XML creation" << std::endl; Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); + std::cout << std::endl; } } } @@ -552,6 +553,7 @@ void Downloader::download() { std::cout << "Starting automatic XML creation" << std::endl; Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); + std::cout << std::endl; } } } @@ -584,6 +586,7 @@ void Downloader::download() { std::cout << "Starting automatic XML creation" << std::endl; Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); + std::cout << std::endl; } } } @@ -606,30 +609,24 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil // Using local XML data for version check before resuming boost::filesystem::path local_xml_file; - if (config.sXMLDirectory.empty()) - local_xml_file = config.sHome + "/.gogdownloader/xml/" + filenameXML; - else - local_xml_file = config.sXMLDirectory + "/" + filenameXML; + local_xml_file = config.sXMLDirectory + "/" + filenameXML; bool bSameVersion = true; // assume same version - bool bLocalXMLExists = boost::filesystem::exists(local_xml_file); + bool bLocalXMLExists = boost::filesystem::exists(local_xml_file); // This is additional check to see if remote xml should be saved to speed up future version checks + std::string localHash = this->getLocalFileHash(filepath); if (!xml_data.empty()) { - // Do version check if local XML file exists - if (bLocalXMLExists) + // Do version check if local hash exists + if (!localHash.empty()) { - TiXmlDocument remote_xml, local_xml; + TiXmlDocument remote_xml; remote_xml.Parse(xml_data.c_str()); - local_xml.LoadFile(local_xml_file.string()); TiXmlNode *fileNodeRemote = remote_xml.FirstChild("file"); - TiXmlNode *fileNodeLocal = local_xml.FirstChild("file"); - if (fileNodeRemote && fileNodeLocal) + if (fileNodeRemote) { TiXmlElement *fileElemRemote = fileNodeRemote->ToElement(); - TiXmlElement *fileElemLocal = fileNodeLocal->ToElement(); std::string remoteHash = fileElemRemote->Attribute("md5"); - std::string localHash = fileElemLocal->Attribute("md5"); if (remoteHash != localHash) bSameVersion = false; } @@ -1518,3 +1515,149 @@ void Downloader::checkOrphans() return; } + +// Check status of files +void Downloader::checkStatus() +{ + for (unsigned int i = 0; i < games.size(); ++i) + { + if (!config.bNoInstallers) + { + for (unsigned int j = 0; j < games[i].installers.size(); ++j) + { + boost::filesystem::path filepath = Util::makeFilepath(config.sDirectory, games[i].installers[j].path, games[i].gamename); + + std::string remoteHash; + std::string localHash; + bool bHashOK = true; // assume hash OK + size_t filesize; + + localHash = this->getLocalFileHash(filepath.string()); + remoteHash = this->getRemoteFileHash(games[i].gamename, games[i].installers[j].id); + + if (boost::filesystem::exists(filepath)) + { + filesize = boost::filesystem::file_size(filepath); + + if (remoteHash != localHash) + bHashOK = false; + + std::cout << (bHashOK ? "OK " : "MD5 ") << games[i].gamename << " " << filepath.filename().string() << " " << filesize << " " << localHash << std::endl; + } + else + { + std::cout << "ND " << games[i].gamename << " " << filepath.filename().string() << std::endl; + } + } + } + + if (!config.bNoExtras) + { + for (unsigned int j = 0; j < games[i].extras.size(); ++j) + { + boost::filesystem::path filepath = Util::makeFilepath(config.sDirectory, games[i].extras[j].path, games[i].gamename); + + std::string localHash = this->getLocalFileHash(filepath.string()); + size_t filesize; + + if (boost::filesystem::exists(filepath)) + { + filesize = boost::filesystem::file_size(filepath); + std::cout << "OK " << games[i].gamename << " " << filepath.filename().string() << " " << filesize << " " << localHash << std::endl; + } + else + { + std::cout << "ND " << games[i].gamename << " " << filepath.filename().string() << std::endl; + } + } + } + + if (!config.bNoPatches) + { + for (unsigned int j = 0; j < games[i].patches.size(); ++j) + { + boost::filesystem::path filepath = Util::makeFilepath(config.sDirectory, games[i].patches[j].path, games[i].gamename); + + std::string localHash = this->getLocalFileHash(filepath.string()); + size_t filesize; + + if (boost::filesystem::exists(filepath)) + { + filesize = boost::filesystem::file_size(filepath); + std::cout << "OK " << games[i].gamename << " " << filepath.filename().string() << " " << filesize << " " << localHash << std::endl; + } + else + { + std::cout << "ND " << games[i].gamename << " " << filepath.filename().string() << std::endl; + } + } + } + + if (!config.bNoLanguagePacks) + { + for (unsigned int j = 0; j < games[i].languagepacks.size(); ++j) + { + boost::filesystem::path filepath = Util::makeFilepath(config.sDirectory, games[i].languagepacks[j].path, games[i].gamename); + + std::string localHash = this->getLocalFileHash(filepath.string()); + size_t filesize; + + if (boost::filesystem::exists(filepath)) + { + filesize = boost::filesystem::file_size(filepath); + std::cout << "OK " << games[i].gamename << " " << filepath.filename().string() << " " << filesize << " " << localHash << std::endl; + } + else + { + std::cout << "ND " << games[i].gamename << " " << filepath.filename().string() << std::endl; + } + } + } + } + + return; +} + +std::string Downloader::getLocalFileHash(const std::string& filepath) +{ + std::string localHash; + boost::filesystem::path path = filepath; + boost::filesystem::path local_xml_file = config.sXMLDirectory + "/" + path.filename().string() + ".xml"; + if (boost::filesystem::exists(local_xml_file)) + { + TiXmlDocument local_xml; + local_xml.LoadFile(local_xml_file.string()); + TiXmlNode *fileNodeLocal = local_xml.FirstChild("file"); + if (fileNodeLocal) + { + TiXmlElement *fileElemLocal = fileNodeLocal->ToElement(); + localHash = fileElemLocal->Attribute("md5"); + } + } + else + { + if (boost::filesystem::exists(path)) + { + localHash = Util::getFileHash(path.string(), RHASH_MD5); + } + } + return localHash; +} + +std::string Downloader::getRemoteFileHash(const std::string& gamename, const std::string& id) +{ + std::string remoteHash; + std::string xml_data = gogAPI->getXML(gamename, id); + if (!xml_data.empty()) + { + TiXmlDocument remote_xml; + remote_xml.Parse(xml_data.c_str()); + TiXmlNode *fileNodeRemote = remote_xml.FirstChild("file"); + if (fileNodeRemote) + { + TiXmlElement *fileElemRemote = fileNodeRemote->ToElement(); + remoteHash = fileElemRemote->Attribute("md5"); + } + } + return remoteHash; +}