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"
This commit is contained in:
Sude 2013-11-14 15:40:59 +02:00
parent a63165d5a5
commit 0d9c27d20a
4 changed files with 173 additions and 20 deletions

View File

@ -19,7 +19,6 @@ class Config
bool bNoRemoteXML; bool bNoRemoteXML;
bool bNoCover; bool bNoCover;
bool bUpdateCheck; bool bUpdateCheck;
bool bHelp;
bool bDownload; bool bDownload;
bool bList; bool bList;
bool bListDetails; bool bListDetails;
@ -29,10 +28,11 @@ class Config
bool bNoExtras; bool bNoExtras;
bool bNoPatches; bool bNoPatches;
bool bNoLanguagePacks; bool bNoLanguagePacks;
bool bNoUnicode; // don't use Unicode in console output bool bUnicode; // use Unicode in console output
bool bNoColor; // don't use colors bool bColor; // use colors
bool bVerifyPeer; bool bVerifyPeer;
bool bCheckOrphans; bool bCheckOrphans;
bool bCheckStatus;
std::string sGameRegex; std::string sGameRegex;
std::string sDirectory; std::string sDirectory;
std::string sXMLFile; std::string sXMLFile;

View File

@ -49,6 +49,7 @@ class Downloader
void repair(); void repair();
void download(); void download();
void checkOrphans(); void checkOrphans();
void checkStatus();
CURL* curlhandle; CURL* curlhandle;
Timer timer; Timer timer;
Config config; Config config;
@ -64,6 +65,8 @@ class Downloader
size_t getResumePosition(); size_t getResumePosition();
CURLcode beginDownload(); CURLcode beginDownload();
std::string getResponse(const std::string& url); 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); int HTTP_Login(const std::string& email, const std::string& password);
std::vector< std::pair<std::string,std::string> > getGames(); std::vector< std::pair<std::string,std::string> > getGames();

View File

