Initial commit

This commit is contained in:
Sude 2013-03-15 22:46:16 +02:00
commit 2d045c4052
14 changed files with 2526 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.layout
*~
*.[oa]
bin/*
obj/*

14
COPYING Normal file
View File

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

124
Makefile Normal file
View File

@ -0,0 +1,124 @@
#------------------------------------------------------------------------------#
# This makefile was generated by 'cbp2make' tool rev.127 #
#------------------------------------------------------------------------------#
WORKDIR = `pwd`
CC = gcc
CXX = g++
AR = ar
LD = g++
WINDRES = windres
INC = -Iinclude
CFLAGS = -std=c++0x -Wall -fexceptions
RESINC =
LIBDIR =
LIB = -lcurl -loauth -ljsoncpp -lhtmlcxx -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lboost_date_time -ltinyxml -lrhash
LDFLAGS =
INC_DEBUG = $(INC)
CFLAGS_DEBUG = $(CFLAGS) -g -DDEBUG
RESINC_DEBUG = $(RESINC)
RCFLAGS_DEBUG = $(RCFLAGS)
LIBDIR_DEBUG = $(LIBDIR)
LIB_DEBUG = $(LIB)
LDFLAGS_DEBUG = $(LDFLAGS)
OBJDIR_DEBUG = obj/Debug
DEP_DEBUG =
OUT_DEBUG = bin/Debug/lgogdownloader
INC_RELEASE = $(INC)
CFLAGS_RELEASE = $(CFLAGS) -O2
RESINC_RELEASE = $(RESINC)
RCFLAGS_RELEASE = $(RCFLAGS)
LIBDIR_RELEASE = $(LIBDIR)
LIB_RELEASE = $(LIB)
LDFLAGS_RELEASE = $(LDFLAGS) -s
OBJDIR_RELEASE = obj/Release
DEP_RELEASE =
OUT_RELEASE = bin/Release/lgogdownloader
OBJ_DEBUG = $(OBJDIR_DEBUG)/main.o $(OBJDIR_DEBUG)/src/api.o $(OBJDIR_DEBUG)/src/downloader.o $(OBJDIR_DEBUG)/src/progressbar.o $(OBJDIR_DEBUG)/src/util.o
OBJ_RELEASE = $(OBJDIR_RELEASE)/main.o $(OBJDIR_RELEASE)/src/api.o $(OBJDIR_RELEASE)/src/downloader.o $(OBJDIR_RELEASE)/src/progressbar.o $(OBJDIR_RELEASE)/src/util.o
all: debug release
clean: clean_debug clean_release
before_debug:
test -d bin/Debug || mkdir -p bin/Debug
test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG)
test -d $(OBJDIR_DEBUG)/src || mkdir -p $(OBJDIR_DEBUG)/src
after_debug:
debug: before_debug out_debug after_debug
out_debug: $(OBJ_DEBUG) $(DEP_DEBUG)
$(LD) $(LDFLAGS_DEBUG) $(LIBDIR_DEBUG) $(OBJ_DEBUG) $(LIB_DEBUG) -o $(OUT_DEBUG)
$(OBJDIR_DEBUG)/main.o: main.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c main.cpp -o $(OBJDIR_DEBUG)/main.o
$(OBJDIR_DEBUG)/src/api.o: src/api.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/api.cpp -o $(OBJDIR_DEBUG)/src/api.o
$(OBJDIR_DEBUG)/src/downloader.o: src/downloader.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/downloader.cpp -o $(OBJDIR_DEBUG)/src/downloader.o
$(OBJDIR_DEBUG)/src/progressbar.o: src/progressbar.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/progressbar.cpp -o $(OBJDIR_DEBUG)/src/progressbar.o
$(OBJDIR_DEBUG)/src/util.o: src/util.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/util.cpp -o $(OBJDIR_DEBUG)/src/util.o
clean_debug:
rm -f $(OBJ_DEBUG) $(OUT_DEBUG)
rm -rf bin/Debug
rm -rf $(OBJDIR_DEBUG)
rm -rf $(OBJDIR_DEBUG)/src
before_release:
test -d bin/Release || mkdir -p bin/Release
test -d $(OBJDIR_RELEASE) || mkdir -p $(OBJDIR_RELEASE)
test -d $(OBJDIR_RELEASE)/src || mkdir -p $(OBJDIR_RELEASE)/src
after_release:
release: before_release out_release after_release
out_release: $(OBJ_RELEASE) $(DEP_RELEASE)
$(LD) $(LDFLAGS_RELEASE) $(LIBDIR_RELEASE) $(OBJ_RELEASE) $(LIB_RELEASE) -o $(OUT_RELEASE)
$(OBJDIR_RELEASE)/main.o: main.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c main.cpp -o $(OBJDIR_RELEASE)/main.o
$(OBJDIR_RELEASE)/src/api.o: src/api.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/api.cpp -o $(OBJDIR_RELEASE)/src/api.o
$(OBJDIR_RELEASE)/src/downloader.o: src/downloader.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/downloader.cpp -o $(OBJDIR_RELEASE)/src/downloader.o
$(OBJDIR_RELEASE)/src/progressbar.o: src/progressbar.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/progressbar.cpp -o $(OBJDIR_RELEASE)/src/progressbar.o
$(OBJDIR_RELEASE)/src/util.o: src/util.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/util.cpp -o $(OBJDIR_RELEASE)/src/util.o
clean_release:
rm -f $(OBJ_RELEASE) $(OUT_RELEASE)
rm -rf bin/Release
rm -rf $(OBJDIR_RELEASE)
rm -rf $(OBJDIR_RELEASE)/src
install:
install $(OUT_RELEASE) /usr/bin
uninstall:
rm /usr/bin/lgogdownloader
.PHONY: before_debug after_debug clean_debug before_release after_release clean_release

112
include/api.h Normal file
View File

@ -0,0 +1,112 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef API_H
#define API_H
#include <iostream>
#include <vector>
#include <curl/curl.h>
extern "C" {
#include <oauth.h>
}
#include <cstring>
#define CONSUMER_KEY "1f444d14ea8ec776585524a33f6ecc1c413ed4a5"
#define CONSUMER_SECRET "20d175147f9db9a10fc0584aa128090217b9cf88"
#define OAUTH_VERIFIER_LENGTH 14
#define OAUTH_TOKEN_LENGTH 11
#define OAUTH_SECRET_LENGTH 18
#define INSTALLER_WINDOWS 1
#define INSTALLER_MAC 2
#define LANGUAGE_EN 1
#define LANGUAGE_DE 2
#define LANGUAGE_FR 4
#define LANGUAGE_PL 8
#define LANGUAGE_RU 16
class gameFile {
public:
gameFile(const bool& t_updated, const std::string& t_id, const std::string& t_name, const std::string& t_path, const std::string& t_size);
bool updated;
std::string id;
std::string name;
std::string path;
std::string size;
virtual ~gameFile();
};
class gameDetails {
public:
std::vector<gameFile *> extras;
std::vector<gameFile *> installers;
std::string gamename;
std::string title;
std::string icon;
};
class userDetails {
public:
std::string avatar_small;
std::string avatar_big;
std::string username;
std::string email;
unsigned long long id;
int notifications_forum;
int notifications_games;
int notifications_messages;
};
class apiConfig {
public:
std::string oauth_authorize_temp_token;
std::string oauth_get_temp_token;
std::string oauth_get_token;
std::string get_user_games;
std::string get_user_details;
std::string get_installer_link;
std::string get_game_details;
std::string get_extra_link;
std::string set_app_status;
std::string oauth_token;
std::string oauth_secret;
};
size_t writeMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userp);
class API
{
public:
apiConfig config;
userDetails user;
API(const std::string& token,const std::string& secret);
int init();
int login(const std::string& email, const std::string& password);
int getAPIConfig();
std::string getResponse(const std::string& url);
std::string getResponseOAuth(const std::string& url);
int getUserDetails();
int getGames();
gameDetails getGameDetails(const std::string& game_name, const unsigned int& type = INSTALLER_WINDOWS, const unsigned int& lang = LANGUAGE_EN);
std::string getInstallerLink(const std::string& game_name, const std::string& id);
std::string getExtraLink(const std::string& game_name, const std::string& id);
std::string getXML(const std::string& game_name, const std::string& id);
void clearError();
bool getError() { return this->error; };
std::string getErrorMessage() { return this->error_message; };
virtual ~API();
protected:
private:
CURL* curlhandle;
void setError(const std::string& err);
bool error;
std::string error_message;
};
#endif // API_H

48
include/config.h Normal file
View File

@ -0,0 +1,48 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef CONFIG_H__
#define CONFIG_H__
#include <iostream>
#include <curl/curl.h>
class Config
{
public:
Config() {};
virtual ~Config() {};
bool bVerbose;
bool bNoRemoteXML;
bool bNoCover;
bool bUpdateCheck;
bool bHelp;
bool bDownload;
bool bList;
bool bListDetails;
bool bLogin;
bool bRepair;
bool bNoInstallers;
bool bNoExtras;
bool bNoUnicode; // don't use Unicode in console output
bool bNoColor; // don't use colors
std::string sGameRegex;
std::string sDirectory;
std::string sXMLFile;
std::string sXMLDirectory;
std::string sToken;
std::string sSecret;
std::string sVersionString;
std::string sHome;
std::string sCookiePath;
std::string sConfigFilePath;
unsigned int iInstallerType;
unsigned int iInstallerLanguage;
size_t iChunkSize;
curl_off_t iDownloadRate;
};
#endif // CONFIG_H__

85
include/downloader.h Normal file
View File

@ -0,0 +1,85 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include "config.h"
#include "api.h"
#include "progressbar.h"
#include <curl/curl.h>
#include <ctime>
#if __GNUC__
# if __x86_64__ || __ppc64__ || __LP64__
# define ENVIRONMENT64
# else
# define ENVIRONMENT32
# endif
#endif
class Timer
{
public:
Timer() { this->reset(); };
void reset() { gettimeofday(&(this->last_update), NULL); };
double getTimeBetweenUpdates()
{ // Returns time elapsed between updates in milliseconds
struct timeval time_now;
gettimeofday(&time_now, NULL);
double time_between = ( (time_now.tv_sec+(time_now.tv_usec/1000000.0))*1000.0 - (this->last_update.tv_sec+(this->last_update.tv_usec/1000000.0))*1000.0 );
return time_between;
};
~Timer() {};
private:
struct timeval last_update;
};
class Downloader
{
public:
Downloader(Config &conf);
virtual ~Downloader();
int init();
void listGames();
void updateCheck();
void repair();
void download();
CURL* curlhandle;
Timer timer;
Config config;
ProgressBar* progressbar;
protected:
private:
CURLcode downloadFile(std::string url, std::string filepath);
int repairFile(std::string url, std::string filepath, std::string xml_data = std::string(), std::string xml_dir = std::string());
int downloadCovers(std::string gamename, std::string directory, std::string cover_xml_data);
int login();
int getGameDetails();
void getGameList();
size_t getResumePosition();
CURLcode beginDownload();
std::string getResponse(const std::string& url);
int HTTP_Login(const std::string& email, const std::string& password);
std::vector<std::string> getGames();
std::vector<std::string> getFreeGames();
static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
static size_t writeMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userp);
static size_t writeData(void *ptr, size_t size, size_t nmemb, FILE *stream);
static size_t readData(void *ptr, size_t size, size_t nmemb, FILE *stream);
API *gogAPI;
std::vector<std::string> gameNames;
std::vector<gameDetails> games;
std::string coverXML;
size_t resume_position;
};
#endif // DOWNLOADER_H

35
include/progressbar.h Normal file
View File

@ -0,0 +1,35 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef PROGRESSBAR_H
#define PROGRESSBAR_H
#include <iostream>
#include <vector>
class ProgressBar
{
public:
ProgressBar(bool bUnicode, bool bColor);
virtual ~ProgressBar();
void draw(unsigned int length, double fraction);
protected:
private:
std::vector<std::string> const m_bar_chars;
std::string const m_left_border;
std::string const m_right_border;
std::string const m_simple_left_border;
std::string const m_simple_right_border;
std::string const m_simple_empty_fill;
std::string const m_simple_bar_char;
std::string const m_bar_color;
std::string const m_border_color;
std::string const COLOR_RESET;
bool m_use_unicode;
bool m_use_color;
};
#endif // PROGRESSBAR_H

26
include/util.h Normal file
View File

@ -0,0 +1,26 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#ifndef UTIL_H
#define UTIL_H
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <iostream>
#include <sstream>
#include <rhash.h>
namespace Util
{
std::string makeFilepath(const std::string& directory, const std::string& path, const std::string& gamename);
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());
}
#endif // UTIL_H

67
lgogdownloader.cbp Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="lgogdownloader" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin/Debug/lgogdownloader" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Debug/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
<Add option="-DDEBUG" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin/Release/lgogdownloader" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Release/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-std=c++0x" />
<Add option="-Wall" />
<Add option="-fexceptions" />
<Add directory="include" />
</Compiler>
<Linker>
<Add library="curl" />
<Add library="oauth" />
<Add library="jsoncpp" />
<Add library="htmlcxx" />
<Add library="boost_system" />
<Add library="boost_filesystem" />
<Add library="boost_regex" />
<Add library="boost_program_options" />
<Add library="boost_date_time" />
<Add library="tinyxml" />
<Add library="rhash" />
</Linker>
<Unit filename="include/api.h" />
<Unit filename="include/config.h" />
<Unit filename="include/downloader.h" />
<Unit filename="include/progressbar.h" />
<Unit filename="include/util.h" />
<Unit filename="main.cpp" />
<Unit filename="src/api.cpp" />
<Unit filename="src/downloader.cpp" />
<Unit filename="src/progressbar.cpp" />
<Unit filename="src/util.cpp" />
<Extensions>
<code_completion />
<debugger />
<envvars />
</Extensions>
</Project>
</CodeBlocks_project_file>

190
main.cpp Normal file
View File

@ -0,0 +1,190 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "downloader.h"
#include "config.h"
#include "util.h"
#include <unistd.h> // getpass
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <tinyxml.h>
#if __GNUC__
# if __x86_64__ || __ppc64__ || __LP64__
# define ENVIRONMENT64
# else
# define ENVIRONMENT32
# endif
#endif
namespace bpo = boost::program_options;
int main(int argc, char *argv[])
{
Config config;
config.sVersionString = "LGOGDownloader 2.2";
config.sHome = (std::string)getenv("HOME");
config.sCookiePath = config.sHome + "/.gogdownloader/cookies.txt";
config.sConfigFilePath = config.sHome + "/.gogdownloader/config.cfg";
config.sXMLDirectory = config.sHome + "/.gogdownloader/xml";
// Create gogdownloader directories
boost::filesystem::path path = config.sXMLDirectory;
if (!boost::filesystem::exists(path))
{
if (!boost::filesystem::create_directories(path))
{
std::cout << "Failed to create directory: " << path << std::endl;
return 1;
}
}
// Create help text for --platform option
std::string platform_text = "Select which installers are downloaded\n"
+ std::to_string(INSTALLER_WINDOWS) + " = Windows\n"
+ std::to_string(INSTALLER_MAC) + " = Mac\n"
+ std::to_string(INSTALLER_WINDOWS | INSTALLER_MAC) + " = Both";
// Create help text for --language option
std::string language_text = "Select which language installers are downloaded\n"
+ std::to_string(LANGUAGE_EN) + " = English\n"
+ std::to_string(LANGUAGE_DE) + " = German\n"
+ std::to_string(LANGUAGE_FR) + " = French\n"
+ std::to_string(LANGUAGE_PL) + " = Polish\n"
+ std::to_string(LANGUAGE_RU) + " = Russian\n"
+ "Add the values to download multiple languages\n"
+ "All = " + std::to_string(LANGUAGE_EN) + "+" + std::to_string(LANGUAGE_DE) + "+" + std::to_string(LANGUAGE_FR) + "+" + std::to_string(LANGUAGE_PL) + "+" + std::to_string(LANGUAGE_RU) + " = "
+ std::to_string(LANGUAGE_EN | LANGUAGE_DE | LANGUAGE_FR | LANGUAGE_PL | LANGUAGE_RU) + "\n"
+ "French + Polish = " + std::to_string(LANGUAGE_FR) + "+" + std::to_string(LANGUAGE_PL) + " = " + std::to_string(LANGUAGE_FR | LANGUAGE_PL);
bpo::variables_map vm;
bpo::options_description desc("Options");
bpo::options_description config_file_options("Configuration");
try
{
desc.add_options()
("help,h", "Print help message")
("login", bpo::value<bool>(&config.bLogin)->zero_tokens()->default_value(false), "Login")
("list", bpo::value<bool>(&config.bList)->zero_tokens()->default_value(false), "List games")
("list-details", bpo::value<bool>(&config.bListDetails)->zero_tokens()->default_value(false), "List games with detailed info")
("download", bpo::value<bool>(&config.bDownload)->zero_tokens()->default_value(false), "Download")
("repair", bpo::value<bool>(&config.bRepair)->zero_tokens()->default_value(false), "Repair downloaded files")
("game", bpo::value<std::string>(&config.sGameRegex)->default_value(""), "Set regular expression filter\nfor download/list/repair (Perl syntax)\nAliases: \"all\", \"free\"")
("directory", bpo::value<std::string>(&config.sDirectory)->default_value(""), "Set download directory")
#ifndef ENVIRONMENT32
("limit-rate", bpo::value<curl_off_t>(&config.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited")
#endif
("create-xml", bpo::value<std::string>(&config.sXMLFile)->default_value(""), "Create GOG XML for file\n\"automatic\" to enable automatic XML creation")
("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")
("update-check", bpo::value<bool>(&config.bUpdateCheck)->zero_tokens()->default_value(false), "Check for update notifications")
("platform", bpo::value<unsigned int>(&config.iInstallerType)->default_value(INSTALLER_WINDOWS), platform_text.c_str())
("language", bpo::value<unsigned int>(&config.iInstallerLanguage)->default_value(LANGUAGE_EN), language_text.c_str())
("no-installers", bpo::value<bool>(&config.bNoInstallers)->zero_tokens()->default_value(false), "Don't download/list/repair installers")
("no-extras", bpo::value<bool>(&config.bNoExtras)->zero_tokens()->default_value(false), "Don't download/list/repair extras")
("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-unicode", bpo::value<bool>(&config.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")
("verbose", bpo::value<bool>(&config.bVerbose)->zero_tokens()->default_value(false), "Print lots of information")
;
bpo::store(bpo::parse_command_line(argc, argv, desc), vm);
bpo::notify(vm);
// Read token and secret from config file
config_file_options.add_options()
("token", bpo::value<std::string>(&config.sToken)->default_value(""), "oauth token")
("secret", bpo::value<std::string>(&config.sSecret)->default_value(""), "oauth secret")
;
if (boost::filesystem::exists(config.sConfigFilePath))
{
std::ifstream ifs(config.sConfigFilePath.c_str());
if (!ifs)
{
std::cout << "Could not open config file: " << config.sConfigFilePath << std::endl;
return 1;
}
else
{
bpo::store(bpo::parse_config_file(ifs, config_file_options), vm);
bpo::notify(vm);
ifs.close();
}
}
if (vm.count("help"))
{
std::cout << config.sVersionString << std::endl
<< desc << std::endl;
return 0;
}
if (vm.count("chunk-size"))
config.iChunkSize <<= 20; // Convert chunk size from bytes to megabytes
if (vm.count("limit-rate"))
config.iDownloadRate <<= 10; // Convert download rate from bytes to kilobytes
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
catch (...)
{
std::cerr << "Exception of unknown type!" << std::endl;
return 1;
}
if (config.iInstallerType < INSTALLER_WINDOWS || config.iInstallerType > (INSTALLER_WINDOWS | INSTALLER_MAC))
{
std::cout << "Invalid value for --platform" << std::endl;
return 1;
}
if (!config.sXMLDirectory.empty())
{
// Make sure that xml directory doesn't have trailing slash
if (config.sXMLDirectory.at(config.sXMLDirectory.length()-1)=='/')
config.sXMLDirectory.assign(config.sXMLDirectory.begin(),config.sXMLDirectory.end()-1);
}
// Create GOG XML for a file
if (!config.sXMLFile.empty() && (config.sXMLFile != "automatic"))
{
Util::createXML(config.sXMLFile, config.iChunkSize, config.sXMLDirectory);
return 0;
}
// Make sure that directory has trailing slash
if (!config.sDirectory.empty())
if (config.sDirectory.at(config.sDirectory.length()-1)!='/')
config.sDirectory += "/";
Downloader downloader(config);
int result = downloader.init();
if (config.bLogin)
return result;
else if (config.bDownload) // Download games
downloader.download();
else if (config.bRepair) // Repair file
downloader.repair();
else if (config.bListDetails || config.bList) // Detailed list of games/extras
downloader.listGames();
else if (config.bUpdateCheck) // Detailed list of games/extras
downloader.updateCheck();
else
{ // Show help message
std::cout << config.sVersionString << std::endl
<< desc << std::endl;
}
return 0;
}

528
src/api.cpp Normal file
View File

@ -0,0 +1,528 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "api.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <jsoncpp/json/json.h>
size_t writeMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userp) {
std::ostringstream *stream = (std::ostringstream*)userp;
size_t count = size * nmemb;
stream->write(ptr, count);
return count;
}
gameFile::gameFile(const bool& t_updated, const std::string& t_id, const std::string& t_name, const std::string& t_path, const std::string& t_size)
{
this->updated = t_updated;
this->id = t_id;
this->name = t_name;
this->path = t_path;
this->size = t_size;
}
gameFile::~gameFile()
{
}
API::API(const std::string& token, const std::string& secret)
{
curlhandle = curl_easy_init();
curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 0);
curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, 5);
curl_easy_setopt(curlhandle, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(curlhandle, CURLOPT_FAILONERROR, true);
curl_easy_setopt(curlhandle, CURLOPT_SSL_VERIFYPEER, 0);
this->error = false;
this->getAPIConfig();
this->config.oauth_token = token;
this->config.oauth_secret = secret;
}
int API::init()
{
int res = 0;
// Check if we already have token and secret
if (!this->config.oauth_token.empty() && !this->config.oauth_secret.empty())
{
// Test authorization by getting user details
res = this->getUserDetails(); // res = 1 if successful
}
return res;
}
int API::getAPIConfig()
{
std::string url = "https://api.gog.com/en/downloader2/status/stable/"; // Stable API
//std::string url = "https://api.gog.com/en/downloader2/status/beta/"; // Beta API
//std::string url = "https://api.gog.com/en/downloader2/status/e77989ed21758e78331b20e477fc5582/"; // Development API? Not sure because the downloader version number it reports is lower than beta.
int res = 0;
std::string json = this->getResponse(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getAPIConfig)" << std::endl << root << std::endl;
#endif
this->config.oauth_authorize_temp_token = root["config"]["oauth_authorize_temp_token"].asString();
this->config.oauth_get_temp_token = root["config"]["oauth_get_temp_token"].asString();
this->config.oauth_get_token = root["config"]["oauth_get_token"].asString();
this->config.get_user_games = root["config"]["get_user_games"].asString();
this->config.get_user_details = root["config"]["get_user_details"].asString();
this->config.get_installer_link = root["config"]["get_installer_link"].asString();
this->config.get_game_details = root["config"]["get_game_details"].asString();
this->config.get_extra_link = root["config"]["get_extra_link"].asString();
this->config.set_app_status = root["config"]["set_app_status"].asString();
res = 1;
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getAPIConfig)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
res = 0;
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
res = 0;
}
return res;
}
int API::login(const std::string& email, const std::string& password)
{
int res = 0;
std::string url;
std::string token, secret;
// Get temporary request token
url = oauth_sign_url2(this->config.oauth_get_temp_token.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY, CONSUMER_SECRET, NULL /* token */, NULL /* secret */);
std::string request_token_resp = this->getResponse(url);
char **rv = NULL;
int rc = oauth_split_url_parameters(request_token_resp.c_str(), &rv);
qsort(rv, rc, sizeof(char *), oauth_cmpstringp);
if (rc == 3 && !strncmp(rv[1], "oauth_token=", OAUTH_TOKEN_LENGTH) && !strncmp(rv[2], "oauth_token_secret=", OAUTH_SECRET_LENGTH)) {
token = rv[1]+OAUTH_TOKEN_LENGTH+1;
secret = rv[2]+OAUTH_SECRET_LENGTH+1;
rv = NULL;
}
else
{
return res;
}
// Authorize temporary token and get verifier
url = this->config.oauth_authorize_temp_token + "?username=" + oauth_url_escape(email.c_str()) + "&password=" + oauth_url_escape(password.c_str());
url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY, CONSUMER_SECRET, token.c_str(), secret.c_str());
std::string authorize_resp = this->getResponse(url);
std::string verifier;
rc = oauth_split_url_parameters(authorize_resp.c_str(), &rv);
qsort(rv, rc, sizeof(char *), oauth_cmpstringp);
if (rc == 2 && !strncmp(rv[1], "oauth_verifier=", OAUTH_VERIFIER_LENGTH)) {
verifier = rv[1]+OAUTH_VERIFIER_LENGTH+1;
rv = NULL;
}
else
{
return res;
}
// Get final token and secret
url = this->config.oauth_get_token + "?oauth_verifier=" + verifier;
url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY, CONSUMER_SECRET, token.c_str(), secret.c_str());
std::string token_resp = this->getResponse(url);
rc = oauth_split_url_parameters(token_resp.c_str(), &rv);
qsort(rv, rc, sizeof(char *), oauth_cmpstringp);
if (rc == 2 && !strncmp(rv[0], "oauth_token=", OAUTH_TOKEN_LENGTH) && !strncmp(rv[1], "oauth_token_secret=", OAUTH_SECRET_LENGTH)) {
this->config.oauth_token = rv[0]+OAUTH_TOKEN_LENGTH+1;
this->config.oauth_secret = rv[1]+OAUTH_SECRET_LENGTH+1;
free(rv);
res = 1;
}
return res;
}
int API::getUserDetails()
{
int res = 0;
std::string url;
url = this->config.get_user_details;
std::string json = this->getResponseOAuth(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getUserDetails)" << std::endl << root << std::endl;
#endif
this->user.id = std::stoull(root["user"]["id"].asString());
this->user.username = root["user"]["xywka"].asString();
this->user.email = root["user"]["email"].asString();
this->user.avatar_big = root["user"]["avatar"]["big"].asString();
this->user.avatar_small = root["user"]["avatar"]["small"].asString();
this->user.notifications_forum = root["user"]["notifications"]["forum"].isInt() ? root["user"]["notifications"]["forum"].asInt() : std::stoi(root["user"]["notifications"]["forum"].asString());
this->user.notifications_games = root["user"]["notifications"]["games"].isInt() ? root["user"]["notifications"]["games"].asInt() : std::stoi(root["user"]["notifications"]["games"].asString());
this->user.notifications_messages = root["user"]["notifications"]["messages"].isInt() ? root["user"]["notifications"]["messages"].asInt() : std::stoi(root["user"]["notifications"]["messages"].asString());
res = 1;
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getUserDetails)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
res = 0;
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
res = 0;
}
return res;
}
int API::getGames()
{
// Not implemented on the server side currently
//std::string json = this->getResponseOAuth(this->config.get_user_games);
return 0;
}
std::string API::getResponse(const std::string& url)
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getResponse)" << std::endl << "URL: " << url << std::endl;
#endif
std::ostringstream memory;
curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, writeMemoryCallback);
curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, &memory);
CURLcode result = curl_easy_perform(curlhandle);
std::string response = memory.str();
memory.str(std::string());
if (result == CURLE_HTTP_RETURNED_ERROR)
{
long int response_code = 0;
result = curl_easy_getinfo(curlhandle, CURLINFO_RESPONSE_CODE, &response_code);
if (result == CURLE_OK)
this->setError("HTTP ERROR: " + std::to_string(response_code));
else
this->setError("HTTP ERROR: failed to get error code: " + static_cast<std::string>(curl_easy_strerror(result)));
}
return response;
}
std::string API::getResponseOAuth(const std::string& url)
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getResponseOAuth)" << std::endl << "URL: " << url << std::endl;
#endif
std::string url_oauth = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY, CONSUMER_SECRET, this->config.oauth_token.c_str(), this->config.oauth_secret.c_str());
std::string response = this->getResponse(url_oauth);
return response;
}
gameDetails API::getGameDetails(const std::string& game_name, const unsigned int& type, const unsigned int& lang)
{
std::string url;
gameDetails game;
url = this->config.get_game_details + game_name + "/" + "installer_win_en"; // can't get game details without file id, any file id seems to return all details which is good for us
std::string json = this->getResponseOAuth(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getGameDetails)" << std::endl << root << std::endl;
#endif
game.gamename = game_name;
game.title = root["game"]["title"].asString();
game.icon = root["game"]["icon"].asString();
// Installer details
std::vector<Json::Value> installers;
if (type & INSTALLER_WINDOWS)
{
if (lang & LANGUAGE_EN)
installers.push_back(root["game"]["installer_win_en"]);
if (lang & LANGUAGE_DE)
installers.push_back(root["game"]["installer_win_de"]);
if (lang & LANGUAGE_FR)
installers.push_back(root["game"]["installer_win_fr"]);
if (lang & LANGUAGE_PL)
installers.push_back(root["game"]["installer_win_pl"]);
if (lang & LANGUAGE_RU)
installers.push_back(root["game"]["installer_win_ru"]);
}
if (type & INSTALLER_MAC)
{
if (lang & LANGUAGE_EN)
installers.push_back(root["game"]["installer_mac_en"]);
if (lang & LANGUAGE_DE)
installers.push_back(root["game"]["installer_mac_de"]);
if (lang & LANGUAGE_FR)
installers.push_back(root["game"]["installer_mac_fr"]);
if (lang & LANGUAGE_PL)
installers.push_back(root["game"]["installer_mac_pl"]);
if (lang & LANGUAGE_RU)
installers.push_back(root["game"]["installer_mac_ru"]);
}
for ( unsigned int i = 0; i < installers.size(); ++i )
{
for ( unsigned int index = 0; index < installers[i].size(); ++index )
{
Json::Value installer = installers[i][index];
game.installers.push_back(
new gameFile( installer["#updated"].isBool() ? installer["id"].asBool() : false,
installer["id"].isInt() ? std::to_string(installer["id"].asInt()) : installer["id"].asString(),
std::string(), // empty string because installer doesn't have "name"
installer["link"].asString(),
installer["size"].asString()
)
);
}
}
// Extra details
const Json::Value extras = root["game"]["extras"];
for ( unsigned int index = 0; index < extras.size(); ++index )
{
Json::Value extra = extras[index];
game.extras.push_back(
new gameFile( false, /* extras don't have "updated" flag */
extra["id"].isInt() ? std::to_string(extra["id"].asInt()) : extra["id"].asString(),
extra["name"].asString(),
extra["link"].asString(),
extra["size_mb"].asString()
)
);
}
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getGameDetails)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
}
return game;
}
std::string API::getInstallerLink(const std::string& game_name, const std::string& id)
{
std::string url, link;
std::stringstream ss;
ss << this->config.get_installer_link << game_name << "/" << id << "/";
url = ss.str();
std::string json = this->getResponseOAuth(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
/*
std::string result = root["result"].asString();
int timestamp = root["timestamp"].asInt();
int available = root["file"]["available"].asInt();
std::string link = root["file"]["link"].asString();
std::string message = root["file"]["message"].asString();
*/
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getInstallerLink)" << std::endl << root << std::endl;
#endif
int available = root["file"]["available"].isInt() ? root["file"]["available"].asInt() : std::stoi(root["file"]["available"].asString());
if (available)
link = root["file"]["link"].asString();
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getInstallerLink)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
}
return link;
}
std::string API::getExtraLink(const std::string& game_name, const std::string& id)
{
std::string url, link;
std::stringstream ss;
ss << this->config.get_extra_link << game_name << "/" << id << "/";
url = ss.str();
std::string json = this->getResponseOAuth(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
/*
std::string result = root["result"].asString();
int timestamp = root["timestamp"].asInt();
int available = root["file"]["available"].asInt();
std::string link = root["file"]["link"].asString();
std::string type = root["file"]["type"].asString();
std::string name = root["file"]["name"].asString();
std::string message = root["file"]["message"].asString();
*/
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getExtraLink)" << std::endl << root << std::endl;
#endif
int available = root["file"]["available"].isInt() ? root["file"]["available"].asInt() : std::stoi(root["file"]["available"].asString());
if (available)
link = root["file"]["link"].asString();
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getExtraLink)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
}
return link;
}
std::string API::getXML(const std::string& game_name, const std::string& id)
{
std::string url, XML;
std::stringstream ss;
ss << this->config.get_installer_link << game_name << "/" << id << "/crc/";
url = ss.str();
std::string json = this->getResponseOAuth(url);
if (!json.empty())
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, root))
{
/*
std::string result = root["result"].asString();
int timestamp = root["timestamp"].asInt();
int available = root["file"]["available"].asInt();
std::string link = root["file"]["link"].asString();
std::string message = root["file"]["message"].asString();
*/
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getXML)" << std::endl << root << std::endl;
#endif
int available = root["file"]["available"].isInt() ? root["file"]["available"].asInt() : std::stoi(root["file"]["available"].asString());
if (available)
{
url = root["file"]["link"].asString();
XML = this->getResponse(url);
}
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (API::getXML)" << std::endl << json << std::endl;
#endif
this->setError(jsonparser->getFormatedErrorMessages());
}
delete jsonparser;
}
else
{
this->setError("Found nothing in " + url);
}
return XML;
}
void API::clearError()
{
this->error = false;
this->error_message = "";
}
void API::setError(const std::string& err)
{
this->error = true;
if (this->error_message.empty())
this->error_message = err;
else
this->error_message += "\n" + err;
}
API::~API()
{
curl_easy_cleanup(curlhandle);
}

