mirror of
https://github.com/Sude-/lgogdownloader.git
synced 2024-11-20 03:39:17 +01:00
Initial commit
This commit is contained in:
commit
2d045c4052
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.layout
|
||||
*~
|
||||
*.[oa]
|
||||
bin/*
|
||||
obj/*
|
14
COPYING
Normal file
14
COPYING
Normal 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
124
Makefile
Normal 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
112
include/api.h
Normal 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
48
include/config.h
Normal 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
85
include/downloader.h
Normal 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
35
include/progressbar.h
Normal 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
26
include/util.h
Normal 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
67
lgogdownloader.cbp
Normal 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
190
main.cpp
Normal 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
528
src/api.cpp
Normal 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
1027
src/downloader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
85
src/progressbar.cpp
Normal file
85
src/progressbar.cpp
Normal 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
180
src/util.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user