@ -76,6 +76,8 @@ int main(int argc, char *argv[])
try try
{ {
bool bInsecure = false; bool bInsecure = false;
bool bNoColor = false;
bool bNoUnicode = false;
desc.add_options() desc.add_options()
("help,h", "Print help message") ("help,h", "Print help message")
("login", bpo::value<bool>(&config.bLogin)->zero_tokens()->default_value(false), "Login") ("login", bpo::value<bool>(&config.bLogin)->zero_tokens()->default_value(false), "Login")
@ -98,12 +100,13 @@ int main(int argc, char *argv[])
("no-language-packs", bpo::value<bool>(&config.bNoLanguagePacks)->zero_tokens()->default_value(false), "Don't download/list/repair language packs") ("no-language-packs", bpo::value<bool>(&config.bNoLanguagePacks)->zero_tokens()->default_value(false), "Don't download/list/repair language packs")
("no-cover", bpo::value<bool>(&config.bNoCover)->zero_tokens()->default_value(false), "Don't download cover images") ("no-cover", bpo::value<bool>(&config.bNoCover)->zero_tokens()->default_value(false), "Don't download cover images")
("no-remote-xml", bpo::value<bool>(&config.bNoRemoteXML)->zero_tokens()->default_value(false), "Don't use remote XML for repair") ("no-remote-xml", bpo::value<bool>(&config.bNoRemoteXML)->zero_tokens()->default_value(false), "Don't use remote XML for repair")
("no-unicode", bpo::value<bool>(&config.bNoUnicode)->zero_tokens()->default_value(false), "Don't use Unicode in the progress bar") ("no-unicode", bpo::value<bool>(&bNoUnicode)->zero_tokens()->default_value(false), "Don't use Unicode in the progress bar")
("no-color", bpo::value<bool>(&config.bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar") ("no-color", bpo::value<bool>(&bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar")
("verbose", bpo::value<bool>(&config.bVerbose)->zero_tokens()->default_value(false), "Print lots of information") ("verbose", bpo::value<bool>(&config.bVerbose)->zero_tokens()->default_value(false), "Print lots of information")
("insecure", bpo::value<bool>(&bInsecure)->zero_tokens()->default_value(false), "Don't verify authenticity of SSL certificates") ("insecure", bpo::value<bool>(&bInsecure)->zero_tokens()->default_value(false), "Don't verify authenticity of SSL certificates")
("timeout", bpo::value<long int>(&config.iTimeout)->default_value(10), "Set timeout for connection\nMaximum time in seconds that connection phase is allowed to take") ("timeout", bpo::value<long int>(&config.iTimeout)->default_value(10), "Set timeout for connection\nMaximum time in seconds that connection phase is allowed to take")
("check-orphans", bpo::value<bool>(&config.bCheckOrphans)->zero_tokens()->default_value(false), "Check for orphaned files (files found on local filesystem that are not found on GOG servers)") ("check-orphans", bpo::value<bool>(&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<bool>(&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); 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.iDownloadRate <<= 10; // Convert download rate from bytes to kilobytes
config.bVerifyPeer = !bInsecure; config.bVerifyPeer = !bInsecure;
config.bColor = !bNoColor;
config.bUnicode = !bNoUnicode;
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -203,6 +208,8 @@ int main(int argc, char *argv[])
downloader.listGames(); downloader.listGames();
else if (config.bCheckOrphans) else if (config.bCheckOrphans)
downloader.checkOrphans(); downloader.checkOrphans();
else if (config.bCheckStatus)
downloader.checkStatus();
else else
{ // Show help message { // Show help message
std::cout << config.sVersionString << std::endl std::cout << config.sVersionString << std::endl

View File

@ -69,7 +69,7 @@ int Downloader::init()
gogAPI->curlSetOpt(CURLOPT_SSL_VERIFYPEER, config.bVerifyPeer); gogAPI->curlSetOpt(CURLOPT_SSL_VERIFYPEER, config.bVerifyPeer);
gogAPI->curlSetOpt(CURLOPT_CONNECTTIMEOUT, config.iTimeout); gogAPI->curlSetOpt(CURLOPT_CONNECTTIMEOUT, config.iTimeout);
progressbar = new ProgressBar(!config.bNoUnicode, !config.bNoColor); progressbar = new ProgressBar(config.bUnicode, config.bColor);
bool bInitOK = gogAPI->init(); bool bInitOK = gogAPI->init();
if (config.bLogin || !bInitOK) 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(); this->getGameDetails();
} }
@ -520,6 +520,7 @@ void Downloader::download()
{ {
std::cout << "Starting automatic XML creation" << std::endl; std::cout << "Starting automatic XML creation" << std::endl;
Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); 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; std::cout << "Starting automatic XML creation" << std::endl;
Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); 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; std::cout << "Starting automatic XML creation" << std::endl;
Util::createXML(filepath, config.iChunkSize, config.sXMLDirectory); 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 // Using local XML data for version check before resuming
boost::filesystem::path local_xml_file; 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 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()) if (!xml_data.empty())
{ {
// Do version check if local XML file exists // Do version check if local hash exists
if (bLocalXMLExists) if (!localHash.empty())
{ {
TiXmlDocument remote_xml, local_xml; TiXmlDocument remote_xml;
remote_xml.Parse(xml_data.c_str()); remote_xml.Parse(xml_data.c_str());
local_xml.LoadFile(local_xml_file.string());
TiXmlNode *fileNodeRemote = remote_xml.FirstChild("file"); TiXmlNode *fileNodeRemote = remote_xml.FirstChild("file");
TiXmlNode *fileNodeLocal = local_xml.FirstChild("file"); if (fileNodeRemote)
if (fileNodeRemote && fileNodeLocal)
{ {
TiXmlElement *fileElemRemote = fileNodeRemote->ToElement(); TiXmlElement *fileElemRemote = fileNodeRemote->ToElement();
TiXmlElement *fileElemLocal = fileNodeLocal->ToElement();
std::string remoteHash = fileElemRemote->Attribute("md5"); std::string remoteHash = fileElemRemote->Attribute("md5");
std::string localHash = fileElemLocal->Attribute("md5");
if (remoteHash != localHash) if (remoteHash != localHash)
bSameVersion = false; bSameVersion = false;
} }
@ -1518,3 +1515,149 @@ void Downloader::checkOrphans()
return; 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;
}