1027
src/downloader.cpp Normal file

File diff suppressed because it is too large Load Diff

85
src/progressbar.cpp Normal file
View File

@ -0,0 +1,85 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "progressbar.h"
#include <cmath>
ProgressBar::ProgressBar(bool bUnicode, bool bColor)
:
// Based on block characters.
// See https://en.wikipedia.org/wiki/List_of_Unicode_characters#Block_elements
// u8"\u2591" - you can try using this ("light shade") instead of space, but it looks worse,
// since partial bar has no shade behind it.
m_bar_chars
{
" ", // 0/8
u8"\u258F", // 1/8
u8"\u258E", // 2/8
u8"\u258D", // 3/8
u8"\u258C", // 4/8
u8"\u258B", // 5/8
u8"\u258A", // 6/8
u8"\u2589", // 7/8
u8"\u2588" /* 8/8 */
},
m_left_border(u8"\u2595"), // right 1/8th
m_right_border(u8"\u258F"), // left 1/8th
m_simple_left_border("["),
m_simple_right_border("]"),
m_simple_empty_fill(" "),
m_simple_bar_char("="),
// using vt100 escape sequences for colors... See http://ascii-table.com/ansi-escape-sequences.php
m_bar_color("\033[1;34m"),
m_border_color("\033[1;37m"),
COLOR_RESET("\033[0m"),
m_use_unicode(bUnicode),
m_use_color(bColor)
{ }
ProgressBar::~ProgressBar()
{
//dtor
}
void ProgressBar::draw(unsigned int length, double fraction)
{
// validation
if (!std::isnormal(fraction) || (fraction < 0.0)) fraction = 0.0;
else if (fraction > 1.0) fraction = 1.0;
double bar_part = fraction * length;
double whole_bar_chars = std::floor(bar_part);
unsigned int whole_bar_chars_i = (unsigned int) whole_bar_chars;
// The bar uses symbols graded with 1/8
unsigned int partial_bar_char_index = (unsigned int) std::floor((bar_part - whole_bar_chars) * 8.0);
// left border
if (m_use_color) std::cout << m_border_color;
std::cout << (m_use_unicode ? m_left_border : m_simple_left_border);
// whole completed bars
if (m_use_color) std::cout << m_bar_color;
unsigned int i = 0;
for (; i < whole_bar_chars_i; i++)
{
std::cout << (m_use_unicode ? m_bar_chars[8] : m_simple_bar_char);
}
// partial completed bar
if (i < length) std::cout << (m_use_unicode ? m_bar_chars[partial_bar_char_index] : m_simple_empty_fill);
// whole unfinished bars
if (m_use_color) std::cout << COLOR_RESET;
for (i = whole_bar_chars_i + 1; i < length; i++)
{ // first entry in m_bar_chars is assumed to be the empty bar
std::cout << (m_use_unicode ? m_bar_chars[0] : m_simple_empty_fill);
}
// right border
if (m_use_color) std::cout << m_border_color;
std::cout << (m_use_unicode ? m_right_border : m_simple_right_border);
if (m_use_color) std::cout << COLOR_RESET;
}

180
src/util.cpp Normal file
View File

@ -0,0 +1,180 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "util.h"
#include <boost/filesystem.hpp>
#include <tinyxml.h>
/*
Create filepath from specified directory and path
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 filepath;
if (gamename.empty())
{
if (path.at(0)=='/')
{
std::string tmp_path = path.substr(1,path.length());
filepath = directory + tmp_path;
}
else
{
filepath = directory + path;
}
}
else
{
std::string extras = "";
if (path.find("extras")!=std::string::npos)
{
extras = "/extras";
}
std::string filename = path.substr(path.find_last_of("/")+1, path.length());
filepath = directory + gamename + extras + "/" + filename;
}
return filepath;
}
std::string Util::getFileHash(const std::string& filename, unsigned hash_id)
{
unsigned char digest[rhash_get_digest_size(hash_id)];
char result[rhash_get_hash_length(hash_id)];
rhash_library_init();
int i = rhash_file(hash_id, filename.c_str(), digest);
if (i < 0)
std::cout << "LibRHash error: " << strerror(errno) << std::endl;
else
rhash_print_bytes(result, digest, rhash_get_digest_size(hash_id), RHPR_HEX);
return result;
}
std::string Util::getChunkHash(unsigned char *chunk, size_t chunk_size, unsigned hash_id)
{
unsigned char digest[rhash_get_digest_size(hash_id)];
char result[rhash_get_hash_length(hash_id)];
rhash_library_init();
int i = rhash_msg(hash_id, chunk, chunk_size, digest);
if (i < 0)
std::cout << "LibRHash error: " << strerror(errno) << std::endl;
else
rhash_print_bytes(result, digest, rhash_get_digest_size(hash_id), RHPR_HEX);
return result;
}
// Create GOG XML
int Util::createXML(std::string filepath, size_t chunk_size, std::string xml_dir)
{
int res = 0;
FILE *infile;
FILE *xmlfile;
size_t filesize, size;
int chunks, i;
std::string home = (std::string)getenv("HOME");
if (xml_dir.empty())
xml_dir = home + "/.gogdownloader/xml";
// Make sure directory exists
boost::filesystem::path path = xml_dir;
if (!boost::filesystem::exists(path)) {
if (!boost::filesystem::create_directories(path)) {
std::cout << "Failed to create directory: " << path << std::endl;
return res;
}
}
if ((infile=fopen(filepath.c_str(), "r"))!=NULL) {
//File exists
fseek(infile, 0, SEEK_END);
filesize = ftell(infile);
rewind(infile);
} else {
std::cout << filepath << " doesn't exist" << std::endl;
return res;
}
// Get filename
boost::filesystem::path pathname = filepath;
std::string filename = pathname.filename().string();
std::string filenameXML = xml_dir + "/" + filename + ".xml";
std::cout << filename << std::endl;
//Determine number of chunks
int remaining = filesize % chunk_size;
chunks = (remaining == 0) ? filesize/chunk_size : (filesize/chunk_size)+1;
std::cout << "Filesize: " << filesize << " bytes" << std::endl
<< "Chunks: " << chunks << std::endl
<< "Chunk size: " << (chunk_size >> 20) << " MB" << std::endl
<< "MD5: " << std::flush;
std::string file_md5 = Util::getFileHash(filepath.c_str(), RHASH_MD5);
std::cout << file_md5 << std::endl;
TiXmlDocument xml;
TiXmlElement *fileElem = new TiXmlElement("file");
fileElem->SetAttribute("name", filename);
fileElem->SetAttribute("md5", file_md5);
fileElem->SetAttribute("chunks", chunks);
fileElem->SetAttribute("total_size", filesize);
std::cout << "Getting MD5 for chunks" << std::endl;
for (i = 0; i < chunks; i++) {
size_t range_begin = i*chunk_size;
fseek(infile, range_begin, SEEK_SET);
if ((i == chunks-1) && (remaining != 0))
chunk_size = remaining;
size_t range_end = range_begin + chunk_size - 1;
unsigned char *chunk = (unsigned char *) malloc(chunk_size * sizeof(unsigned char *));
if (chunk == NULL)
{
std::cout << "Memory error" << std::endl;
return res;
}
size = fread(chunk, 1, chunk_size, infile);
if (size != chunk_size)
{
std::cout << "Read error" << std::endl;
free(chunk);
return res;
}
std::string hash = Util::getChunkHash(chunk, chunk_size, RHASH_MD5);
free(chunk);
TiXmlElement *chunkElem = new TiXmlElement("chunk");
chunkElem->SetAttribute("id", i);
chunkElem->SetAttribute("from", range_begin);
chunkElem->SetAttribute("to", range_end);
chunkElem->SetAttribute("method", "md5");
TiXmlText *text = new TiXmlText(hash);
chunkElem->LinkEndChild(text);
fileElem->LinkEndChild(chunkElem);
std::cout << "Chunks hashed " << (i+1) << " / " << chunks << "\r" << std::flush;
}
fclose(infile);
xml.LinkEndChild(fileElem);
std::cout << std::endl << "Writing XML: " << filenameXML << std::endl;
if ((xmlfile=fopen(filenameXML.c_str(), "w"))!=NULL) {
xml.Print(xmlfile);
fclose(xmlfile);
res = 1;
} else {
std::cout << "Can't create " << filenameXML << std::endl;
return res;
}
return res;
}